1. 网络编程基础与TCP/IP协议栈
在开始深入探讨TCP并发服务器实现之前,我们需要先理解几个核心的网络编程概念。TCP/IP协议栈是现代互联网通信的基础,它由四层组成:应用层、传输层、网络层和链路层。我们主要关注传输层的TCP协议,因为它提供了可靠的、面向连接的通信服务。
TCP协议有三个重要特性:
- 面向连接:通信前需要建立连接(三次握手)
- 可靠传输:通过确认机制和重传机制保证数据不丢失
- 流式传输:数据被视为字节流,没有固定边界
在Linux系统中,网络编程主要通过套接字(socket)接口实现。套接字是通信端点的抽象,类似于文件描述符。我们常用的套接字类型包括:
- SOCK_STREAM:提供面向连接的TCP服务
- SOCK_DGRAM:提供无连接的UDP服务
- SOCK_RAW:原始套接字,可以直接操作网络层
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 单进程TCP服务器实现
让我们先看一个最基本的TCP服务器实现,这是理解并发服务器的基础。以下代码展示了一个简单的单进程TCP服务器:
c复制#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define PORT 50000
#define BUFFER_SIZE 1024
int main() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
struct sockaddr_in server_addr = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = INADDR_ANY
};
if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("bind failed");
close(listen_fd);
exit(EXIT_FAILURE);
}
if (listen(listen_fd, 5) < 0) {
perror("listen failed");
close(listen_fd);
exit(EXIT_FAILURE);
}
printf("Server listening on port %d\n", PORT);
while (1) {
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int conn_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_len);
if (conn_fd < 0) {
perror("accept failed");
continue;
}
char buffer[BUFFER_SIZE];
ssize_t bytes_read = read(conn_fd, buffer, BUFFER_SIZE-1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Received: %s\n", buffer);
write(conn_fd, buffer, bytes_read);
}
close(conn_fd);
}
close(listen_fd);
return 0;
}
这个简单实现有几个明显问题:
- 一次只能处理一个客户端连接
- 在服务一个客户端时,其他客户端会被阻塞
- 没有错误处理和资源清理的完善机制
3. 多进程并发服务器实现
为了解决单进程服务器的限制,我们可以使用多进程模型。父进程负责接受新连接,子进程处理具体客户端通信。以下是关键实现要点:
3.1 进程模型设计
c复制while (1) {
int conn_fd = accept(listen_fd, ...);
if (conn_fd < 0) {
perror("accept");
continue;
}
pid_t pid = fork();
if (pid == 0) { // 子进程
close(listen_fd); // 关闭不需要的监听套接字
handle_client(conn_fd);
exit(0);
} else if (pid > 0) { // 父进程
close(conn_fd); // 关闭不需要的连接套接字
} else {
perror("fork");
close(conn_fd);
}
}
3.2 僵尸进程处理
子进程退出
