1. 项目概述
去年我分享了一个简易版HTTP服务器的实现,没想到收到了不少读者的反馈。很多朋友希望我能继续深入,实现一个完整的HTTP/1.1服务器。经过几个月的开发和测试,今天终于可以分享这个更完善的版本了。
这个项目不是简单的玩具代码,而是一个符合RFC标准的HTTP/1.1服务器实现,支持持久连接、分块传输、内容协商等核心特性。我在开发过程中踩了不少坑,特别是在处理各种边缘情况时,这些经验都会在文中详细分享。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心需求解析
2.1 HTTP/1.1协议基础
HTTP/1.1相比早期的HTTP/1.0有几个关键改进:
- 持久连接(Keep-Alive):默认保持TCP连接不立即关闭
- 分块传输编码(Chunked Transfer Encoding):支持流式传输
- 内容协商(Content Negotiation):客户端和服务器协商最佳资源表示
- 缓存控制(Cache Control):更精细的缓存管理机制
2.2 服务器功能规划
我们的服务器需要实现以下核心功能:
- 基本请求解析和响应生成
- 支持GET、HEAD、POST方法
- 处理持久连接
- 实现分块传输
- 支持内容协商
- 基本的错误处理
3. 技术实现细节
3.1 网络层实现
我们使用C++标准库中的socket API进行网络通信:
cpp复制int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 设置SO_REUSEADDR选项避免端口占用问题
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
注意:在开发过程中发现,如果不设置SO_REUSEADDR选项,服务器重启时可能会遇到"Address already in use"错误。这是因为TCP连接处于TIME_WAIT状态。
3.2 HTTP请求解析
HTTP请求解析是服务器的核心功能之一。我们需要处理各种可能的请求格式:
cpp复制class HttpRequest {
public:
std::string method;
std::string uri;
std::string version;
std::unordered_map<std::string, std::string> headers;
std::string body;
void parse(const std::string& raw_request) {
std::istringstream request_stream(raw_request);
std::string line;
// 解析请求行
std::getline(request_stream, line);
std::istringstream line_stream(line);
line_stream >> method >> uri >> version;
// 解析头部
while (std::getline(request_stream, line) && line != "\r") {
size_t colon_pos = line.find(':');
if (colon_pos != std::string::npos) {
std::string key = line.substr(0, colon_pos);
std::string value = line.substr(colon_pos + 2); // 跳过冒号和空格
headers[key] = value;
}
}
// 解析正文
if (headers.count("Content-Le
