C Socket编程逐行传输大文本到服务器仅到第84行中断问题
问题描述
我编写的TCP逐行传输文件程序,客户端能正常读取10000行的大文本文件,但每次传输都在第84行中断。传输小体积文件时程序运行完全正常,初步判断问题大概率出在服务端接收缓冲区逻辑上。我是C语言与TCP Socket编程的新手,恳请帮忙定位问题根源。
相关代码、测试数据文件示例如下:
客户端代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <ctype.h> #define PORT "20000" #define HOST "::1" int main() { struct addrinfo hints, *res, *result; int sockfd; char *line = NULL; size_t len = 0; ssize_t readLine; int number; FILE *filePointer; int valread; memset (&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_PASSIVE; int errcode = getaddrinfo(HOST, PORT, &hints, &result); if (errcode != 0){ perror("无法获取地址"); return -1; } res = result; while (res){ sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd >= 0) break; res = res->ai_next; } if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) { perror("连接失败"); exit(EXIT_FAILURE); } filePointer = fopen("dataBIG.txt", "r"); if (filePointer == NULL) { printf("无法打开data.txt"); } else { readLine = getline(&line, &len, filePointer) - 1; sscanf(line, "%d", &number); send(sockfd, line, sizeof(line), 0); valread = recv(sockfd, line, sizeof(line), 0); for(int i = 0; i < number; i++){ readLine = getline(&line, &len, filePointer) - 1; printf("%d\n", i); if(readLine != 0){ send(sockfd, line, sizeof(line), 0); } valread = recv(sockfd, line, sizeof(line), 0); } fclose(filePointer); } freeaddrinfo(result); return 0; }
服务端代码
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <ctype.h> #define PORT1 "20000" #define HOST "::1" int main() { struct addrinfo hints, *res, *result; int errcode; char str[300]; void *ptr; int sockfd, new_socket, valread; int number; char *message = "ALL GOOD"; memset (&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_PASSIVE; errcode = getaddrinfo(HOST, PORT1, &hints, &result); if (errcode != 0){ perror("无法获取地址"); return -1; } res = result; while (res){ sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd >= 0) break; res = res->ai_next; } if (bind(sockfd, res->ai_addr, res->ai_addrlen) < 0) { perror("Bind调用失败"); exit(EXIT_FAILURE); } if (listen(sockfd, 3) < 0){ perror("Listen调用错误"); exit(EXIT_FAILURE); } int addrlen = sizeof(res); if ((new_socket = accept(sockfd, (struct sockaddr*)&res, (socklen_t*)&addrlen)) < 0) { perror("Accept调用错误"); exit(EXIT_FAILURE); } valread = recv(new_socket, str, sizeof(str), 0); sscanf(str, "%d", &number); send(new_socket, message, sizeof(message), 0); for(int i = 0; i < number; i++){ valread1 = recv(new_socket, str, sizeof(str), 0); int l = strlen(str); int j = 0, count = 0; for(j; j < l; j++){ if ('a' == str[j] || 'A' == str[j]) count++; } printf("%d letter A count = %d\n", i+1, count); sprintf(str, "%d", count); send(new_socket, message, sizeof(message), 0); } freeaddrinfo(result); return 0; }
测试数据文件示例
10000 Hi Car Alex ...
问题根源定位
问题和服务端接收缓冲区容量没有关系,核心是几处硬性逻辑错误:
- send调用的长度参数完全错误
所有send操作都用sizeof(指针)作为发送长度。客户端的line、服务端的message都是指针类型,64位系统下这类指针的sizeof结果固定为8字节,和实际要发送的字符串长度没有任何关系。也就是说每次发送的内容根本不是有效文本,只是指针本身的内存值。当TCP缓冲区攒够一定量的无效数据、两边收发节奏错开后,程序就会阻塞在recv调用上,表现为固定传输次数后中断。
正确写法应该用strlen(实际要发的字符串)计算发送长度,同时处理send返回的实际发送字节数——TCP是流协议,单次send不保证把所有请求的字节全部发完。 - 服务端存在未定义变量
循环中使用的valread1没有提前声明,正规编译流程下这段代码根本无法通过。能运行依赖了编译器宽松的隐式声明规则,运行时这个变量的值是随机的,会直接导致后续逻辑失控。 - recv调用完全没有边界判断
不管客户端还是服务端,调用recv后完全不检查返回值:返回0代表对方正常关闭连接,返回-1代表调用出错。不做判断的话,程序会直接把空缓冲区内容传给strlen处理,轻则触发段错误,重则永久死锁。 - 协议设计不符合TCP流特性
TCP是面向字节流的协议,没有内置的消息边界。代码默认一次recv就能拿到一整行的完整内容,但实际网络传输中,可能两行内容被合并到同一个TCP包,也可能一行内容被拆成多个TCP包。传输小文件时数据量小、收发节奏容易对齐,一旦数据量上升,缓冲区攒够足够多的内容后,边界错位就会直接卡死流程。 - 服务端发送内容逻辑错位
循环里统计完字母A的数量、把结果格式化到str数组后,发送时仍然固定发"ALL GOOD"字符串,完全没有把统计结果回传给客户端。
内容的提问来源于stack exchange,提问作者DarkDudeD
相关产品推荐
相关产品推荐

