Windows下C Socket调用recv()随机返回-1但errno为0问题排查
你的代码存在几个关键问题,导致recv()出现返回-1但errno为0的异常情况,以下是具体问题和修复方式:
1. Windows下Winsock错误处理误用errno
Winsock API的错误码不能通过errno获取,必须使用WSAGetLastError()函数。errno是POSIX标准的错误机制,Windows下Winsock有独立的错误体系,当Winsock函数返回SOCKET_ERROR(即-1)时,要调用WSAGetLastError()获取真实错误码,而非读取errno。你代码中所有依赖errno的错误判断都是错误的,这直接导致了错误信息异常、errno为0的情况。
修复示例:
服务器中recv的错误判断修改为:
int len = recv(clientSocket, buffer, BUFFER_LEN, 0); if (len == SOCKET_ERROR) { int err = WSAGetLastError(); // 排除非阻塞模式的WSAEWOULDBLOCK(对应POSIX的EAGAIN) if (err != WSAEWOULDBLOCK) { printf("Erreur lors de la réception du message. Code d'erreur: %d\n", err); exit(EXIT_FAILURE); } }
客户端创建socket的错误处理修改为:
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) { int err = WSAGetLastError(); printf("Erreur lors de la création du socket. Code d'erreur: %d\n", err); exit(EXIT_FAILURE); }
2. 客户端未正确关闭socket就退出
客户端调用send()后直接执行WSACleanup()并退出,未调用closesocket(sock)关闭套接字。这会导致操作系统强制终止连接,服务器端recv()可能收到连接提前关闭的信号,表现为异常返回。
修复:在客户端的WSACleanup()之前添加:
closesocket(sock);
3. 客户端sprintf调用参数非法
客户端中connect和send的错误处理里,sprintf的第一个参数是字符串常量"Erreur...",这属于非法操作——sprintf的第一个参数必须是可写的缓冲区,否则会引发内存访问错误,导致未定义行为。
修复:定义缓冲区存储错误信息,示例:
char err_buf[256]; if ((connect(sock, (SOCKADDR *)&address, sizeof(address))) == SOCKET_ERROR) { int err = WSAGetLastError(); sprintf(err_buf, "Erreur lors de la connexion au socket. Code d'erreur: %d\n", err); printf("%s", err_buf); exit(EXIT_FAILURE); }
4. 服务器recv后未添加字符串终止符
recv()接收到的字节流不会自动添加\0终止符,直接用printf("%s", buffer)会打印到内存中第一个\0为止,大概率出现乱码。
修复:recv成功后手动添加终止符,同时预留一个字节的缓冲区空间:
char buffer[BUFFER_LEN]; // 留一个字节给终止符,避免越界 int len = recv(clientSocket, buffer, BUFFER_LEN - 1, 0); // ... 错误判断 ... else if (len > 0) { buffer[len] = '\0'; // 确保字符串终止 printf("Message reçu : %s\n", buffer); }
关键修正后的代码片段
修正后的client.c核心部分
// ... 其他代码 ... if ((connect(sock, (SOCKADDR *)&address, sizeof(address))) == SOCKET_ERROR) { char err_buf[256]; int err = WSAGetLastError(); sprintf(err_buf, "Erreur lors de la connexion au socket. Code d'erreur: %d\n", err); printf("%s", err_buf); exit(EXIT_FAILURE); } // ... 发送代码 ... printf("Bytes envoyés : %d\n", bytesSent); closesocket(sock); // 新增关闭socket WSACleanup(); return 0;
修正后的server.c核心部分
// ... accept之后的代码 ... char buffer[BUFFER_LEN]; int len = recv(clientSocket, buffer, BUFFER_LEN - 1, 0); if (len == SOCKET_ERROR) { int err = WSAGetLastError(); if (err != WSAEWOULDBLOCK) { printf("Erreur lors de la réception du message. Code d'erreur: %d\n", err); exit(EXIT_FAILURE); } } else if (len == 0) { printf("Le client s'est déconnecté (extrémité de la socket fermée)\n"); exit(EXIT_FAILURE); } else if (len > 0) { buffer[len] = '\0'; printf("Message reçu : %s\n", buffer); } // ... 关闭socket代码 ...
内容的提问来源于stack exchange,提问作者Leogout

