Winsock2套接字本地通信正常,远程连接失败求助
TCP远程连接失败(错误码10057)排查与修复
问题背景
基于Winsock实现的TCP客户端/服务端,本地运行时聊天功能正常,但局域网或远程连接时通信失败。客户端调用recv时触发错误,WSAGetLastError返回10057(套接字未连接时尝试发送/接收数据)。已关闭防火墙并配置端口转发,程序使用非阻塞套接字。
服务端代码
#include "sockets.h" int main(){ // SETUP AND BIND SERVER SOCKET SOCKET server_socket = socket_setup(); SOCKADDR_IN address; address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr("192.168.86.179"); //using LAN IP, not local (127.0.0.1) address.sin_port = htons(6502); if(bind(server_socket, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR) WSA_ERR("Socket Bind Error"); if(listen(server_socket, 5) == SOCKET_ERROR) WSA_ERR("Listening Error"); SOCKET accept_socket = SOCKET_ERROR; do{ accept_socket = accept(server_socket, NULL, NULL); } while(accept_socket == SOCKET_ERROR); // SEND AND RECEIVE BYTES char sendmsg[100] = "Hello Client"; char recvmsg[100] = ""; int bytes_sent = send(accept_socket, sendmsg, strlen(sendmsg), 0); if(bytes_sent == SOCKET_ERROR){ WSA_ERR("Send Error"); }else{ printf("Send OK\n"); printf("Bytes Sent: %ld\n", bytes_sent); printf("%s\n", sendmsg); } int bytes_rcvd = blocking_recv(accept_socket, recvmsg); if(bytes_rcvd == SOCKET_ERROR){ WSA_ERR("Recv Error"); }else{ printf("Recv OK\n"); printf("Bytes Rcvd: %ld\n", bytes_rcvd); printf("%s\n", recvmsg); } chat_loop(recvmsg, sendmsg, accept_socket); return 0; }
客户端代码
#include "sockets.h" int main(){ // SETUP AND CONNECT CLIENT SOCKET SOCKET client_socket = socket_setup(); SOCKADDR_IN address; address.sin_family = AF_INET; printf("Enter IP address:\n"); char ip[15]; gets_s(ip, 15); address.sin_addr.s_addr = inet_addr(ip); address.sin_port = htons(6502); if(connect(client_socket, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK){ WSA_ERR("Connection Error"); } // SEND AND RECIEVE BYTES char sendmsg[100] = "Hello Server"; char recvmsg[100] = ""; int bytes_rcvd = blocking_recv(client_socket, recvmsg); if(bytes_rcvd == SOCKET_ERROR){ WSA_ERR("Recv Error"); //<-- WSAGetLastError() from inside macro gives error 10057 here }else{ printf("Recv OK\n"); printf("Bytes Rcvd: %ld\n", bytes_rcvd); printf("%s\n", recvmsg); } int bytes_sent = send(client_socket, sendmsg, strlen(sendmsg), 0); if(bytes_sent == SOCKET_ERROR){ WSA_ERR("Send Error"); }else{ printf("Send OK\n"); printf("Bytes Sent: %ld\n", bytes_sent); printf("%s\n", sendmsg); } chat_loop(recvmsg, sendmsg, client_socket); system("pause"); return 0; }
sockets.h 核心函数
int blocking_recv(SOCKET sock, char buf[]){ int result = 0; do{ result = recv(sock, buf, 100, 0); }while(result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK); return result; }
核心问题分析
错误码10057的直接原因是:客户端在套接字未完成连接建立的情况下,就调用了recv操作。
客户端使用非阻塞套接字,但对connect的处理存在逻辑漏洞:
- 非阻塞模式下,
connect会立即返回SOCKET_ERROR,同时WSAGetLastError返回WSAEWOULDBLOCK,这表示连接正在异步建立中,而非连接失败。 - 当前代码仅判断了
WSAEWOULDBLOCK就跳过错误处理,但没有等待连接真正建立完成,直接进入recv流程,此时套接字实际上还未处于已连接状态,导致10057错误。
另外服务端绑定存在优化空间:
- 服务端绑定了固定LAN IP
192.168.86.179,如果服务器有多网卡或IP变动,会导致监听失效,建议改为绑定INADDR_ANY(允许接收来自所有网卡的连接)。
修复方案
1. 修复客户端非阻塞connect的连接等待逻辑
非阻塞模式下,需要通过select等待连接完成,确保套接字进入已连接状态后再进行收发操作:
// 客户端connect后的等待逻辑,放在connect之后、recv之前 fd_set write_set; FD_ZERO(&write_set); FD_SET(client_socket, &write_set); struct timeval timeout; timeout.tv_sec = 10; // 设置10秒超时 timeout.tv_usec = 0; // 等待套接字可写(表示连接完成) int ret = select(0, NULL, &write_set, NULL, &timeout); if (ret == SOCKET_ERROR) { WSA_ERR("select error"); } else if (ret == 0) { printf("Connect timeout\n"); closesocket(client_socket); return 1; } else { // 检查连接是否真的成功建立 int error = 0; int len = sizeof(error); getsockopt(client_socket, SOL_SOCKET, SO_ERROR, (char*)&error, &len); if (error != 0) { WSA_ERR("Connect failed after select"); } }
2. 优化服务端绑定地址
将服务端的绑定IP改为INADDR_ANY,适配多网卡场景:
// 替换原有的inet_addr("192.168.86.179") address.sin_addr.s_addr = htonl(INADDR_ANY);
3. 补充非阻塞套接字的错误处理细节
blocking_recv函数中,循环处理WSAEWOULDBLOCK是正确的,但要注意如果返回0表示对方关闭连接,需要在调用处处理这种情况。- 客户端
gets_s读取IP时,要校验输入合法性(比如是否是有效IP),避免inet_addr返回INADDR_NONE导致连接失败。
内容的提问来源于stack exchange,提问作者Joshua White
相关产品推荐
相关产品推荐

