CRLF是否会导致socket read()返回?C++ HTTP服务器开发疑问
结论:这是完全符合预期的正常行为,不属于代码或系统异常
触发该现象的两层核心原因
- Telnet客户端的发送逻辑:你使用的telnet在默认交互模式下,会把终端的回车操作转换为
\r\n两个字节的序列,在你按下回车的瞬间就把整行输入连同换行符一起通过TCP连接发送出去,不会等到后续输入内容攒够一定长度再批量发送。 - 阻塞式socket
read()/recv()的返回规则:阻塞socket的读操作没有内置“等固定长度、等特定结束标记才返回”的逻辑,它的返回触发条件只有三个:- 内核socket接收缓冲区中存在至少1字节的可读数据,此时会把缓冲区当前能提供的所有数据拷贝到用户态传入的缓冲区,返回实际拷贝的字节数
- 对端正常关闭连接(发送FIN包),此时
read()返回0 - 连接发生异常错误,此时
read()返回-1并设置对应的错误码
为什么会产生“读到\r\n就返回”的感知
这个现象是两个独立行为叠加的结果,并不是read()函数识别到\r\n换行符就主动中断返回:
因为telnet每次敲完回车就立刻把对应大小的数据包发出去,服务器内核的接收缓冲区在你调用read()的时间点,刚好已经收完了这整段带\r\n的输入内容,所以read()自然就带着这些数据返回了。
如果你换一个自定义客户端,攒够1KB的内容再一次性发送,会发现read()会直接把这1KB内容全部读完返回,根本不会因为内容中间包含\r\n就提前中断。
编写HTTP服务器时的对应注意事项
正因为阻塞式read()的这种返回特性,你绝对不能指望单次read()调用就能拿到完整的HTTP请求。必须自行维护持续的接收缓冲区,逐次把read()读到的内容拼接进去,再根据HTTP协议的规则(比如头部以连续的\r\n\r\n作为结束标记、结合请求头中的Content-Length值判断请求体长度),判断当前累计收到的内容是否构成了完整的可处理请求,再开展后续的解析逻辑。
内容的提问来源于stack exchange,提问作者Grim
相关产品推荐
相关产品推荐

