花拾录
← 返回知识库

用 C 写一个极简 HTTP 服务器:从 socket 到解析请求行

后端开发AI2026/09/281 阅读0 评论

用 C 写一个极简 HTTP 服务器:从 socket 到解析请求行

HTTP 服务器本质上是一个监听 TCP 端口的程序,接收连接、读取请求、返回响应。本文用 C 语言实现一个仅支持 GET 请求的极简服务器,重点展示从创建 socket 到解析请求行的完整过程。

1. 创建监听 socket

服务器需要创建一个 TCP socket 并绑定到指定端口,然后开始监听:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUF_SIZE 4096

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) { perror("socket"); exit(1); }

    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind"); exit(1);
    }
    if (listen(server_fd, 10) < 0) { perror("listen"); exit(1); }
    printf("Listening on port %d\n", PORT);

关键点:SO_REUSEADDR 允许地址重用,避免重启时 bind 失败;INADDR_ANY 表示监听所有网卡。

2. 接受连接并读取请求

使用 accept 接受客户端连接,然后 recv 读取数据:

    while (1) {
        int client_fd = accept(server_fd, NULL, NULL);
        if (client_fd < 0) { perror("accept"); continue; }

        char buf[BUF_SIZE];
        ssize_t n = recv(client_fd, buf, BUF_SIZE - 1, 0);
        if (n <= 0) { close(client_fd); continue; }
        buf[n] = '\0';

注意:recv 一次不一定读到完整请求,生产环境需循环读取直到遇到 \r\n\r\n。

3. 解析请求行

HTTP 请求的第一行格式为:METHOD PATH VERSION,例如 GET /index.html HTTP/1.1。用 sscanf 或 strtok 解析:

        char method[8], path[256], version[16];
        if (sscanf(buf, "%7s %255s %15s", method, path, version) != 3) {
            close(client_fd); continue;
        }
        printf("Method: %s, Path: %s, Version: %s\n", method, path, version);

解析后应校验方法是否为 GET,路径是否合法(如不以 .. 开头),防止路径穿越。

4. 构造并发送响应

一个最简单的响应包含状态行、Content-Length 和正文:

        const char *body = "Hello, World!";
        char response[512];
        int len = snprintf(response, sizeof(response),
            "HTTP/1.1 200 OK\r\n"
            "Content-Type: text/plain\r\n"
            "Content-Length: %zu\r\n"
            "Connection: close\r\n"
            "\r\n"
            "%s", strlen(body), body);
        send(client_fd, response, len, 0);
        close(client_fd);
    }
    close(server_fd);
    return 0;
}

5. 编译与测试

保存为 server.c,编译:

gcc -Wall -o server server.c
./server

另开终端测试:

curl http://localhost:8080/
# 输出 Hello, World!

6. 注意事项

  • 上述代码仅用于学习,未处理并发(可用 fork 或 pthread 改进)。
  • 未做错误处理与超时控制,实际项目应添加。
  • 解析请求行时务必限制缓冲区长度,避免溢出。

通过这个极简实现,可以清晰理解 HTTP 服务器从 socket 到请求解析的基本流程。

评论(0)

  • 还没有评论,来抢沙发~

相关文章