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

Posted

技术标签:

【中文标题】一旦客户端断开连接,服务器应用程序就会崩溃【英文标题】:Server application crashing as soon as a client disconnects 【发布时间】:2015-09-21 18:57:35 【问题描述】:

所以我只是使用 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);

客户端的线程然后检查客户端是否发送了某些内容,然后将其发送到服务器。它还检查客户端是否仍然连接。 无效客户端::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;

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

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

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

【问题讨论】:

&amp;Client() 评估为指向右值的指针,只有在使用不符合标准的编译器时才有可能。不要那样做。 所以我应该改用法线向量? All debugging shows me is this as my error 真的是这样吗?你检查调用堆栈了吗?您是否尝试使用try/catch 封装一些代码?您是否尝试评论 server-&gt;removeClient(this); 以查看它是否停止崩溃? 为什么会有一个指针向量? Client 对象应该在什么时候被销毁?不清楚你为什么要做你正在做的事情,这让你很难告诉你做这件事的正确方法。 我假设 Client 对象在方法“server->removeClient(this);”后立即被销毁被调用并且客户端对象被从向量中移除。 (因为我不会将客户保存在其他任何地方) 【参考方案1】:

错误出现在这段代码中:

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));

【讨论】:

std::unique_ptr 需要 C++11。对于 C++11 之前的编译器,您必须直接使用 newstd::vector&lt;Client*&gt; clients; while (true) clients.push_back(new Client(accept(serverSocket, NULL, NULL), this)); ,并确保在将 Clientvector 中删除时手动 delete 谢谢,我现在改为使用智能指针。但是,当客户端断开连接时,它仍然会崩溃。 @Styxs 你是加入还是分离tickThread?如果没有完整的代码示例,很难看出问题出在哪里,但std::thread 必须要么加入主线程,要么分离,否则当线程被销毁时,您可能会看到崩溃。请参阅文档here。 @Open 我多次尝试加入或分离线程,但是一旦调用客户端对象的析构函数,服务器就会崩溃。 @Styxs 就像我说的那样,如果没有一个完整的最小示例来重现问题,就很难确定哪里可能存在问题。 Visual Studio 有一些很棒的调试工具,你试过用吗?如果您遇到崩溃并且能够在调试模式下重现它,那么调用堆栈很有可能会直接将您指向有问题的代码行。【参考方案2】:

在这段代码中:

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

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

【讨论】:

我认为许多编译器都会允许这样做。它在编译时是完全有效的代码。在运行时运行它只是不安全的代码。编译器不假定指向临时指针的用途(在获取指针时,临时指针仍然有效),并且它不知道 vector 将持有指针比临时生命周期更长的时间。 我正在使用 Visual Studio 2013 及其编译器。 @RemyLebeau,获取临时地址是一个错误。它是 MSVC 扩展,我觉得可恶。例如,这是 g++ 所做的: main.cpp:7:5: error: cannot take the address of an rvalue of type 'int' &(int(10)); ^ ~~~~~~~ 1 个错误产生。

以上是关于一旦客户端断开连接,服务器应用程序就会崩溃的主要内容,如果未能解决你的问题,请参考以下文章

客户端断开连接时套接字服务器崩溃

Java CORBA 客户端立即断开连接

Java CORBA 客户端在事件触发时断开连接

如果已断开连接的客户端已重新连接,Python3就会知道

socket.io 由于数据大小而断开连接

Java:使用 AWT 按钮断开与聊天服务器的连接