C语言TCP Socket回显程序问题:服务器recv()调用失败
问题分析与修复:TCP回显程序服务器recv失败问题
核心错误原因
服务器端误用监听套接字进行数据收发是导致recv失败的根本原因:
accept()函数会返回一个新的套接字描述符,这个才是用来和已连接客户端进行数据交互的句柄- 原代码中没有保存
accept()的返回值,反而用最初创建的监听套接字serverSoc调用recv()/send(),这完全不符合TCP套接字的工作逻辑——监听套接字仅用于接受新连接,不能直接收发数据
其他次要问题
- 传递数组地址时多余取址:数组名本身就是首地址,无需加
&,否则会传递指针的地址,导致数据读写错误 - 未处理字符串结束符:
recv()不会自动添加\0,直接用printf("%s")会出现乱码 - 客户端发送时用
sizeof而非strlen:会把数组中未使用的空字符也发出去,浪费带宽 - 客户端逻辑错误:收到服务器消息后直接
return -1,导致程序异常退出
修复后的Server.c
#include<stdio.h> #include<stdlib.h> #include<sys/socket.h> #include<sys/types.h> #include<netinet/ip.h> #include<string.h> int main(){ // 创建监听套接字 int serverSoc = socket(PF_INET, SOCK_STREAM, 0); if (serverSoc < 0) { perror("socket creation failed"); return -1; } struct sockaddr_in serverInfo; serverInfo.sin_family = PF_INET; serverInfo.sin_port = htons(8000); serverInfo.sin_addr.s_addr = INADDR_ANY; socklen_t addrLen = sizeof(serverInfo); // 绑定套接字 if(bind(serverSoc, (struct sockaddr*)&serverInfo, addrLen) < 0){ perror("bind failed"); return -1; } // 监听连接 if(listen(serverSoc, 1) < 0){ perror("listen failed"); return -1; } // 接受客户端连接,保存新的通信套接字 int clientConn = accept(serverSoc, (struct sockaddr*)&serverInfo, &addrLen); if(clientConn < 0){ perror("accept failed"); return -1; } printf("客户端已连接\n"); char recvBuff[50]; ssize_t bytesRead = recv(clientConn, recvBuff, sizeof(recvBuff)-1, 0); if(bytesRead < 0){ perror("recv failed"); return -1; }else{ // 添加字符串结束符,避免乱码 recvBuff[bytesRead] = '\0'; printf("收到客户端消息: %s\n", recvBuff); } char sendBuff[50] = "hello from server"; ssize_t bytesSent = send(clientConn, sendBuff, strlen(sendBuff), 0); if(bytesSent < 0){ perror("send failed"); return -1; }else{ printf("已发送消息给客户端,字节数: %zd\n", bytesSent); } // 关闭套接字 close(clientConn); close(serverSoc); return 0; }
修复后的Client.c
#include<stdio.h> #include<netinet/in.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<string.h> #include<unistd.h> int main(){ char clientRecv[50]; char clientSend[50] = "Hello Server!"; // 创建客户端套接字 int clientSoc = socket(PF_INET, SOCK_STREAM, 0); printf("客户端套接字描述符: %d\n", clientSoc); if(clientSoc < 0){ perror("socket creation failed"); return -1; } struct sockaddr_in serverInfo; serverInfo.sin_family = PF_INET; serverInfo.sin_port = htons(8000); // 连接本地服务器,也可替换为具体IP serverInfo.sin_addr.s_addr = inet_addr("127.0.0.1"); // 连接服务器 if(connect(clientSoc, (struct sockaddr*)&serverInfo, sizeof(serverInfo)) < 0){ perror("connect failed"); return -1; } printf("已连接到服务器\n"); ssize_t bytesSent = send(clientSoc, clientSend, strlen(clientSend), 0); if(bytesSent < 0){ perror("send failed"); return -1; } printf("已发送消息给服务器,字节数: %zd\n", bytesSent); ssize_t bytesRead = recv(clientSoc, clientRecv, sizeof(clientRecv)-1, 0); if(bytesRead < 0){ perror("recv failed"); }else{ clientRecv[bytesRead] = '\0'; printf("收到服务器消息: %s\n", clientRecv); } // 关闭套接字 close(clientSoc); return 0; }
额外说明
- 使用
perror()替代自定义printf,能更准确地输出系统错误信息,方便调试 - 所有套接字使用完毕后要调用
close()关闭,避免资源泄漏 - 客户端连接时用
inet_addr("127.0.0.1")替代INADDR_ANY,更明确指向本地服务器
内容的提问来源于stack exchange,提问作者Inderjeet
相关产品推荐
相关产品推荐

