跳转到内容

C 语言并发服务器

来自代码酷

C语言并发服务器[编辑 | 编辑源代码]

并发服务器是指能够同时处理多个客户端请求的服务器程序。在C语言网络编程中,实现并发服务器通常使用多进程(fork)、多线程(pthread)或I/O多路复用(select/poll/epoll)等技术。本节将详细介绍这些方法及其实现原理。

概述[编辑 | 编辑源代码]

并发服务器的核心目标是提高服务器的吞吐量和响应速度,避免单个客户端请求阻塞其他请求的处理。常见的实现方式包括:

  • 多进程并发服务器:为每个客户端连接创建一个新进程。
  • 多线程并发服务器:为每个客户端连接创建一个新线程。
  • I/O多路复用:使用单个进程/线程监听多个客户端连接。

多进程并发服务器[编辑 | 编辑源代码]

使用fork()系统调用为每个客户端创建子进程。

代码示例[编辑 | 编辑源代码]

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080

void handle_client(int client_socket) {
    char buffer[1024] = {0};
    read(client_socket, buffer, sizeof(buffer));
    printf("Received: %s\n", buffer);
    send(client_socket, "Hello from server!", 18, 0);
    close(client_socket);
}

int main() {
    int server_fd, client_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);

    // 创建socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 绑定socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 监听
    if (listen(server_fd, 3) < 0) {
        perror("listen failed");
        exit(EXIT_FAILURE);
    }

    while (1) {
        if ((client_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept failed");
            exit(EXIT_FAILURE);
        }

        // 创建子进程处理客户端
        pid_t pid = fork();
        if (pid == 0) {  // 子进程
            close(server_fd);  // 关闭不需要的服务器socket
            handle_client(client_socket);
            exit(0);
        } else if (pid > 0) {  // 父进程
            close(client_socket);  // 关闭不需要的客户端socket
        } else {
            perror("fork failed");
        }
    }
    return 0;
}

执行流程[编辑 | 编辑源代码]

graph TD A[主进程创建socket] --> B[绑定端口] B --> C[开始监听] C --> D{等待客户端连接} D -->|新连接| E[创建子进程] E --> F[子进程处理请求] D -->|继续| D

多线程并发服务器[编辑 | 编辑源代码]

使用POSIX线程(pthread)为每个客户端创建线程。

代码示例[编辑 | 编辑源代码]

#include <pthread.h>

// 线程处理函数
void* client_handler(void* arg) {
    int client_socket = *(int*)arg;
    handle_client(client_socket);
    free(arg);
    return NULL;
}

// 在主循环中替换fork()部分:
pthread_t thread_id;
int* new_sock = malloc(sizeof(int));
*new_sock = client_socket;
if (pthread_create(&thread_id, NULL, client_handler, (void*)new_sock) < 0) {
    perror("could not create thread");
}

I/O多路复用[编辑 | 编辑源代码]

使用select()/poll()/epoll()实现单进程处理多个连接。

select() 示例[编辑 | 编辑源代码]

fd_set readfds;
int max_fd = server_fd;

while (1) {
    FD_ZERO(&readfds);
    FD_SET(server_fd, &readfds);

    // 添加所有客户端socket到集合
    for (int i = 0; i < max_clients; i++) {
        if (client_sockets[i] > 0) {
            FD_SET(client_sockets[i], &readfds);
            if (client_sockets[i] > max_fd) {
                max_fd = client_sockets[i];
            }
        }
    }

    // 等待活动
    int activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);
    if (activity < 0) {
        perror("select error");
    }

    // 检查服务器socket是否有新连接
    if (FD_ISSET(server_fd, &readfds)) {
        // 接受新连接...
    }

    // 检查客户端socket是否有数据
    for (int i = 0; i < max_clients; i++) {
        if (client_sockets[i] > 0 && FD_ISSET(client_sockets[i], &readfds)) {
            // 处理客户端请求...
        }
    }
}

性能比较[编辑 | 编辑源代码]

并发服务器技术比较
方法 优点 缺点
多进程 隔离性好,稳定性高 资源消耗大,进程间通信复杂
多线程 资源共享方便,创建开销小 需要处理线程同步问题
I/O多路复用 资源消耗最小 编程复杂度高

实际应用案例[编辑 | 编辑源代码]

1. Web服务器:如Apache早期版本使用多进程,Nginx使用事件驱动(epoll) 2. 数据库服务器:MySQL使用多线程处理连接 3. 即时通讯服务器:需要同时维持大量连接

进阶话题[编辑 | 编辑源代码]

  • 线程池技术
  • 事件驱动架构
  • 协程(Coroutine)实现并发
  • 异步I/O(AIO)

参见[编辑 | 编辑源代码]