【网络编程】服务器模型(一):循环服务器模型和并发服务器模型(多进程)

服务器模型

网络编程 中,服务器通常需要处理多个客户端的请求。根据服务器处理多个客户端的方式,可以分为 循环服务器模型并发服务器模型

在网络程序里面,通常都是一个服务器处理多个客户机。为了处理多个客户机的请求, 服务器端的程序有不同的处理方式。目前最常用的服务器模型. 
循环服务器:循环服务器在同一个时刻只能响应一个客户端的请求 
并发服务器:并发服务器在同一个时刻可以响应多个客户端的请求 

📌 1. 循环服务器模型

目前最常用的服务器模型. 
1》循环服务器:循环服务器在同一个时刻只能响应一个客户端的请求 TCP服务器端运行后等待客户端的连接请求。TCP服务器接受一个客户端的连接后开始处理,完成了客户的所有请求后断开连接。 TCP循环服务器一次只能处理一个客户端的请求。只有在当前客户的所有请求都完成后,服务器才能处理下一个客户的连接/服务请求。如果某个客户端一直占用服务器资源,那么其它的客户端都不能被处理。TCP服务器一般很少采用循环服务器模型
特点:
  • 服务器 一次只能处理一个客户端 的请求,其他客户端必须等待。
  • 如果某个客户端 长时间占用服务器,其他客户端无法访问。
  • 适用于 简单应用单客户端环境,但 不适合大并发场景

✅ 一、TCP 循环服务器

工作流程:

  1. 创建套接字(socket()

  2. 绑定 IP 和端口(bind()

  3. 监听连接(listen()

  4. 循环等待客户端连接(accept()

  5. 接收请求并处理(recv() -> process() -> send()

  6. 关闭连接(close()

  7. 处理下一个客户端

     socket(...);bind(...);listen(...);while(1){accept(...);while(1){recv(...);process(...);send(...);}close(...);}
    

TCP 循环服务器代码示例(C 语言)

代码功能
  • 服务器监听 指定端口
  • 单客户端模式:一次只能处理一个客户端
  • 客户端连接后可以发送消息,服务器 回显(echo) 该消息
  • 服务器处理完 当前客户端 后,才能接受下一个客户端连接

C语言实现:代码可执行(每个环节都标记上了重点,每一步流程根据上述编程思路实现,依次执行代码,已经过测试,代码可直接使用)

#include <stdio.h>      // 标准输入输出
#include <stdlib.h>     // exit()、malloc()等
#include <string.h>     // 字符串操作
#include <unistd.h>     // read(), write(), close()
#include <arpa/inet.h>  // sockaddr_in, inet_addr()
#include <sys/socket.h> // 套接字 API
#include <netinet/in.h> // sockaddr_in 结构体#define PORT 8080     // 服务器监听端口
#define BUFFER_SIZE 1024  // 缓冲区大小int main() {int server_fd, client_fd;  // 服务器和客户端的 socket 文件描述符struct sockaddr_in server_addr, client_addr; // 服务器和客户端的地址结构socklen_t addr_len = sizeof(client_addr); // 地址结构大小char buffer[BUFFER_SIZE]; // 消息缓冲区int bytes_read;// 1️⃣ 创建服务器套接字 (socket)server_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd == -1) {perror("Socket 创建失败");exit(EXIT_FAILURE);}// 2️⃣ 绑定服务器地址和端口 (bind)server_addr.sin_family = AF_INET; // 使用 IPv4server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IPserver_addr.sin_port = htons(PORT); // 设置端口(网络字节序)if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("绑定失败");close(server_fd);exit(EXIT_FAILURE);}// 3️⃣ 监听客户端连接 (listen)if (listen(server_fd, 5) < 0) { // 最多允许 5 个客户端排队等待perror("监听失败");close(server_fd);exit(EXIT_FAILURE);}printf("⚡ TCP 服务器已启动,监听端口 %d...\n", PORT);while (1) { // 无限循环,处理客户端请求printf("\n🔄 等待客户端连接...\n");// 4️⃣ 接受客户端连接 (accept)client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addr_len);if (client_fd < 0) {perror("接受客户端连接失败");continue; // 继续等待下一个客户端}printf("✅ 客户端连接成功!IP: %s, 端口: %d\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));// 5️⃣ 处理客户端请求while (1) {memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区bytes_read = read(client_fd, buffer, BUFFER_SIZE);if (bytes_read <= 0) {printf("❌ 客户端断开连接\n");break; // 退出该客户端处理}printf("📩 收到消息: %s\n", buffer);// 6️⃣ 发送回显消息 (echo)write(client_fd, buffer, bytes_read);}// 7️⃣ 关闭客户端连接close(client_fd);}// 8️⃣ 关闭服务器(通常不会执行到这里)close(server_fd);return 0;
}

代码运行步骤:

  1. 编译:(假设文件名为 tcp_server.c
gcc tcp_server.c -o tcp_server
  1. 运行服务器
./tcp_server

输出示例:

TCP 服务器已启动,监听端口 8080...
等待客户端连接...

✅ 连接测试:

方式 1:使用 telnet 连接
telnet 127.0.0.1 8080
# 输入消息后按 Enter,服务器会返回相同的消息。
方式 2:使用 nc(Netcat)测试
nc 127.0.0.1 8080

输入内容,服务器会回显,如:

Hello Server
Hello Server  # 服务器返回相同内容

该代码是一个最基本的 TCP 循环服务器,适用于简单的 回显服务器单客户端应用。建议进一步优化为多线程或多进程服务器以支持并发(下面会介绍):代码优化后可新增内容:

  1. 多线程并发处理:目前 一次只能处理一个客户端,可以使用 多线程 让服务器同时处理多个客户端。
  2. 日志记录:服务器可以使用 syslog() 或文件写入方式,记录 客户端连接信息。
  3. 超时处理:服务器可以设置 setsockopt() 来 限制客户端连接时间,防止长时间占用资源。

✅ 二、UDP 循环服务器

UDP 是 无连接的协议,服务器无需 accept(),直接 接收客户端数据回传响应

工作流程:

  1. 创建套接字(socket())-- socket(AF_INET, SOCK_DGRAM, 0) 创建 UDP 服务器
  2. 绑定 IP 和端口(bind())-- 监听 8080 端口
  3. 循环接收客户端数据(recvfrom())-- 等待客户端消息
  4. 处理数据(process()
  5. 返回响应数据(sendto())-- 把数据发回客户端
  6. 关闭服务器(close())-- 释放资源
代码功能
  • 监听指定端口(8080)
  • 接收客户端消息
  • 回显(Echo)消息
  • 循环处理多个客户端请求
  • 无需建立连接,支持并发
#include <stdio.h>      // 标准输入输出
#include <stdlib.h>     // exit()、malloc()等
#include <string.h>     // 字符串操作
#include <unistd.h>     // read(), write(), close()
#include <arpa/inet.h>  // sockaddr_in, inet_addr()
#include <sys/socket.h> // 套接字 API
#include <netinet/in.h> // sockaddr_in 结构体#define PORT 8080        // 服务器监听端口
#define BUFFER_SIZE 1024 // 缓冲区大小int main() {int server_fd;  // 服务器 socket 文件描述符struct sockaddr_in server_addr, client_addr; // 服务器 & 客户端地址结构socklen_t addr_len = sizeof(client_addr); // 地址结构大小char buffer[BUFFER_SIZE]; // 消息缓冲区int bytes_read;// 1️⃣ 创建 UDP 套接字 (socket)server_fd = socket(AF_INET, SOCK_DGRAM, 0);if (server_fd == -1) {perror("❌ Socket 创建失败");exit(EXIT_FAILURE);}// 2️⃣ 绑定服务器地址和端口 (bind)server_addr.sin_family = AF_INET; // IPv4server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IPserver_addr.sin_port = htons(PORT); // 设置端口(网络字节序)if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("❌ 绑定失败");close(server_fd);exit(EXIT_FAILURE);}printf("UDP 服务器已启动,监听端口 %d...\n", PORT);while (1) { // 无限循环,处理客户端请求memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区// 3️⃣ 接收客户端消息 (recvfrom)bytes_read = recvfrom(server_fd, buffer, BUFFER_SIZE, 0,(struct sockaddr*)&client_addr, &addr_len);if (bytes_read < 0) {perror("❌ 接收数据失败");continue; // 继续等待下一个请求}printf("📩 收到来自 %s:%d 的消息: %s\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buffer);// 4️⃣ 发送回显消息 (sendto)sendto(server_fd, buffer, bytes_read, 0,(struct sockaddr*)&client_addr, addr_len);}// 5️⃣ 关闭服务器(通常不会执行到这里)close(server_fd);return 0;
}

代码运行步骤:

  1. 编译:(假设文件名为 udp_server.c
gcc udp_server.c -o udp_server
  1. 运行服务器
./udp_server

输出示例:

UDP 服务器已启动,监听端口 8080...

✅ 连接测试:

方式 1:使用 nc(Netcat)

启动服务器

./udp_server

运行客户端发送消息

echo "Hello Server" | nc -u 127.0.0.1 8080

服务器输出

📩 收到来自 127.0.0.1:xxxxx 的消息: Hello Server
# (xxxxx 为随机端口)
方式 2:使用 Python UDP 客户端

运行服务器

./udp_server

运行 Python 代码

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ("127.0.0.1", 8080)message = "Hello from Python UDP Client"
client.sendto(message.encode(), server_address)data, _ = client.recvfrom(1024)
print("服务器回显:", data.decode())client.close()

服务器输出

收到来自 127.0.0.1:xxxxx 的消息: Hello from Python UDP Client

客户端输出

服务器回显: Hello from Python UDP Client
✅ 代码优化
  1. 非阻塞模式
    • 使用 fcntl() 设置 非阻塞模式,防止 recvfrom() 阻塞:
#include <fcntl.h>
fcntl(server_fd, F_SETFL, O_NONBLOCK);
  1. 多线程处理
    • 每个客户端请求可以在 新线程 中处理,防止阻塞主进程:
pthread_create(&thread_id, NULL, handle_client, (void*)&client_addr);
  1. 超时设置
    • 通过 setsockopt() 设置 超时时间,避免客户端长期占用:
struct timeval timeout = {5, 0}; // 5 秒超时
setsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

📌 2. 并发服务器模型

由于只有在当前客户的所有请求都完成后,服务器才能处理下一个客户的连接/服务请求。如果某个客户端一直占用服务器资源,那么其它的客户端都不能被处理。所以TCP服务器一般很少采用循环服务器模型。

为了弥补TCP循环服务器的缺陷,人们又设计了并发服务器的模型

并发服务器的设计思想是服务器接受客户端的连接请求后创建子进程来为客户端服务; TCP并发服务器可以避免TCP循环服务器中客户端独占服务器的情况。为了响应客户机的请求,服务器要创建子进程来处理。如果有多个客户端的话,服务器端需要创建多个子进程。过多的子进程会影响服务器端的运行效率。

特点:
  • 同时处理多个客户端,不会因为一个客户端的请求而阻塞其他客户端。
  • 适用于高并发场景,如 Web 服务器、数据库服务器。
  • 服务器通过 多进程多线程或 I/O 复用 实现并发。

✅ TCP 并发服务器(多进程)

工作流程:

  1. 创建套接字(socket()

  2. 绑定 IP 和端口(bind()

  3. 监听连接(listen()

  4. 接受客户端连接(accept()

  5. 创建子进程(fork())处理客户端请求

  6. 父进程继续监听新的客户端连接

  7. 子进程处理完请求后关闭连接

     socket(...);bind(...);listen(...);while(1){accept(...);if (fork() = = 0) { {while(1) { recv(...); process(...); send(...); }close(...); exit(...); }close(...);}
    

完整代码:

#include <stdio.h>      // 标准输入输出
#include <stdlib.h>     // exit()、malloc() 等
#include <string.h>     // 字符串操作
#include <unistd.h>     // read(), write(), close()
#include <arpa/inet.h>  // sockaddr_in, inet_addr()
#include <sys/socket.h> // 套接字 API
#include <netinet/in.h> // sockaddr_in 结构体
#include <sys/types.h>  // pid_t
#include <sys/wait.h>   // waitpid()
#include <signal.h>     // 信号处理#define PORT 8080        // 服务器监听端口
#define BUFFER_SIZE 1024 // 缓冲区大小// 处理 SIGCHLD 信号,避免僵尸进程
void sigchld_handler(int signo) {while (waitpid(-1, NULL, WNOHANG) > 0); // 处理所有已终止的子进程
}int main() {int server_fd, client_fd;  // 服务器和客户端的 socket 文件描述符struct sockaddr_in server_addr, client_addr; // 服务器 & 客户端地址结构socklen_t addr_len = sizeof(client_addr); // 地址结构大小char buffer[BUFFER_SIZE]; // 消息缓冲区int bytes_read;pid_t child_pid;// 1️⃣ 处理 SIGCHLD 以清理僵尸进程signal(SIGCHLD, sigchld_handler);// 2️⃣ 创建服务器套接字 (socket)server_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd == -1) {perror("❌ Socket 创建失败");exit(EXIT_FAILURE);}// 3️⃣ 绑定服务器地址和端口 (bind)server_addr.sin_family = AF_INET; // IPv4server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IPserver_addr.sin_port = htons(PORT); // 设置端口(网络字节序)if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("❌ 绑定失败");close(server_fd);exit(EXIT_FAILURE);}// 4️⃣ 监听客户端连接 (listen)if (listen(server_fd, 5) < 0) { // 最多允许 5 个客户端排队等待perror("❌ 监听失败");close(server_fd);exit(EXIT_FAILURE);}printf("⚡ TCP 并发服务器已启动,监听端口 %d...\n", PORT);while (1) { // 无限循环,处理客户端请求printf("\n 等待客户端连接...\n");// 5️⃣ 接受客户端连接 (accept)client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addr_len);if (client_fd < 0) {perror("❌ 接受客户端连接失败");continue; // 继续等待下一个客户端}printf("✅ 客户端连接成功!IP: %s, 端口: %d\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));// 6️⃣ 创建子进程处理客户端child_pid = fork();if (child_pid == 0) { // 子进程close(server_fd); // 关闭监听 socket(子进程不需要)while (1) {memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区bytes_read = read(client_fd, buffer, BUFFER_SIZE);if (bytes_read <= 0) {printf("❌ 客户端断开连接\n");break; // 退出该客户端处理}printf("📩 收到来自 %s:%d 的消息: %s\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buffer);// 7️⃣ 发送回显消息 (echo)write(client_fd, buffer, bytes_read);}// 8️⃣ 关闭客户端连接并退出子进程close(client_fd);exit(0);}// 父进程关闭已连接的 socket(由子进程处理)close(client_fd);}// 9️⃣ 关闭服务器(通常不会执行到这里)close(server_fd);return 0;
}
代码功能:监听指定端口(8080)支持多个客户端同时连接每个客户端连接后,服务器创建子进程进行处理回显(Echo)客户端发送的消息支持 SIGCHLD 处理僵尸进程
✅ 代码运行步骤
  1. 编译:(假设文件名为 tcp_server_fork.c
gcc tcp_server_fork.c -o tcp_server_fork
  1. 运行服务器
./tcp_server_fork

输出示例:

TCP 并发服务器已启动,监听端口 8080...
等待客户端连接...
✅ 连接测试

方式 1:使用 telnet 连接

telnet 127.0.0.1 8080
# 输入消息后按 Enter,服务器会返回相同的消息。

方式 2:使用 nc(Netcat)测试

nc 127.0.0.1 8080

输入内容,服务器会回显,如:

Hello Server
Hello Server  # 服务器返回相同内容

详细步骤:

1. 处理 SIGCHLD   --	signal(SIGCHLD, handler) --	避免子进程变成僵尸进程
2. 创建 TCP 套接字 --	socket()                 -- 创建服务器 socket
3. 绑定 IP 和端口  --	bind()	                 -- 监听 8080 端口
4. 监听连接       -- listen()	             -- 允许最多 5 个客户端排队
5. 等待客户端连接  --	accept()	             -- 接受一个客户端连接
6. 创建子进程     --	fork()	                 -- 让子进程处理客户端请求
7. 处理客户端请求  --	read()	                 -- 读取客户端发送的数据
8. 发送回显数据    --	write()	                 -- 把数据发回客户端
9. 关闭连接       --	close()	                 -- 释放资源

代码可优化部分:

多线程替代 fork()fork() 开销较大,可用 pthread(多线程) 替代,提高性能。
使用 I/O 复用select() / epoll() 可提高并发性能,避免大量进程消耗资源。
日志记录服务器可以使用 syslog() 或 文件写入 方式记录 客户端连接信息。
超时处理服务器可以设置 setsockopt() 限制客户端连接时间:
struct timeval timeout = {5, 0}; // 5 秒超时
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));

备注:

  • fork() 创建子进程,每个客户端由单独的进程处理。
  • 使用 signal(SIGCHLD, handler) 处理僵尸进程。
  • 处理僵尸进程的 handler() 函数:
void handler(int signo) {while (waitpid(-1, NULL, WNOHANG) > 0);
}

该代码是一个最基本的 TCP 并发服务器(多进程),适用于 中等并发负载。适合 fork() 并发模型,这里一般建议进一步优化为 epoll + 线程池高效服务器(后续会继续详细介绍)。

✅ UDP 并发服务器(多进程)

UDP 是 无连接 的协议,服务器不需要 accept(),可以直接 接收多个客户端的数据回传响应
由于 UDP 服务器在处理某个客户端请求时可能会 阻塞,我们可以使用 多进程 方式,让每个请求都由 子进程 处理,实现并发。

完整代码
#include <stdio.h>      // 标准输入输出
#include <stdlib.h>     // exit()、malloc() 等
#include <string.h>     // 字符串操作
#include <unistd.h>     // read(), write(), close()
#include <arpa/inet.h>  // sockaddr_in, inet_addr()
#include <sys/socket.h> // 套接字 API
#include <netinet/in.h> // sockaddr_in 结构体
#include <sys/types.h>  // pid_t
#include <sys/wait.h>   // waitpid()
#include <signal.h>     // 信号处理#define PORT 8080        // 服务器监听端口
#define BUFFER_SIZE 1024 // 缓冲区大小// 处理 SIGCHLD 信号,避免僵尸进程
void sigchld_handler(int signo) {while (waitpid(-1, NULL, WNOHANG) > 0); // 清理所有僵尸进程
}int main() {int server_fd;  // 服务器 socket 文件描述符struct sockaddr_in server_addr, client_addr; // 服务器 & 客户端地址结构socklen_t addr_len = sizeof(client_addr); // 地址结构大小char buffer[BUFFER_SIZE]; // 消息缓冲区int bytes_read;pid_t child_pid;// 1️⃣ 处理 SIGCHLD 以清理僵尸进程signal(SIGCHLD, sigchld_handler);// 2️⃣ 创建 UDP 套接字 (socket)server_fd = socket(AF_INET, SOCK_DGRAM, 0);if (server_fd == -1) {perror("❌ Socket 创建失败");exit(EXIT_FAILURE);}// 3️⃣ 绑定服务器地址和端口 (bind)server_addr.sin_family = AF_INET; // IPv4server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IPserver_addr.sin_port = htons(PORT); // 设置端口(网络字节序)if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("❌ 绑定失败");close(server_fd);exit(EXIT_FAILURE);}printf("⚡ UDP 并发服务器已启动,监听端口 %d...\n", PORT);while (1) { // 无限循环,处理客户端请求memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区// 4️⃣ 接收客户端消息 (recvfrom)bytes_read = recvfrom(server_fd, buffer, BUFFER_SIZE, 0,(struct sockaddr*)&client_addr, &addr_len);if (bytes_read < 0) {perror("❌ 接收数据失败");continue; // 继续等待下一个请求}printf("📩 收到来自 %s:%d 的消息: %s\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buffer);// 5️⃣ 创建子进程处理请求child_pid = fork();if (child_pid == 0) { // 子进程// 6️⃣ 发送回显消息 (sendto)sendto(server_fd, buffer, bytes_read, 0,(struct sockaddr*)&client_addr, addr_len);printf("🔄 处理完毕,发送回显消息: %s\n", buffer);// 7️⃣ 退出子进程exit(0);}}// 8️⃣ 关闭服务器(通常不会执行到这里)close(server_fd);return 0;
}
代码功能监听指定端口(8080)接收多个客户端的消息使用 fork() 创建子进程处理每个请求回显(Echo)客户端发送的消息支持 SIGCHLD 处理僵尸进程

代码运行步骤:

  1. 编译:(假设文件名为 udp_server_fork.c
gcc udp_server_fork.c -o udp_server_fork
  1. 运行服务器
./udp_server_fork

输出示例:

UDP 并发服务器已启动,监听端口 8080...

连接测试:

方式 1:使用 nc(Netcat)

➤ 启动服务器

./udp_server_fork

➤ 运行客户端发送消息

echo "Hello Server" | nc -u 127.0.0.1 8080

➤ 服务器输出

收到来自 127.0.0.1:xxxxx 的消息: Hello Server
处理完毕,发送回显消息: Hello Server
# (xxxxx 为随机端口)
方式 2:使用 Python UDP 客户端

➤ 运行服务器

./udp_server_fork

➤ 运行 Python 代码

import socketclient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ("127.0.0.1", 8080)message = "Hello from Python UDP Client"
client.sendto(message.encode(), server_address)data, _ = client.recvfrom(1024)
print("服务器回显:", data.decode())client.close()

➤ 服务器输出

收到来自 127.0.0.1:xxxxx 的消息: Hello from Python UDP Client
处理完毕,发送回显消息: Hello from Python UDP Client

➤ 客户端输出

服务器回显: Hello from Python UDP Client
详细步骤:1. 处理 SIGCHLD   --	  signal(SIGCHLD, handler)   --	 避免子进程变成僵尸进程2. 创建 UDP 套接字 --	  socket()	                 -- 创建服务器 socket3. 绑定 IP 和端口  --   bind()	                 -- 监听 8080 端口4. 接收数据        --   recvfrom()	             -- 等待客户端消息5. 创建子进程      --   fork()	                 -- 让子进程处理客户端请求6. 发送回显数据	 --   sendto()	                 -- 把数据发回客户端7. 退出子进程	     --   exit(0)	                 -- 释放资源,防止占用内存

服务器模型对比

模型适用协议特点优缺点
循环服务器TCP / UDP单线程处理客户端简单但不支持并发
多进程并发服务器TCP / UDPfork() 创建子进程稳定,但进程开销大
多线程并发服务器TCPpthread 处理多个客户端资源消耗较低,但线程管理复杂
I/O 复用服务器select / epoll)(重点:后面详细开一篇单独详细解析TCP / UDP监听多个客户端适合高并发,但实现复杂

服务器优化

  1. 使用 I/O 复用(select() / epoll())
    • 适用于 高并发 场景,如 Web 服务器。
    • select() 适用于 小规模并发,而 epoll() 适用于 大规模并发
  2. 使用线程池
    • 线程池可以 复用线程,减少 fork() / pthread_create() 的开销。
    • 适用于 中等并发 场景,如 聊天室数据库服务器
  3. 使用异步 I/Oaio_*()
    • 适用于 超高并发 场景,如 消息队列异步 API 服务器
    • 依赖 操作系统支持(如 Linux libaio)。

循环服务器 适用于 低并发,但性能有限。多进程 / 多线程服务器 适用于 中等并发,但资源开销较大。I/O 复用(epoll)或异步 I/O 适用于高并发服务器,如 NginxRedis。如果要开发高性能服务器,推荐使用 epoll + 线程池

由于篇幅有限,多线程实现并发服务器I/O 复用(epoll)或异步 I/Oepoll + 线程池在后续的文章中会依次介绍!

以上。仅供学习与分享交流,请勿用于商业用途!转载需提前说明。

我是一个十分热爱技术的程序员,希望这篇文章能够对您有帮助,也希望认识更多热爱程序开发的小伙伴。
感谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/21881.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Python CNN和词向量的句子相似性度量

毕业设计&#xff1a;基于CNN和词向量的句子相似性度量 注意&#xff1a;因为要计算WMD距离所以需要安装依赖库pyemd 开发环境 Anaconda Pycharm 项目说明 按照老师要求复现论文(论文提出了一个新概念相似元&#xff0c;通过相似元来计算两个句子的相似度‘)&#xff0c;同…

CPU安装pytorch(别点进来)

终于&#xff01; 深度学习环境配置5——windows下的torch-cpu1.2.0环境配置_requirement怎么写torch cu-CSDN博客

Django-Vue 学习-VUE

主组件中有多个Vue组件 是指在Vue.js框架中&#xff0c;主组件是一个父组件&#xff0c;它包含了多个子组件&#xff08;Vue组件&#xff09;。这种组件嵌套的方式可以用于构建复杂的前端应用程序&#xff0c;通过拆分功能和视图&#xff0c;使代码更加模块化、可复用和易于维…

MATLAB基础学习相关知识

MATLAB安装参考&#xff1a;抖音-记录美好生活 MATLAB基础知识学习参考&#xff1a;【1小时Matlab速成教程-哔哩哔哩】 https://b23.tv/CnvHtO3 第1部分&#xff1a;变量定义和基本运算 生成矩阵&#xff1a; % 生成矩阵% 直接法% ,表示行 ;表示列 a [1,2,3;4,5,6;7,8,9];%…

TypeScript - 数据类型 - 声明变量

TypeScript 是一种强类型的 JavaScript 超集&#xff0c;它引入了静态类型检查和类型注解。基础类型是 TypeScript 中最基本的类型&#xff0c;用于定义变量的类型。 一、数据类型 常用基本类型&#xff1a;boolean 、number 、string 常用&#xff0c;都是小写 1.布尔类型&…

有序任务规划的局限性

有序任务规划的局限性&#xff08;Limitation of Ordered-Task Planning&#xff09; 1. 任务前向分解&#xff08;TFD&#xff09;的限制 TFD&#xff08;Task Forward Decomposition&#xff09;是一种 基于完全有序方法&#xff08;Totally Ordered Methods&#xff09;的任…

MATLAB学习之旅:数据插值与曲线拟合

在MATLAB的奇妙世界里,我们已经走过了一段又一段的学习旅程。从基础的语法和数据处理,到如今,我们即将踏入数据插值与曲线拟合这片充满魅力的领域。这个领域就像是魔法中的艺术创作,能够让我们根据现有的数据点,构建出更加丰富的曲线和曲面,从而更好地理解和描述数据背后…

ASP.NET Core 下载文件

本文使用 ASP .NET Core&#xff0c;适用于 .NET Core 3.1、.NET 5、.NET 6和.NET 8。 另请参阅&#xff1a; 如何在将文件发送到浏览器后自动删除该文件。 如何将文件从浏览器上传到服务器。 如何在 ASP.NET Core 应用程序中从 URL/URI 下载文件。 如果使用.NET Framework&am…

Part 3 第十二章 单元测试 Unit Testing

概述 第十二章围绕单元测试展开&#xff0c;阐述了单元测试的实践与重要性&#xff0c;通过对比其他测试类型&#xff0c;突出其特点&#xff0c;还介绍了单元测试的最佳实践、避免的反模式以及与测试替身相关的内容&#xff0c;为编写高质量单元测试提供指导。 章节概要 1…

SpringCloud-Eureka初步使用

什么是REST是一组用于规范资源在网络中转移的表现形式软件架构设计风格.简单来说就是客户端和服务器之间的一种交互形式 什么是RESTful,满足了REST风格的接口或者程序,RESTful API是其中的接口,spring中提供了RestTemplate这个类,他强制执行了REST的规范,包括使用HTTP协议的状…

Linux 高级篇 日志管理、定制自己的Linux系统、备份与恢复

一、日志管理 &#xff08;1&#xff09;基本介绍 日志文件是重要的系统信息文件&#xff0c;记录了如用户登录、系统启动、系统安全、邮件及各种服务等相关重要系统事件在安全方面&#xff0c;日志也至关重要&#xff0c;它能记录系统日常发生的各类事情&#xff0c;可用于检…

SpringMVC 请求参数接收

目录 请求 传递单个参数 基本类型参数传递 未传递参数 ?传递参数类型不匹配 传递多个参数 传递对象 后端参数重命名 传递数组 传递集合 传递JSON数据 JSON是什么 JSON的优点 传递JSON对象 获取URL中的参数 文件上传 在浏览器与程序进行交互时&#xff0c;主要…

深度学习之图像回归(一)

前言 图像回归任务主要是理解一个最简单的深度学习相关项目的结构&#xff0c;整体的思路&#xff0c;数据集的处理&#xff0c;模型的训练过程和优化处理。 因为深度学习的项目思路是差不多的&#xff0c;主要的区别是对于数据集的处理阶段&#xff0c;之后模型训练有一些小…

23. AI-大语言模型-DeepSeek简介

文章目录 前言一、DeepSeek是什么1. 简介2. 产品版本1. 类型2. 版本3. 参数规模与模型能力 3. 特征4. 三种访问方式1. 网页端和APP2. DeepSeek API 二、DeepSeek可以做什么1. 应用场景2. 文本生成1. 文本创作2. 摘要与改写3. 结构化生成 3. 自然语言理解与分析1. 语义分析2. 文…

京东cfe滑块 分析

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 headers {"accept&qu…

什么是事务?并发事务引发的问题?什么是MVCC?

文章目录 什么是事务&#xff1f;并发事务引发的问题&#xff1f;什么是MVCC&#xff1f;1.事务的四大特性2.并发事务下产生的问题&#xff1a;脏读、不可重复读、幻读3.如何应对并发事务引发的问题&#xff1f;4.什么是MVCC&#xff1f;5.可见性规则&#xff1f;参考资料 什么…

火语言RPA--Excel插入空行

【组件功能】&#xff1a;在Excel内指定的位置插入空行 配置预览 配置说明 在第n行之前 支持T或# 填写添加插入第n行之前行号。 插入n行 支持T或# 插入多少行。 Sheet页名称 支持T或# Excel表格工作簿名称。 示例 Excel插入空行 描述 在第3行之后插入3行。 配置 输…

【算法基础】--前缀和

前缀和 一、一维前缀和示例模板[寻找数组的中心下标 ](https://leetcode.cn/problems/tvdfij/description/)除自身以外的数组乘积和可被k整除的子数组 一、一维前缀和 前缀和就是快速求出数组某一个连续区间内所有元素的和。 示例模板 已知一个数组arr&#xff0c;求前缀和 …

buuctf-[极客大挑战 2019]Knife题解

一个很简单的web题&#xff0c;进入界面 网页名还加白给的shell&#xff0c;并且给的提示也很明显&#xff0c;给了一个一句话木马再加上菜刀&#xff0c;很怀疑是一个webshell题&#xff0c;那么直接打开蚁剑测试连接拿shell 用提示的一句话木马的密码&#xff0c;测试链接发现…

基于YOLO11深度学习的半导体芯片缺陷检测系统【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…