Windows下C++ TCP Socket无法响应外部连接,Linux运行正常求助
从你的描述和代码来看,UDP功能正常但TCP在Windows下无响应,结合Wireshark抓包结果(三次握手完成、请求已发送但无返回),我整理了几个关键问题点,你可以逐一排查:
1. 非阻塞模式的跨平台不一致设置
看你的Socket初始化代码,Windows下TCP套接字的非阻塞模式被注释掉了,但Linux下是正常启用的:
#ifdef _WIN32 unsigned long nonblocking = 1; // if(ioctlsocket(mTcpSocket,FIONBIO, &nonblocking)) // std::cout << "Couldn't set TCP-Socket to nonblocking mode! (" << std::strerror(errno) << ")" << endl; ... #else int tcpFlags = fcntl(mTcpSocket, F_GETFL, 0); if(fcntl(mTcpSocket, F_SETFL, tcpFlags | O_NONBLOCK)) ... #endif
这会导致Windows下TCP是阻塞模式,但你的tcpListen循环逻辑是按照非阻塞设计的(accept失败直接continue)。如果是阻塞模式,accept本身没问题,但后续的recv会一直阻塞等待数据——如果客户端发送的是空请求(0字节),recv会卡在那里,根本进不到send逻辑里。
建议:要么取消Windows下TCP非阻塞设置的注释,要么调整循环逻辑适配阻塞模式。
2. accept的socklen_t参数未重置
你在循环外初始化了size = sizeof(sockaddr),但accept的第三个参数是输入输出参数,调用后size会被修改为实际的地址长度。在Windows下,如果后续循环复用这个被修改的size,很可能导致accept调用失败(错误码WSAEINVAL),但你的错误处理里用了strerror(errno)——Windows的Socket错误不会设置errno,只会通过WSAGetLastError()返回,所以你根本看不到这个错误。
修复方法:把size的初始化移到while循环内部,每次调用accept前重置:
while(mTcpServerRunning) { socklen_t size = sizeof(sockaddr); // 移到循环内,每次重置 int cSock = accept(mTcpSocket, (sockaddr*) &remoteHost, &size); // ... 后续逻辑 }
3. Windows下错误处理的兼容性问题
你在Windows下同时混用了strerror(errno)和WSAGetLastError(),但Windows的Socket错误不会更新errno,strerror(errno)会显示完全无关的错误信息,导致你无法定位真正的问题。
建议:在Windows平台下,统一用WSAGetLastError()获取错误码,比如可以替换错误打印逻辑:
#ifdef WIN32 cerr << "Couldn't bind port. WSA Error: " << WSAGetLastError() << endl; #else cerr << "Couldn't bind port. (" << std::strerror(errno) << " " << errno << ")" << endl; #endif
4. 客户端套接字未关闭导致资源泄漏
处理完客户端请求后,你没有调用closesocket(cSock)(Windows下用这个,Linux用close()),会导致套接字资源泄漏。Windows下每个进程的套接字数量有限,积累到一定程度就无法处理新连接了。
修复:在处理完请求后(不管成功失败),关闭客户端套接字:
if(r > 0) { // ... 发送时间字符串、打印日志的逻辑 closesocket(cSock); } else { // 即使recv失败,也要关闭套接字 closesocket(cSock); }
5. 空请求的处理逻辑缺失
根据Wireshark,客户端发送的是空字符串(PSH, ACK),这时候recv会返回0(表示客户端关闭了发送端连接),但你的代码只有r > 0时才会发送时间字符串,所以这种情况不会返回任何数据。
如果你的需求是无论客户端发送什么(包括空请求)都返回时间字符串,可以调整逻辑:
r = recv(cSock, buf, sizeof(buf), 0); // 只要不是错误,就发送时间字符串 if(r != SOCKET_ERROR) { time(&t); timeinfo = localtime(&t); timeMsg = asctime(timeinfo); r = send(cSock, (char*) timeMsg.c_str(), timeMsg.length(), 0); // ... 后续日志和关闭套接字 }
按照上面的点逐一排查,应该能解决Windows下TCP无响应的问题。
内容的提问来源于stack exchange,提问作者Ian

