C语言Socket单连接聊天程序异常:回复消息后陷入无限循环求助
排查与解决Socket单连接聊天程序的无限循环问题
常见问题根源
1. 换行符残留坑
用fgets()读取用户输入时,会把换行符\n也塞进缓冲区。要是直接把带换行的内容发出去,服务器收到的是xxx\n,和你要判断的"end"完全不匹配,触发不了结束逻辑,循环就一直跑。
2. 没处理recv()/send()的返回值
很多人会忽略这俩函数的返回值:
recv()返回0意味着客户端已经关闭连接,返回-1是出错了,要是不管这些继续循环,程序就会无限执行。send()失败后没做处理,也会导致循环卡死。
3. 循环结束逻辑写崩了
比如主循环直接写while(1),但收到"end"后没正确关闭套接字或跳出循环,自然会无限转。
4. 缓冲区没清空
每次接收消息后,没把缓冲区用memset()清干净,旧数据和新数据混在一起,干扰"end"的判断。
解决步骤+修正代码
先解决换行符问题
发送前把fgets()读进来的换行符替换成字符串结束符:
char buf[1024]; fgets(buf, sizeof(buf), stdin); // 用strcspn找到换行符位置,直接替换成'\0' buf[strcspn(buf, "\n")] = '\0';
严格处理返回值
每次调用recv()/send()后必须检查返回值:
// 服务器接收示例 ssize_t recv_len = recv(client_fd, buf, sizeof(buf)-1, 0); if (recv_len <= 0) { // 连接断了或出错,直接关套接字跳出循环 close(client_fd); break; } buf[recv_len] = '\0'; // 手动加结束符,避免乱码
写对结束逻辑
收到"end"后,立刻关套接字+跳出循环:
if (strcmp(buf, "end") == 0) { printf("关闭连接\n"); close(client_fd); break; }
完整修正代码
server.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8080 #define BUF_SIZE 1024 int main() { int server_fd, client_fd; struct sockaddr_in address; int opt = 1; int addrlen = sizeof(address); char buf[BUF_SIZE]; // 创建套接字 if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // 设置套接字可重用端口/地址 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) { perror("setsockopt"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // 绑定端口 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听连接,最多排队3个 if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } // 接受客户端连接 if ((client_fd = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } printf("客户端已连接\n"); while (1) { // 清空缓冲区 memset(buf, 0, BUF_SIZE); // 接收客户端消息 ssize_t recv_len = recv(client_fd, buf, BUF_SIZE-1, 0); if (recv_len <= 0) { printf("客户端连接关闭\n"); close(client_fd); break; } buf[recv_len] = '\0'; printf("收到客户端消息: %s\n", buf); // 判断是否结束 if (strcmp(buf, "end") == 0) { printf("关闭连接\n"); close(client_fd); break; } // 输入回复 printf("请输入回复: "); fgets(buf, BUF_SIZE, stdin); buf[strcspn(buf, "\n")] = '\0'; // 自己输入end也关闭 if (strcmp(buf, "end") == 0) { send(client_fd, buf, strlen(buf), 0); printf("关闭连接\n"); close(client_fd); break; } // 发送回复 ssize_t send_len = send(client_fd, buf, strlen(buf), 0); if (send_len < 0) { perror("send failed"); close(client_fd); break; } } close(server_fd); return 0; }
client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8080 #define BUF_SIZE 1024 int main() { int sock = 0; struct sockaddr_in serv_addr; char buf[BUF_SIZE]; // 创建套接字 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); // 转换本地IP地址 if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { perror("invalid address"); exit(EXIT_FAILURE); } // 连接服务器 if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { perror("connection failed"); exit(EXIT_FAILURE); } printf("已连接到服务器\n"); while (1) { // 输入消息 printf("请输入消息: "); fgets(buf, BUF_SIZE, stdin); buf[strcspn(buf, "\n")] = '\0'; // 输入end就终止 if (strcmp(buf, "end") == 0) { send(sock, buf, strlen(buf), 0); printf("终止连接\n"); close(sock); break; } // 发送消息 ssize_t send_len = send(sock, buf, strlen(buf), 0); if (send_len < 0) { perror("send failed"); close(sock); break; } // 清空缓冲区接收回复 memset(buf, 0, BUF_SIZE); ssize_t recv_len = recv(sock, buf, BUF_SIZE-1, 0); if (recv_len <= 0) { printf("服务器连接关闭\n"); close(sock); break; } buf[recv_len] = '\0'; printf("收到服务器回复: %s\n", buf); // 服务器发end也关闭 if (strcmp(buf, "end") == 0) { printf("服务器关闭连接\n"); close(sock); break; } } return 0; }
编译运行
# 编译服务器 gcc server.c -o server # 编译客户端 gcc client.c -o client # 先开服务器 ./server # 再开客户端 ./client
内容的提问来源于stack exchange,提问作者bekahboo
相关产品推荐
相关产品推荐

