C语言循环监听socket的简易HTTP服务器代码问题排查
极简HTTP服务器Socket逻辑异常排查
问题原始代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <netdb.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <pthread.h> #include <arpa/inet.h> struct sockaddr_in server, acc; int sock; int stuff(void) { char buffer[2000]; recv(sock, buffer, sizeof(buffer), 0); send(sock, "HTTP/1.1 200 OK\nContent-Type: text/html\n\nHello", sizeof("HTTP/1.1 200 OK\nContent-Type: text/html\n\nHello"), 0); shutdown(sock, SHUT_RDWR); return 0; } int main(void) { int size; sock = socket(AF_INET, SOCK_STREAM, 0); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(1111); if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1) { perror("Error:"); return 1; } while (1) { listen(sock, 5); sock = accept(sock, (struct sockaddr*)&acc, &size); printf("accepted\n"); stuff(); close(sock); } }
故障表现
- 客户端连接无法正常终止,浏览器访问服务时页面持续加载无响应
- 控制台循环打印第42行的
accepted日志 - 服务无法正常处理新的连接请求
根因分析
代码存在4个核心逻辑错误,直接导致所有异常现象:
- 套接字变量混用覆盖:全局变量
sock一开始存储的是绑定1111端口的监听套接字,但第一次调用accept时,直接将函数返回的已连接套接字赋值给了sock,原始监听套接字的文件描述符被覆盖丢失。后续循环再调用listen、accept时,操作的都是和客户端通信的已连接套接字,而非监听套接字,逻辑完全失效。同时代码未检查任何系统调用的返回值,哪怕accept调用失败返回-1,也会直接打印日志进入后续流程,导致控制台循环输出accepted。 accept参数未初始化且类型错误:accept的第三个参数要求传入指向socklen_t类型的指针,且指针指向的值必须提前初始化为客户端地址结构体acc的长度。代码中int size;是未初始化的栈变量,存储的是随机垃圾值,直接导致accept行为不可控。listen调用位置错误:listen的作用是将套接字标记为被动监听状态,只需要在bind成功后、进入连接接收循环前调用1次即可。代码将listen放在循环内部反复调用,在套接字被覆盖为已连接套接字后,这些调用本身就是无效操作。- HTTP响应格式不符合规范:返回的响应头使用
\n作为行分隔符,而HTTP标准要求行结束符必须是\r\n;同时响应没有携带Content-Length或Connection: close标识,浏览器无法判断响应是否已经传输完成,会一直保持连接等待后续数据,表现为页面持续加载。
修复方向
- 拆分套接字变量:单独定义监听套接字变量和每次接收连接生成的已连接套接字变量,禁止混用覆盖
- 进入循环前调用一次
listen,不要在循环内重复调用 - 初始化
accept所需的地址长度参数,使用正确的socklen_t类型传参 - 为所有系统调用添加返回值检查,出错时打印错误信息及时感知问题
- 按照HTTP规范调整响应格式,使用标准换行符,添加
Connection: close头明确告知客户端响应结束可以关闭连接
内容的提问来源于stack exchange,提问作者jerald29
相关产品推荐
相关产品推荐

