Linux下C语言服务器与客户端send()/recv()安全数据传输异常问题排查
Linux下C语言服务器与客户端send()/recv()安全数据传输异常问题排查
嘿,我看了你的问题和代码,首先得明确:你遇到的问题根本不是send/recv的flags能解决的——核心原因是你误解了TCP协议的特性,它是字节流协议,不是消息流协议,没法保证你send一次的数据,recv就能原封不动一次收到。
先给你拆解下问题出在哪:
- 你服务器每次send 14字节的一行数据,但TCP会把这些字节当成连续的流来传输,客户端的recv可能一次只收到一部分(比如某次收了10字节,剩下4字节和下一行的前10字节粘在一起),也可能一次收到多行的内容。而你现在客户端每次循环只调用一次recv就直接把buffer当成完整一行处理,这自然会出现字符串移位、错乱的情况。
- 另外,你还忽略了send的返回值——send不一定能把14字节全部发出去,比如当内核缓冲区满的时候,send只会发送一部分字节,剩下的需要你循环发送,否则数据就丢了。
为什么flags救不了你?
比如你可能想到用MSG_WAITALL flag让recv一直等到收到指定长度,但这个flag有局限性:如果连接中途断开、或者出现网络错误,它的行为可能不符合预期,而且不是所有系统都能完美支持,本质上还是绕开了字节流的问题,不是可靠的解决方案。
正确的解决思路:处理应用层消息边界
因为你的每行数据是固定14字节的,最适合的方案就是固定长度消息传输——不管send/recv分多少次,我们确保每次凑够完整的14字节再处理。
先改服务器端的send逻辑:
你原来直接调用send,没有处理部分发送的情况,改成循环send直到全部字节发送完成:
void signal_data_generator( int connfd ) { // ... 你的原有代码 ... str_line[13] = '\0'; printf("%s\n", str_line); // 替换原来的send,确保14字节全部发送 ssize_t total_sent = 0; ssize_t sent_bytes; const char* send_buf = str_line; int total_len = 14; while (total_sent < total_len) { sent_bytes = send(connfd, send_buf + total_sent, total_len - total_sent, 0); if (sent_bytes == -1) { perror("send failed"); close(connfd); return; } total_sent += sent_bytes; } }
再改客户端的recv逻辑:
客户端需要循环recv,直到凑够完整的14字节再处理:
// 客户端循环部分修改 char buffer[15]; // 留一个位置放终止符 for( idx =0; idx < 1024; idx++) { ssize_t total_recv = 0; ssize_t recvd_bytes; while (total_recv < 14) { recvd_bytes = recv(client_fd, buffer + total_recv, 14 - total_recv, 0); if (recvd_bytes == -1) { perror("recv failed"); close(client_fd); exit(EXIT_FAILURE); } else if (recvd_bytes == 0) { // 服务器主动关闭连接了 printf("Server closed connection prematurely\n"); close(client_fd); exit(EXIT_SUCCESS); } total_recv += recvd_bytes; } buffer[14] = '\0'; // 确保字符串以'\0'结尾 puts(buffer); }
额外提两个小问题:
- 服务器里的
srand(time(NULL)):你每次调用signal_data_generator都调用它,但time(NULL)是秒级的,同一秒内的多次调用会重置随机数种子,导致生成的随机数完全一样。应该把srand放在程序启动时(比如main函数开头)只调用一次。 - 你的formatXData/formatYData函数里的条件判断有冗余,比如
num < DIGIT1和num < DIGIT2的分支都是sprintf( temp_str," %d",num);,可以合并,不过这不是传输问题的根源,只是代码优化点。
总结
TCP字节流的特性决定了我们必须在应用层处理消息边界,要么用固定长度,要么用分隔符(比如换行符),而不是依赖send/recv的flags。只要保证每次处理的是完整的消息单元,你看到的字符串移位问题就会消失。
内容来源于stack exchange
相关产品推荐
相关产品推荐

