【C/C++】用 C 写 HTTP 客户端

用 C 写 HTTP 客户端:为什么 select 还要配合非阻塞 IO

学习代码:http/http_request.c

在这里插入图片描述

HTTP 项目表面上是写一个 GET/POST 客户端,本质上是在练 TCP 客户端的完整流程:域名解析、建立连接、构造请求、发送数据、循环接收响应。和浏览器相比,自己用 C 写一遍会发现,HTTP 文本格式很清楚,但网络 IO 的边界情况一点都不少。

一个最基础的 GET 请求可以这样构造:

snprintf(request, sizeof(request),
         "GET %s HTTP/1.1\r\n"
         "Host: %s\r\n"
         "User-Agent: SimpleCClient/1.0\r\n"
         "Accept: */*\r\n"
         "Connection: close\r\n"
         "\r\n",
         path, host);

这里要注意 \r\n,HTTP 请求行和请求头都用回车换行结束,头部结束后还要有一个空行。Connection: close 的作用是告诉服务器响应完成后关闭连接,这样客户端可以通过 recv 返回 0 判断对端关闭,简化读取逻辑。

项目里更关键的是 select 接收模板。刚开始我以为 select 返回可读之后,recv 就一定不会阻塞。后来看笔记才明白:select 只能说明“当时看起来可读”,它不是永久保证。多线程抢读、连接异常、协议状态变化,都可能让后续阻塞调用卡住。因此更稳妥的方式是把 fd 设置成非阻塞:

int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

同时,每轮 select 前都要重新设置 fd_settimeval

FD_ZERO(&read_fds);
FD_SET(sock, &read_fds);

tv.tv_sec = timeout_sec;
tv.tv_usec = 0;

ret = select(sock + 1, &read_fds, NULL, NULL, &tv);

这是一个很容易踩的坑。因为 select 会修改 fd_set,有些系统还会把 timeval 改成剩余时间。如果循环里不重置,后面的判断就可能被污染,出现空转或漏读。

我的理解是,HTTP 客户端练的不只是协议文本,更是“可靠地读完不确定长度的数据”。网络编程里,读到一半、超时、对端关闭、暂时无数据都是正常情况。代码必须把这些状态写清楚,程序才不会在真实网络环境里表现得忽好忽坏。

学习链接: https://github.com/0voice

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值