指向被覆盖的值的指针访问异常

问题描述

所以我的类的对象指针有一些问题,并且不断被函数覆盖。为了帮助您理解,这是一个旨在帮助人们轻松地从客户端与服务器建立 winsock 套接字连接的库的项目。

Main.cpp:

(includes aleready included)

int main() {

Client client;

//AUTH SERVER
client.WSA_init(2,2);
SOCKET server_auth = client.init("127.0.0.1",3211);

std::string auth = client.ClientRecieve(server_auth);
std::string hash = client.Auth(auth);

client.End(server_auth);

client.WSA_init(2,2);
SOCKET server_main = client.init("127.0.0.1",3212);

client.ClientSend(server_main,(char*)hash.c_str());

ClientSocketParameters* t_params = new ClientSocketParameters; //Basically a parameter object pointer
t_params->Recv_BufferAccesibilityMode = THREAD_CONSOLE_PRINT_INFO; //These are defined aleready as macros in the win32s.h file
t_params->Recv_Raw = false;
t_params->Recv_Server_Type = SERVER_TYPE_JAVA;
t_params->Send_Mode = THREAD_KEYBOARD_SEND;
t_params->Send_Raw = false;
t_params->Send_Server_Type = SERVER_TYPE_JAVA;

client.setThreadParameters(t_params); //Sets the params

DWORD tid;

ThreadHandler::Socket_CallBack* t_recv = new ThreadHandler::Socket_CallBack;
t_recv->client = &client;
t_recv->socket = server_main;
ThreadHandler::Socket_CallBack* t_send = new ThreadHandler::Socket_CallBack;
t_send->client = &client;
t_send->socket = server_main;

HANDLE h1 = CreateThread(nullptr,NULL,ThreadHandler::ThreadClientRecieve,&t_recv,&tid); //Launches functions that are starting recv and sending with infinity loop (cant put here a client method so needed to do this (btw. these are also in the win32s files))
HANDLE h2 = CreateThread(nullptr,ThreadHandler::ThreadClientSend,&t_send,&tid);

WaitForSingleObject(h1,INFINITE);
WaitForSingleObject(h2,INFINITE);

client.End(server_main);

return 0;
}

win32_s.h 文件

(includes aleready included)

class ClientSocketParameters { //Parameter class
public:
//Recieving
DWORD Recv_BufferAccesibilityMode;
bool Recv_Raw;
DWORD Recv_Server_Type;
std::string* Recv_StoreAddress;
char* Recv_StoreAdress_Raw;
//Sending
DWORD Send_Mode;
bool Send_Raw;
DWORD Send_Server_Type;
std::string* Send_StoredDataAddress;
};

class Client {
private:

ClientSocketParameters* thread_settings = new ClientSocketParameters; //This is the pointer that is getting overwritten. It is basically a pointer to the parameters that you set with a method in your main

SOCKET init(std::string ip,int port); //Most of these functions are not necessary for problem cause
WSAData WSA_init(int version_makeword_1,int version_makeword_2);
std::string Auth(std::string key);
int PackageManager(); //Todo
int ClientSend(SOCKET dst,char buffer[]);
std::string ClientRecieve(SOCKET src);
std::string BufferToString(char buffer[]);
std::string BufferToJavaString(char buffer[]);
std::string JavaBufferToString(char buffer[]);
int End(SOCKET con);

Some macros

void setThreadParameters(ClientSocketParameters* params);

DWORD WINAPI Thread_ClientRecieve(LPVOID socket_src);
DWORD WINAPI Thread_ClientSend(LPVOID dst);
};

namespace ThreadHandler { //These lauches the thread functions with calling them (check .cpp of this file)
DWORD WINAPI ThreadClientRecieve(LPVOID param);
DWORD WINAPI ThreadClientSend(LPVOID param);

struct Socket_CallBack {
    SOCKET socket;
    Client* client;
};

}

win32_s.cpp 文件

#include "win32_s.h"
(I'm not going to show the not necessary functions that work)

void Client::setThreadParameters(ClientSocketParameters* params) {
std::cout << params << std::endl;
thread_settings = params;
std::cout << thread_settings << std::endl; //This address is correct to this point,The Thread_ClientRecieve and the thread send function overwrites the address to 0x11C when checking the variables of the pointer
}

DWORD WINAPI Client::Thread_ClientRecieve(LPVOID lpParam) { //Todo: Packet management,Exceptions

const DWORD MAX_BYTES = 1024;
char buffer[MAX_BYTES];
size_t recvbytes;
SOCKET server = *(SOCKET*)lpParam;

while (true) {

    //std::cout << "here #packet recieve" << std::endl;

    ZeroMemory(buffer,MAX_BYTES);
    
    recvbytes = recv(server,buffer,MAX_BYTES,0);

    if (recvbytes <= 0) {
        std::cout << "Recieving info from server Failed. Cancellig thread..." << std::endl;
        break;
    }

    std::cout << thread_settings->Recv_BufferAccesibilityMode << std::endl;
    std::cout << thread_settings->Recv_Raw << std::endl;
    std::cout << thread_settings->Recv_Server_Type << std::endl;

    if (thread_settings->Recv_BufferAccesibilityMode == THREAD_CONSOLE_PRINT_INFO) {

        std::string console_text;

        if (thread_settings->Recv_Raw) {
            std::cout << buffer << std::endl;
        }
        if (thread_settings->Recv_Server_Type == SERVER_TYPE_JAVA) {
            console_text = JavaBufferToString(buffer);
            std::cout << console_text << std::endl;
        }
        if (thread_settings->Recv_Server_Type == SERVER_TYPE_WINSOCK) {
            console_text = BufferToString(buffer);
            std::cout << console_text << std::endl;
        }
    }

    if (thread_settings->Recv_BufferAccesibilityMode == THREAD_STORE_INFO) {

        std::string buffer_s;

        if (thread_settings->Recv_Raw) {
            thread_settings->Recv_StoreAdress_Raw = buffer;
        }
        if (thread_settings->Recv_Server_Type == SERVER_TYPE_JAVA) {
            buffer_s = JavaBufferToString(buffer);
            *thread_settings->Recv_StoreAddress = buffer_s;
        }
        if (thread_settings->Recv_Server_Type == SERVER_TYPE_WINSOCK) {
            buffer_s = BufferToString(buffer);
            *thread_settings->Recv_StoreAddress = buffer_s;
        }
    }

}

return 0;

}

DWORD WINAPI Client::Thread_ClientSend(LPVOID lpParam) {

const DWORD MAX_BYTES = 1024;
char buffer[1024];
SOCKET server = *(SOCKET*)lpParam;

while (true) {

    //std::cout << "here #send packet" << std::endl;

    if (thread_settings->Send_Mode == THREAD_KEYBOARD_SEND) {
        std::cin.getline(buffer,1024);
        std::string data;

        if (thread_settings->Send_Raw) {
            if (send(server,0) == SOCKET_ERROR) {
                std::cout << "Failed while sending to the server. Closing thread...." << std::endl;
                return -1;
            }
        }

        if (thread_settings->Send_Server_Type == SERVER_TYPE_JAVA) {
            data = BufferToJavaString(buffer);
            if (send(server,data.c_str(),data.size(),0) == SOCKET_ERROR) {
                std::cout << "Failed while sending to the server. Closing thread...." << std::endl;
                return -1;
            }
        }

        if (thread_settings->Send_Server_Type == SERVER_TYPE_WINSOCK) {
            data = BufferToString(buffer);
            if (send(server,0) == SOCKET_ERROR) {
                std::cout << "Failed while sending to the server. Closing thread...." << std::endl;
                return -1;
            }
        }

    }

    if (thread_settings->Send_Mode == THREAD_VARIABLE_SEND) {

        if (thread_settings->Send_StoredDataAddress == NULL) {
            std::cout << "Please enter a address to use the thread variable send function. Cancelling thread..." << std::endl;
            return -1;
        }

        //memset(thread_settings.Send_StoredDataAddress,0x90,sizeof(thread_settings.Send_StoredDataAddress));
        std::string StoredData = *thread_settings->Send_StoredDataAddress;

        if (thread_settings->Send_Server_Type == SERVER_TYPE_JAVA) {
            StoredData.append("\n");

            if (send(server,StoredData.c_str(),StoredData.size(),0) == SOCKET_ERROR) {
                std::cout << "Failed while sending to the server. Closing thread...." << std::endl;
                return -1;
            }
        }
        else {
            if (send(server,0) == SOCKET_ERROR) {
                std::cout << "Failed while sending to the server. Closing thread...." << std::endl;
                return -1;
            }
        }
    }
}

}

DWORD WINAPI ThreadHandler::ThreadClientRecieve(LPVOID param) {

Socket_CallBack* s_callback = new Socket_CallBack;
s_callback = (Socket_CallBack*)param;

SOCKET socket = s_callback->socket;

Client* callback_ptr = new Client;
callback_ptr = s_callback->client;

//delete s_callback;
//delete &s_callback->client;

std::cout << "ClientRecvSocketAddr: " << socket << std::endl;
std::cout << "ClientCallBackPtr: " << callback_ptr << std::endl;

return callback_ptr->Thread_ClientRecieve((LPVOID)socket);

}

DWORD WINAPI ThreadHandler::ThreadClientSend(LPVOID param) {

Socket_CallBack* s_callback = new Socket_CallBack;
s_callback = (Socket_CallBack*)param;

SOCKET socket = s_callback->socket;

Client* callback_ptr = new Client;
callback_ptr = s_callback->client;

//delete s_callback;
//delete &s_callback->client;

std::cout << "ClientSendSocketAddr: " << socket << std::endl;
std::cout << "ClientCallBackPtr: " << callback_ptr << std::endl;

return callback_ptr->Thread_ClientSend((LPVOID)socket);

}

这段代码还没有完全完成,所以像检查和更小的细节这样的事情还没有实现。 感谢您提供任何帮助并利用您的时间。

解决方法

我认为问题出在这里

SOCKET server_main = client.init("127.0.0.1",3212);
...
HANDLE h1 = CreateThread(nullptr,NULL,ThreadHandler::ThreadClientRecieve,&server_main,&tid);
HANDLE h2 = CreateThread(nullptr,ThreadHandler::ThreadClientSend,&tid);

您创建了两个线程,将指向 SOCKET 的指针作为参数传递。

但是在线程代码中,您将该参数转换为 Client 指针。

DWORD WINAPI ThreadHandler::ThreadClientSend(LPVOID param) {
    Client* ptr = (Client*)param;
    return ptr->Thread_ClientSend(param);
}

毫不奇怪,将真正是 SOCKET* 的内容转换为 Client* 是行不通的。

我猜代码应该是这样的

HANDLE h1 = CreateThread(nullptr,&client,&tid);

编辑所以仍然存在各种问题

这段代码没有错,只是浪费(和混乱)

DWORD WINAPI ThreadHandler::ThreadClientRecieve(LPVOID param) {

    Socket_CallBack* s_callback = new Socket_CallBack;
    s_callback = (Socket_CallBack*)param;

    SOCKET socket = s_callback->socket;

    Client* callback_ptr = new Client;
    callback_ptr = s_callback->client;

这里绝对不需要分配更多内存,Socket_CallBackClient 对象已经分配好了。此代码将工作

DWORD WINAPI ThreadHandler::ThreadClientRecieve(LPVOID param) {

    Socket_CallBack* s_callback =  (Socket_CallBack*)param;
    SOCKET socket = s_callback->socket;
    Client* callback_ptr = s_callback->client;

现在这是真正的错误

return callback_ptr->Thread_ClientRecieve((LPVOID)socket);

SOCKET 正在传递给 Thread_ClientRecieve。但是当我们看到 Thread_ClientRecieve

DWORD WINAPI Client::Thread_ClientRecieve(LPVOID lpParam) {
    ...
    SOCKET server = *(SOCKET*)lpParam;

参数被转换为 SOCKET*。当您将一种类型转换为其他类型时,您必须转换回相同的类型。您不能将 SOCKET 转换回 SOCKET*

有两种方法可以解决这个问题,一种是最少的,一种是正确的方法(尽可能避免强制转换和指针)。

最简单的方法是传递socket的地址,像这样

return callback_ptr->Thread_ClientRecieve((LPVOID)&socket);

现在将 SOCKET* 传递给 Client::Thread_ClientRecieve,因此您可以安全地将参数转换回 SOCKET*

但在这里我们可以简单地删除所有指针并通过将 Client::Thread_ClientRecieve 的参数更改为 SOCKET,不进行强制转换,不进行指针转换。

DWORD WINAPI Client::Thread_ClientRecieve(SOCKET server) {
    ...

当你调用它时只传递套接字,不需要转换

return callback_ptr->Thread_ClientRecieve(socket);

发送代码看起来有类似的错误。