TCP服务器自动重连客户端的C语言代码实现咨询
搞定TCP服务器自动恢复连接的方案(适配你的传感器场景)
嘿,先提个小细节:你贴的代码是TCP服务器端,不是客户端哦~你的核心问题是:当远程传感器(作为客户端)频繁断开后,服务器没法自动重新接受新连接,一直卡着对吧?下面给你一步步修改,让服务器能在客户端断开后自动回到监听状态,完美适配你的场景:
核心问题分析
你的原代码在accept成功后就进入死循环读取数据,但当客户端断开时:
read会返回0(客户端正常断开)或者-1(异常断开)- 之后代码会一直卡在无效的循环里,无法重新等待新的连接
修改方案步骤
- 把连接处理逻辑封装成单独函数:这样客户端断开后可以直接重新调用,复用代码更清晰
- 增加连接异常判断:通过
read的返回值识别断开状态,及时关闭当前连接并回到监听 - 添加重试等待机制:避免频繁重试占用系统资源,断开后等待几秒再重新监听
- 优化日志文件逻辑:原代码只在启动时创建一次文件,改成每次新连接创建单独文件(可选,按需调整)
- 增加端口重用选项:解决服务器重启时端口被占用的调试痛点
修改后的完整代码
#include <stdio.h> #include <netdb.h> #include <netinet/in.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <time.h> #include <unistd.h> // 用于sleep函数 #include <errno.h> // 用于错误信息输出 #define MAX 1000 #define PORT 14344 #define SA struct sockaddr // 处理单个客户端连接的函数 void handle_client(int connfd) { unsigned char buff[MAX]; int dd, a; char timebuffer[80]; time_t rawtime; struct tm *timeinfo; // 为每个新连接创建独立日志文件 time(&rawtime); timeinfo = localtime(&rawtime); strftime(timebuffer, 80, "/home/%m%d%y_%H%M%S", timeinfo); FILE *fil1 = fopen(timebuffer, "wb"); if (!fil1) { printf("Failed to create log file: %s\n", strerror(errno)); return; } printf("Log file created: %s\n", timebuffer); printf("Client connected, starting data reading...\n"); while (1) { bzero(buff, MAX); dd = read(connfd, buff, sizeof(buff)); // 判断连接状态:read返回0=正常断开,返回-1=异常错误 if (dd <= 0) { if (dd == 0) { printf("Client disconnected normally.\n"); } else { printf("Read error: %s\n", strerror(errno)); } break; } // 打印并写入数据 printf("\nReceived data: "); for (a = 0; a < dd; a++) { fprintf(fil1, "%c", buff[a]); printf("%02X ", buff[a]); } printf("\n"); fflush(fil1); // 立即写入文件,避免数据丢失 } // 清理资源 fclose(fil1); close(connfd); printf("Connection closed, waiting for new client...\n"); } int main() { int sockfd, connfd, len; struct sockaddr_in servaddr, cli; printf("Starting TCP server on port %d...\n", PORT); // 创建Socket sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { printf("Socket creation failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } printf("Socket successfully created.\n"); // 设置端口重用选项:避免服务器重启后端口占用问题 int opt = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { printf("Setsockopt failed: %s\n", strerror(errno)); close(sockfd); exit(EXIT_FAILURE); } // 配置服务器地址 bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); // 绑定Socket if (bind(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { printf("Socket bind failed: %s\n", strerror(errno)); close(sockfd); exit(EXIT_FAILURE); } printf("Socket successfully binded.\n"); // 主循环:持续监听和接受新连接 while (1) { // 开始监听 if (listen(sockfd, 5) != 0) { printf("Listen failed: %s\n", strerror(errno)); sleep(3); // 失败后等待3秒再重试 continue; } printf("Server listening for incoming connections...\n"); len = sizeof(cli); // 等待客户端连接 connfd = accept(sockfd, (SA*)&cli, &len); if (connfd < 0) { printf("Accept failed: %s\n", strerror(errno)); sleep(3); // 失败后等待3秒再重试 continue; } printf("Server accepted new connection.\n"); // 处理当前客户端连接,断开后自动回到主循环重新等待 handle_client(connfd); } // 理论上不会执行到这里,还是加上资源清理 close(sockfd); return 0; }
关键修改点说明
- 封装
handle_client函数:把和客户端交互的逻辑抽离,主循环只负责监听连接,代码更易维护 - 连接状态判断:通过
read的返回值精准识别客户端断开,及时退出处理流程 - SO_REUSEADDR选项:解决服务器重启时端口被占用的常见问题,调试更顺畅
- 独立日志文件:每个连接的数据存储在单独文件中,避免覆盖旧数据(不需要的话可以改回原逻辑)
- 重试等待:用
sleep降低重试频率,减少系统资源消耗 - fflush强制写入:确保数据实时写入文件,防止客户端断开时缓冲区数据丢失
额外小建议
如果之后需要同时处理多个传感器连接,可以考虑引入多线程或多路复用(select/poll/epoll),不过单传感器场景下当前方案完全够用。
内容的提问来源于stack exchange,提问作者JMF
相关产品推荐
相关产品推荐

