一旦客户端断开连接,服务器应用程序就会崩溃

Server application crashing as soon as a client disconnects

所以我刚刚使用 winsock2 和 TCP 编写了一个简单的多线程客户端服务器应用程序。

以下是其工作原理的简要总结:

服务器主线程处于无限循环中接受客户端,然后将它们添加到服务器向量中,该向量包含每个连接的客户端,如下所示: (只为我的问题添加重要内容)

std::vector <Client*> clients;
while (true){
    clients.push_back(&Client(accept(serverSocket, NULL, NULL), this));
}

当一个新的客户端连接到服务器时,我们基本上会创建一个新的客户端对象,并将新客户端的套接字和服务器本身作为参数。

当时我的想法是给每个客户端一个线程,这样每个客户端都可以同时发送数据。

std::thread tickThread;

Client::Client(SOCKET socket,Server* server) :
isConnected(true),
socket(socket),
server(server)
{
    tickThread = std::thread(&Client::tick,this);
}

客户端的线程然后检查客户端是否发送了一些东西,然后将它发送到服务器。它还检查客户端是否仍然连接。 void Client::tick(){

while (isConnected){
    errorHandler = recv(socket, receivedData, 255, 0);
    if (errorHandler == SOCKET_ERROR){
        disconnect();
    }
    else {
        //send received data to server
    }
}

如果客户端断开连接,它会告诉服务器从已连接的客户端矢量中删除客户端,然后将 "isConnected" 布尔值设置为 false,以便线程可以退出其功能。

void Client::disconnect(){
    isConnected = false;
    server->removeClient(this);
}

这是它应该如何工作的,但是一旦客户端再次断开连接,服务器就会崩溃并出现错误:

R6010 - 已调用 abort()

所有调试都显示这是我的错误:

switch (_CrtDbgReportW(_CRT_ERROR, NULL, 0, NULL, L"%s", error_text)){
          case 1: _CrtDbgBreak(); msgshown = 1; break;
          case 0: msgshown = 1; break;
}

所以是的,我真的不知道是什么导致了这次崩溃,但是我怀疑它可能与使用客户端函数的线程有关,该函数基本上被删除了,因为它正在从服务器。

如果事实证明这是问题所在,你们能给我一些想法,让我更好地实现每个客户端都有自己的线程吗?

编辑:更改了向量错误,但是一旦客户端断开连接,崩溃仍然会发生

在这段代码中:

clients.push_back(&Client(accept(serverSocket, NULL, NULL), this));

您正在将临时对象的地址推送到容器中。 push_back() 完成后,临时对象被销毁,因此该地址不再有效。我想知道,什么样的编译器允许你这样做。

错误在这段代码中:

while (true){
    clients.push_back(&Client(accept(serverSocket, NULL, NULL), this));
}

Client(accept(serverSocket, NULL, NULL), this) 是一个表达式,它生成一个临时 Client 对象,该对象在语句完成执行时被销毁。但是,您获取该临时对象的地址并将其添加到您的 vector.

如果要创建 Client 对象并存储指向它们的指针,则需要为它们分配内存。我建议使用 std::unique_ptr 来管理它们,以便您的 vector 声明对它们内存的所有权,并在它们从 vector 中删除或 vector 本身被破坏时自动释放它们.然后你的代码变成:

std::vector<std::unique_ptr<Client>> clients;
while (true){
    clients.push_back(std::make_unique<Client>(accept(serverSocket, NULL, NULL), this));
}