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

news/2025/2/22 20:12:26

服务器模型

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

网络程序里面,通常都是一个服务器处理多个客户机。

为了处理多个客户机的请求, 服务器端的程序有不同的处理方式。

目前最常用的服务器模型. 
循环服务器:
    循环服务器在同一个时刻只能响应一个客户端的请求 
并发服务器:
    并发服务器在同一个时刻可以响应多个客户端的请求 

📌 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; // 使用 IPv4
    server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IP
    server_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; // IPv4
    server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IP
    server_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 socket

client = 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; // IPv4
    server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IP
    server_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; // IPv4
    server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有 IP
    server_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 socket

client = 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()	                 -- 创建服务器 socket
	3. 绑定 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.niftyadmin.cn/n/5862722.html

相关文章

前端面试之Flex布局:核心机制与高频考点全解析

目录 引言&#xff1a;弹性布局的降维打击 一、Flex布局的本质认知 1. 两大核心维度 2. 容器与项目的权力边界 二、容器属性深度剖析 1. 主轴控制三剑客 2. 交叉轴对齐黑科技 三、项目属性关键要点 1. flex复合属性解密 2. 项目排序魔法 四、六大高频面试场景 1. 经…

渲染 101 支持 3ds Max 的渲染器及其优势

在 3ds Max 创作流程里&#xff0c;渲染环节对最终成果的呈现效果起着决定性作用&#xff0c;渲染 101 云渲染平台则为 3ds Max 用户提供了全面且高效的渲染解决方案。 支持的渲染器 V-Ray 渲染器 在 3ds Max 中应用广泛&#xff0c;具备全局光照、光线追踪技术&#xff0c;…

输入框元素覆盖冲突

后端响应中的 "trainingKbGroupName": "基础死型" 通过searchForm2.initFormData(rowData[0]);操作会把基础死型四个字填充到<div class"col-sm-5 form-group"> <label class"col-sm-3 control-label">知识点分组名称<…

一次交换机故障导致的云平台(opensatck+ceph)不可用的记录

前言 发现几年前记录的一次由于交换机故障导致的云平台使用异常的问题&#xff0c;因为比较少见所以也整理记录一下 一、细节过程 用户联系说好多运行在云主机上的网页访问不了了&#xff0c;需要处理一下。 甲方现场还挺远&#xff0c;我就先登陆在虚拟机上部署的堡垒机&…

Java 数学函数库

文章目录 前言为什么用 Java 编写代码量项目结构核心模块 math-library常规整数有理数有限小数进制数复数集合离散函数统计 自研算法大有理数转化为 double 自创概念数排编码组合编码二进制编码 概率组合选概率 压缩均匀压缩 核心模块 math-built-in-type基础计算统计排列组合操…

矩阵-矩阵置零

矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为0 。请使用 原地 算法。在计算机科学中&#xff0c;一个原地算法&#xff08;in-place algorithm&#xff09;是一种使用小的&#xff0c;固定数量的额外之空间来转…

网页五子棋——匹配模块

目录 时序图 约定前后端交互接口 前端页面 game_hall.html game_hall.css 获取用户信息 约定前后端交互接口 controller 层接口设计 service 层接口设计 前端请求 功能测试 前端实现 服务端实现 OnlineUserManager 创建请求/响应对象 处理连接成功 处理开始/结…

鸿蒙NEXT应用App测试-专项测试(DevEco Testing)

注意&#xff1a;大家记得先学通用测试在学专项测试 鸿蒙NEXT应用App测试-通用测试-CSDN博客 注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注…