C++ WebServer / 09
非阻塞写:EAGAIN 与 EPOLLOUT
非阻塞 socket 下 write 不保证一次写完。项目中针对需要连续写三次以上的情况做过修复,说明写入进度必须被显式保存。
这一阶段要解决什么问题
响应头和文件内容可能大于内核发送缓冲区剩余空间。非阻塞 write/writev 写不完时会返回 EAGAIN,服务器必须等 EPOLLOUT 后继续写。
原来的实现有什么缺陷
如果假设一次 write 就完成响应,大文件或高并发下会丢数据。提交记录里有 “Write continuedly when need to write 3 and more times” 的修复,正说明写入进度不能只处理一两次。
我是怎么改的
Connection 保存已经发送的字节数和剩余待发送数据。process_write 根据 writev 返回值推进进度;遇到 EAGAIN 就注册 EPOLLOUT,等待 fd 再次可写。
核心代码 / 关键逻辑
while (bytes_sent < bytes_to_send) {
ssize_t n = write(fd, data + bytes_sent, bytes_to_send - bytes_sent);
if (n > 0) bytes_sent += n;
else if (errno == EAGAIN) {
modify_epoll(fd, EPOLLOUT);
return WRITE_AGAIN;
} else {
return WRITE_ERROR;
}
}
return WRITE_DONE;
踩坑记录
非阻塞写的坑在于“测试小响应时永远正常”。只有响应变大、并发变高或发送缓冲区紧张时,才会暴露一次 write 写不完的问题。