Ubuntu 22下C语言Socket多文件传输崩溃及WiFi连接问题求助
问题解决思路与代码参考
一、多文件传输崩溃问题排查方向
1. 网络字节序一致性检查
服务器发送文件数量时,必须使用网络字节序转换函数(htonl()),客户端接收后用ntohl()转换回主机字节序。如果跳过这一步,不同系统(或同系统但套接字传输的字节序问题)会导致文件数量解析错误,引发循环越界或逻辑混乱,直接导致程序崩溃。
2. 资源复用与清理
每次单文件传输完成后,必须:
- 关闭当前打开的文件描述符(
fclose(fp)),避免文件描述符耗尽 - 重置缓冲区(比如用
memset(buf, 0, sizeof(buf))),防止残留数据污染下一次传输 - 检查套接字状态,若传输中途出错,需及时终止循环并释放资源
3. 可靠收发函数封装
单文件传输时的send()/recv()可能只完成了部分数据传输,多文件循环中这种问题会被放大。必须封装可靠的收发函数,确保数据完整传输:
// 可靠发送函数 ssize_t send_all(int sock, void *buf, size_t len) { size_t sent = 0; while (sent < len) { ssize_t ret = send(sock, buf + sent, len - sent, 0); if (ret == -1) return -1; sent += ret; } return sent; } // 可靠接收函数 ssize_t recv_all(int sock, void *buf, size_t len) { size_t received = 0; while (received < len) { ssize_t ret = recv(sock, buf + received, len - received, 0); if (ret == -1 || ret == 0) return -1; received += ret; } return received; }
4. 内存与缓冲区检查
- 避免循环中重复分配内存但不释放,导致内存泄漏
- 确保文件名、文件内容缓冲区的大小足够,防止缓冲区溢出
二、WiFi连接异常问题排查方向
1. 服务器绑定地址修正
将服务器套接字绑定到INADDR_ANY,而非固定的以太网IP,这样可以监听所有网络接口(包括WiFi):
struct sockaddr_in server_addr; server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
2. 防火墙与端口开放
Ubuntu的UFW防火墙可能仅开放了以太网接口的传输端口,需手动开放对应端口:
sudo ufw allow <你的传输端口号>
或临时关闭防火墙测试:sudo ufw disable
3. MTU适配调整
WiFi的MTU通常小于以太网(多为1492字节),如果单文件传输的缓冲区设置为1500,会导致数据包分片异常。建议将传输缓冲区调小至1024字节,或在发送时处理EMSGSIZE错误,拆分数据发送。
4. 超时设置优化
WiFi延迟高于以太网,需为套接字设置合理的超时时间:
struct timeval timeout; timeout.tv_sec = 10; timeout.tv_usec = 0; setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
三、多文件传输核心逻辑示例
服务器端发送逻辑
// 发送文件数量 int file_count = 3; uint32_t net_count = htonl(file_count); if (send_all(client_sock, &net_count, sizeof(net_count)) == -1) { perror("send file count failed"); close(client_sock); return -1; } // 循环传输每个文件 for (int i = 0; i < file_count; i++) { char filename[256] = {0}; // 接收客户端请求的文件名 if (recv_all(client_sock, filename, sizeof(filename)) == -1) break; FILE *fp = fopen(filename, "rb"); if (!fp) { // 发送文件不存在标记(文件大小设为0) uint32_t empty_size = htonl(0); send_all(client_sock, &empty_size, sizeof(empty_size)); continue; } // 获取并发送文件大小 fseek(fp, 0, SEEK_END); uint32_t file_size = htonl(ftell(fp)); fseek(fp, 0, SEEK_SET); if (send_all(client_sock, &file_size, sizeof(file_size)) == -1) { fclose(fp); break; } // 发送文件内容 char buf[1024] = {0}; size_t read_len; while ((read_len = fread(buf, 1, sizeof(buf), fp)) > 0) { if (send_all(client_sock, buf, read_len) == -1) break; memset(buf, 0, sizeof(buf)); } fclose(fp); }
客户端接收逻辑
// 接收文件数量 uint32_t net_count; if (recv_all(server_sock, &net_count, sizeof(net_count)) == -1) { perror("recv file count failed"); close(server_sock); return -1; } int file_count = ntohl(net_count); // 循环接收每个文件 for (int i = 0; i < file_count; i++) { // 发送请求的文件名 char req_filename[256] = "test_file.txt"; // 替换为实际文件名列表 if (send_all(server_sock, req_filename, sizeof(req_filename)) == -1) break; // 接收文件大小 uint32_t net_size; if (recv_all(server_sock, &net_size, sizeof(net_size)) == -1) break; uint32_t file_size = ntohl(net_size); if (file_size == 0) { printf("File %s not found\n", req_filename); continue; } // 接收文件内容 FILE *fp = fopen(req_filename, "wb"); if (!fp) { perror("open file failed"); break; } char buf[1024] = {0}; size_t total_received = 0; while (total_received < file_size) { size_t recv_len = recv(server_sock, buf, sizeof(buf), 0); if (recv_len == -1 || recv_len == 0) break; fwrite(buf, 1, recv_len, fp); total_received += recv_len; memset(buf, 0, sizeof(buf)); } fclose(fp); printf("Received file %s successfully\n", req_filename); }
内容的提问来源于stack exchange,提问作者Amara Oussama
相关产品推荐
相关产品推荐

