我用 C 构建了一个小型 HTTP 服务器,以了解网络的实际工作原理

发布: (2026年1月11日 GMT+8 04:27)
4 min read
原文: Dev.to

Source: Dev.to

我想要的(以及我没有的)

这个项目从来不是为了构建一个生产服务器。
我并不是想和 Nginx 竞争。我只想得到一些最基本的问题的答案:

  • HTTP 请求到底长什么样?
  • 服务器如何决定返回哪个文件?
  • accept() 与浏览器渲染页面之间发生了什么?

仅此而已。

这个服务器实际能做的事

下面是它的确切范围——不多也不少:

  • 监听 8080 端口
  • 只处理 GET 请求
  • 提供静态文件(HTML、文本、图片)
  • 使用 POSIX 线程(一个客户端 = 一个线程)
  • 发送正确的 Content-Type 头部
  • 当文件不存在时返回普通的 404

小到可以一次性看完。

代码结构(因为单文件是噩梦)

我不想把所有东西都塞进 main.c,所以把代码拆分了:

  • main.c → 套接字初始化、bindlisten、accept 循环
  • request.c → 读取请求并处理客户端
  • http.c → 构建 HTTP 响应
  • utils.c → MIME 类型和 URL 解码

没有花哨的东西。只是职责分离。这让调试轻松了很多。

第一次 “哦,糟了” 的时刻

当我第一次把原始 HTTP 请求打印到终端时,终于恍然大悟:

GET /index.html HTTP/1.1
Host: localhost:8080

就是这样。没有对象。此时,HTTP 不再神秘。

不纠结的解析

我没有写完整的 HTTP 解析器。只用了一个小正则从 GET / 后面提取文件路径,解码后继续处理。

它脆弱吗? 是的。过度设计会扼杀这个项目的初衷。

手动构造响应

另一个被低估的认识:一个合法的响应实际上就是:

status line
headers
(empty line)
body

一旦把这个流程硬编码进去,一切都变得清晰。当文件未找到时,我返回一个简单的 404

MIME 类型:小细节,大教训

有一次,HTML 能正常显示……但图片不行。原来浏览器非常在意 Content-Type。于是我加入了一个小的扩展名 → MIME 映射:

  • .htmltext/html
  • .jpgimage/jpeg
  • .pngimage/png

突然间,一切都渲染正确了。这个小 bug 教会我的东西比十几篇博客都多。

为什么选 C?

因为 C 不会隐藏任何东西。如果能运行,那一定是你让它运行的。用 C 来实现这个项目迫使我:

  • 正确理解套接字
  • 小心管理内存
  • 直接阅读系统调用,而不是抽象层

说实话,这让我对现代框架更加感激。

这个服务器擅长的事(故意不擅长的)

说清楚——这个服务器 处理:

  • POST 请求
  • HTTPS
  • Keep‑alive 连接
  • 安全性
  • 真实流量

这没关系。这是一个学习项目,而不是创业产品。

最后感想

如果你从未在没有框架的情况下自己写过服务器,我建议你尝试一次。不是为了取代你的工具,也不必一定要完成。对我而言,这个项目消除了很多关于 HTTP 的迷雾。仅此一点,就已经很值得。

感谢阅读 🙌

Back to Blog

相关文章

阅读更多 »

TCP 不知道什么是消息

当我在使用 HTTP 时,我心里一直有一个潜在的假设: > 如果我发送一个东西,另一端就会收到一个东西。 这看起来很显然。Al…

待办应用

引言 在完成我的第一个以逻辑为中心的项目 Counters 后,我想在复杂度上迈出下一步——不是通过改进 UI,而是通过...