为什么要从“最简单”的 TCP 服务端开始?
很多人一学网络编程就想直接上:
- epoll
- Reactor
- 高并发模型
但如果你没搞清楚这些最基础的问题:
- socket 怎么创建?
- accept 到底返回什么?
- read / write 怎么完成一次请求响应?
- 一个连接什么时候结束?
那你后面学到的东西,大概率是:
❌ API 会用
❌ 模型会背
❌ 但自己写不出来
所以这一篇的目标非常明确:
✅ 搭一个“能跑通”的最小 TCP 服务端
✅ 搞清楚一次请求的完整链路
一、网络服务端到底在干什么?
先给你一个最核心的流程图:
客户端(浏览器 / curl) │ ▼ 服务器监听端口 │ accept() │ 建立连接 │ read() │ 处理请求 │ write() │ 关闭连接👉 这就是所有服务端的最小本质。
二、核心 API 一次讲清
1️⃣ socket —— 创建“通信端点”
int server_fd = socket(AF_INET, SOCK_STREAM, 0);含义:
AF_INET→ IPv4SOCK_STREAM→ TCP
👉 类比 Java:
new ServerSocket(...)2️⃣ bind —— 绑定端口
bind(server_fd, (sockaddr*)&addr, sizeof(addr));👉 作用:
把 socket 绑定到某个 IP + 端口
3️⃣ listen —— 开始监听
listen(server_fd, 10);👉 含义:
- 允许客户端连接
- 10 = 等待队列大小
4️⃣ accept —— 接收连接(最重要)
int client_fd = accept(server_fd, ...);👉 返回:
一个新的 socket(client_fd)
⚠️ 非常关键:
server_fd:只负责监听client_fd:用于通信
5️⃣ read / write —— 数据收发
read(client_fd, buffer, size); write(client_fd, response, len);👉 一次请求 = 一次读写过程
📌 三、完整最小 TCP 服务端(可运行)
👉可以直接复制运行(Linux / macOS)
#include <iostream> #include <cstring> #include <unistd.h> #include <arpa/inet.h> int main() { // 1. 创建 socket int server_fd = socket(AF_INET, SOCK_STREAM, 0); // 2. 配置地址 sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons(8080); addr.sin_addr.s_addr = INADDR_ANY; // 3. 绑定 bind(server_fd, (sockaddr*)&addr, sizeof(addr)); // 4. 监听 listen(server_fd, 10); std::cout << "Server listening on port 8080..." << std::endl; while (true) { // 5. 接收连接 sockaddr_in client_addr{}; socklen_t len = sizeof(client_addr); int client_fd = accept(server_fd, (sockaddr*)&client_addr, &len); std::cout << "Client connected" << std::endl; // 6. 读取请求 char buffer[1024] = {0}; read(client_fd, buffer, sizeof(buffer)); std::cout << "Received:\n" << buffer << std::endl; // 7. 返回响应 const char* response = "HTTP/1.1 200 OK\r\n" "Content-Length: 13\r\n" "\r\n" "Hello, World!"; write(client_fd, response, strlen(response)); // 8. 关闭连接 close(client_fd); } return 0; }四、怎么测试?
运行程序:
g++ server.cpp -o server ./server然后浏览器访问:
http://localhost:8080
或者:
curl localhost:8080
👉 输出:Hello, World!
五、这段代码到底做了什么?
一次完整请求流程
客户端发起请求 ↓ accept 返回 client_fd ↓ read 读取请求数据 ↓ write 返回响应 ↓ close 关闭连接核心理解
👉 服务端的本质就是:
一个无限循环 + 接收连接 + 处理连接
六、Java 对比
Java 写法
ServerSocket server = new ServerSocket(8080); while (true) { Socket client = server.accept(); InputStream in = client.getInputStream(); OutputStream out = client.getOutputStream(); // read / write }对应关系
| Java | C++ |
|---|---|
| ServerSocket | socket + bind + listen |
| accept() | accept() |
| InputStream | read() |
| OutputStream | write() |
👉 本质完全一样。
七、这个版本的问题(非常关键)
这个服务端:
❌ 只能同时处理一个连接
原因:accept → read → write → close
是串行执行
问题表现:
如果一个请求很慢:
👉 后面的连接全部阻塞
八、这就是下一步要解决的问题
👉 如何让:
- accept 继续接收连接
- 同时多个请求能并发处理
下一篇预告
《C++ 高性能网络服务骨架(二)》—— 线程池接入:accept 与 worker 分离
下一步模型:
主线程: accept 新连接 ↓ 把连接丢给线程池 ↓ worker 线程处理请求你会真正理解:
- 线程池在服务端的作用
- accept 和处理逻辑为什么要分离
- 并发模型是怎么搭起来的
总结(建议记住)
一个服务端,本质就是:
循环 accept + 处理连接
你现在已经完成了:
✔ 第一步:搞清楚“连接是怎么来的”
接下来要做的是:
让多个连接同时被处理(并发化)