C++ Socket服务器处理一次连接后无提示关闭,求故障原因
Socket服务器单次通信后异常关闭问题分析与解决
我正在用C++开发一个Socket服务器,接受连接后创建线程向连接发送数据,但每次运行服务器时,向一个用户发送一次数据后就直接关闭,且无错误提示。相关代码如下:
主循环代码
while (TRUE){ listen_sock = listen(sock, SOMAXCONN); if (listen_sock == 0){ accept_sock = accept(sock, NULL, NULL); if(accept_sock == INVALID_SOCKET){ cout << "part 5\n"; printf("accepting connection failed with error: %d\n", WSAGetLastError()); } thread thrd(send_dat, accept_sock); thrd.detach(); } else if (listen_sock == SOCKET_ERROR){ cout << "part 4\n"; printf("\n\nlistening on socket failed with error: %d\n", WSAGetLastError()); } }
数据处理线程函数
void send_dat(SOCKET accept_sock){ /*variable declarations here*/ data = recv(accept_sock, recvbuf, recvbuflen, 0); if (data == SOCKET_ERROR){ cout << "part -1\n"; throw runtime_error("Failed to receive data"); WSAGetLastError(); } data_res = response(data); send_sock = send(accept_sock, data_res_len, data_res, 0); if (send_sock == SOCKET_ERROR){ cout << "part -2\n"; throw runtime_error("Failed to send data"); WSAGetLastError(); } }
问题原因分析
- listen函数滥用:listen仅需调用一次,用于将套接字切换为监听状态。你在循环中反复调用它,第一次成功后后续调用会返回
SOCKET_ERROR(错误码WSAEINVAL),导致逻辑异常;且accept应在listen成功后循环调用,而非每次循环先执行listen。 - 未捕获异常导致进程终止:分离的线程(
detach())中抛出未捕获的runtime_error会直接终止整个进程,这是服务器无提示关闭的核心原因。另外throw后的WSAGetLastError()永远不会执行,无法获取错误码。 - send参数顺序错误:send的正确参数是
(套接字, 数据缓冲区, 数据长度, 标志),你颠倒了缓冲区和长度的位置,必然导致send调用失败触发异常。 - 未处理客户端正常断开:recv返回0表示客户端主动断开,你的代码未处理该情况,会进入后续错误逻辑。
修复后的代码示例
主循环修正
// 仅调用一次listen,初始化监听状态 listen_sock = listen(sock, SOMAXCONN); if (listen_sock == SOCKET_ERROR){ cout << "part 4\n"; printf("\n\nlistening on socket failed with error: %d\n", WSAGetLastError()); return; // 监听失败直接退出 } // 循环接受客户端连接 while (TRUE){ accept_sock = accept(sock, NULL, NULL); if(accept_sock == INVALID_SOCKET){ cout << "part 5\n"; int err = WSAGetLastError(); printf("accepting connection failed with error: %d\n", err); continue; // 接受失败,继续等待下一个连接 } thread thrd(send_dat, accept_sock); thrd.detach(); }
数据处理线程修正
void send_dat(SOCKET accept_sock){ try { int data_len = recv(accept_sock, recvbuf, recvbuflen, 0); if (data_len == SOCKET_ERROR){ cout << "part -1\n"; int err = WSAGetLastError(); printf("recv failed with error: %d\n", err); closesocket(accept_sock); return; } else if (data_len == 0){ cout << "Client disconnected normally\n"; closesocket(accept_sock); return; } // 假设response函数接收数据长度和缓冲区,返回处理后的数据及长度 // 请根据实际实现调整response的调用方式 char* data_res = nullptr; int data_res_len = 0; tie(data_res, data_res_len) = response(data_len, recvbuf); int send_result = send(accept_sock, data_res, data_res_len, 0); if (send_result == SOCKET_ERROR){ cout << "part -2\n"; int err = WSAGetLastError(); printf("send failed with error: %d\n", err); closesocket(accept_sock); // 释放data_res内存(如果response分配了堆内存) delete[] data_res; return; } // 释放资源 delete[] data_res; closesocket(accept_sock); } catch (const exception& e){ cout << "Thread exception: " << e.what() << "\n"; closesocket(accept_sock); } }
额外注意事项
- 所有套接字使用完毕后必须调用
closesocket关闭,避免资源泄漏。 - 分离线程必须捕获所有异常,禁止未捕获异常导致进程崩溃。
- 确认
response函数的内存管理逻辑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Shoto Todoroki
相关产品推荐
相关产品推荐

