客户端发送空行至服务器后程序停滞,需实现含空行文件正常传输
兄弟,我完全懂你现在的头疼事——逐行发送文件碰到空行时,客户端发完空缓冲区就卡在等服务器响应的环节,连Wireshark都抓不到空行的数据包,跳过空行虽然能跑通,但你又不想丢失文件里的空行内容。咱们来一步步把这个问题拆解清楚,然后彻底解决它。
问题根源分析
你现在的代码里,当碰到空行时,ligne1是空字符串,调用send(clientSd, ligne1.data(), ligne1.size(), 0)相当于发送0字节的数据。这时候操作系统不会生成任何网络数据包,服务器根本收不到这个“空行”的请求,自然不会给你返回响应,客户端就一直阻塞在recv()调用上,程序直接僵住。
另外,你原来用while(!fichier.eof())判断循环结束也有坑——eof()只有在读取失败后才会置位,大概率会导致多循环一次,这也是潜在的问题。
解决方案
1. 客户端:给空行发送明确的标识,修正循环判断逻辑
不能发送0字节,要给服务器一个“这是空行”的信号。最简单的方式是把getline读取到的行(会自动去掉换行符)加上换行符再发送,空行的话就发送一个单独的换行符。同时把循环判断改成while(getline(fichier, ligne1)),这是C++读取文件行的标准正确写法。
修改后的客户端代码:
// 替换原来的while循环 while (getline(fichier, ligne1)) { memset(&msg, 0, sizeof(msg)); cout << "********lu et copy dans msg ******** " << endl; cout << "********ligne "<< ligne1 << endl; // 给每行加上换行符,空行就发送一个换行符 string send_data = ligne1 + "\n"; ssize_t sent_bytes = send(clientSd, send_data.data(), send_data.size(), 0); if (sent_bytes == -1) { perror("发送失败"); break; } bytesWritten += sent_bytes; cout << "********bytesWritten "<< bytesWritten << endl; cout << "********attente ************** "<< endl; memset(&msg, 0, sizeof(msg)); // recv时留一个字节给'\0',避免乱码 ssize_t recvd_bytes = recv(clientSd, (char*)&msg, sizeof(msg) - 1, 0); if (recvd_bytes == -1) { perror("接收失败"); break; } else if (recvd_bytes == 0) { cout << "服务器已断开连接" << endl; break; } // 手动添加字符串结束符 msg[recvd_bytes] = '\0'; cout << "********msg "<< msg << endl; cout << "Server: " << msg << endl; }
2. 服务器:确保收到任何数据(包括空行标识)都回复
服务器原来的逻辑是收到数据后写入文件再回复,但现在客户端会发送换行符作为空行标识,服务器需要正确接收并回复,同时要处理循环接收的情况(避免单次recv没读完数据)。
修改后的服务器代码:
char buffer[1024]; ssize_t valread; // 循环接收直到客户端断开 while ((valread = recv(sd, buffer, sizeof(buffer) - 1, 0)) > 0) { buffer[valread] = '\0'; // 确保字符串正确结束 // 写入文件,包括空行的换行符 std::ofstream outfile("fichier.txt", std::ios_base::app); outfile << buffer; outfile.close(); // 回复客户端,无论收到的是内容还是空行标识 string data = "bien reçu"; memset(buffer, 0, sizeof(buffer)); strcpy(buffer, data.c_str()); send(sd, buffer, strlen(buffer), 0); memset(buffer, 0, sizeof(buffer)); } // 处理接收异常或客户端断开的情况 if (valread == -1) { perror("接收失败"); } else if (valread == 0) { cout << "客户端已断开连接" << endl; }
为什么Wireshark抓不到空行的包?
因为当你发送0字节时,操作系统不会生成任何网络数据包——这个send调用只是返回0,没有实际的网络传输行为,所以Wireshark自然看不到对应的包。只有当你发送至少1字节(比如换行符)时,才会有数据包被发出,Wireshark才能捕获到。
总结
核心问题就是空行时客户端没有发送任何实际的网络数据,服务器收不到请求所以不回复,导致客户端阻塞在recv。只要让客户端给空行发送明确的标识(比如换行符),服务器收到后正常回复,就能完美解决这个问题,同时保留文件里的空行内容。
内容的提问来源于stack exchange,提问作者Thierno

