使用C++ Socket传输文件时开头偶发出现随机多余字节问题求助
问题根源
- 核心原因是TCP是字节流协议,单次
send/recv调用不保证传输完指定长度的数据,你的代码没有处理半包/粘包情况,偶发触发逻辑错误:- 发送端传输文件大小的时候,直接发送了整个
MAX_BUFFER长度的msg数组,数组未初始化的部分携带栈内存残留的脏数据(包括你之前打印的File Size is这类字符串残留) - 接收端第一次调用
recv读取文件大小时,假设单次就能收满MAX_BUFFER字节,但实际可能只收到部分长度的大小包,剩下的大小包字节会被后续接收文件的逻辑当做文件内容写入,就出现了开头随机字节、附带无关字符串的问题 - 发送端读取文件内容前多余执行了
sprintf(buffer, "%d\0", size);,虽然不直接触发错误,但属于无效代码,极端情况如果fread调用失败会额外发送无效的大小字符串 - 所有
send/recv调用都没有判断返回值,也没有循环处理确保传输完预期长度的数据,网络波动时很容易出现传输错误
- 发送端传输文件大小的时候,直接发送了整个
修复步骤
- 修正文件大小的传输逻辑,不要发送整个未初始化的
MAX_BUFFER数组,建议直接传输4字节固定长度的二进制整型(统一用网络字节序避免大小端问题),避免脏数据,也不需要解析字符串 - 封装
send_all和recv_all工具函数,循环调用send/recv确保预期长度的数据全部传输完成 - 删除发送端读取文件前的无效
sprintf代码 - 所有IO调用增加错误判断,处理连接断开、读写错误的情况
修正后的核心代码示例
工具函数封装
// 确保发送完指定长度的数据 ssize_t send_all(int sock, void *buf, size_t len) { size_t total_sent = 0; while (total_sent < len) { ssize_t sent = send(sock, (char*)buf + total_sent, len - total_sent, 0); if (sent <= 0) return sent; // 出错或连接关闭 total_sent += sent; } return total_sent; } // 确保接收完指定长度的数据 ssize_t recv_all(int sock, void *buf, size_t len) { size_t total_recv = 0; while (total_recv < len) { ssize_t recvd = recv(sock, (char*)buf + total_recv, len - total_recv, 0); if (recvd <= 0) return recvd; // 出错或连接关闭 total_recv += recvd; } return total_recv; }
发送端修正
int Socket::send_file(string path,int socket_){ if (socket_ == -1 ) { socket_ = clientSd ; } FILE *fd = fopen(path.c_str(), "rb"); if (!fd) { cout << "Failed to open file " << endl ; exit(0); } // 获取文件大小 fseek(fd, 0, SEEK_END); int size = ftell(fd); fseek(fd, 0, SEEK_SET); cout << "File Size is " << size << endl ; // 转网络字节序发送4字节固定长度大小 int net_size = htonl(size); send_all(socket_, &net_size, sizeof(net_size)); char buffer[MAX_BUFFER]; int bytes_read; while ((bytes_read = fread(buffer, 1, MAX_BUFFER, fd)) > 0) { send_all(socket_, buffer, bytes_read); } fclose(fd); cout << "End" << endl ; return 1 ; }
接收端修正
int Socket::receive_file(string path,int socket_){ if (socket_ == -1 ) { socket_ = clientSd ; } int net_size = 0; // 收4字节大小,转主机字节序 if (recv_all(socket_, &net_size, sizeof(net_size)) <= 0) { cout << "Receive file size failed" << endl; return -1; } int size = ntohl(net_size); cout << "File Size is " << size << endl ; FILE* fd = fopen(path.c_str(), "wb"); if (!fd) { cout << "Open file for write failed" << endl; return -1; } char buffer[MAX_BUFFER]; int received = 0 ; while (received < size) { int to_read = min(MAX_BUFFER, size - received); ssize_t datasize = recv_all(socket_, buffer, to_read); if (datasize <= 0) break; fwrite(buffer, 1, datasize, fd); received += datasize; } fclose(fd); cout << "End" << endl ; return 1 ; }
内容的提问来源于stack exchange,提问作者Zied
相关产品推荐
相关产品推荐

