You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否在C程序中启动多个监听不同IP套接字地址的服务器?

在C程序中同时启动多个TCP服务器的实现方法

核心思路

要在单个C程序中启动多个服务器,核心是让每个服务器实例监听不同端口(或不同IP地址),同时通过**进程(fork)或线程(pthread)**实现并发运行。以下基于你提供的单服务器代码,给出两种可行改造方案。


方案一:用fork创建子进程(推荐,每个服务器独立进程)

利用fork()系统调用循环创建子进程,每个子进程负责一个端口的完整服务器逻辑(socket、bind、listen、accept、连接处理),父进程仅负责创建子进程。

改造后代码示例

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

// 提取原服务器逻辑为独立函数,方便复用
void run_server(long port) {
    int listenerSocket;
    int clientSocket;
    struct sockaddr_in server;
    struct sockaddr_in client;
    char buf[100];

    listenerSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenerSocket == -1) {
        perror("erreur lors de la création du socket");
        exit(EXIT_FAILURE);
    }

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);

    if (bind(listenerSocket, (struct sockaddr *)&server, sizeof(server)) < 0) {
        perror("bind failed");
        close(listenerSocket);
        exit(EXIT_FAILURE);
    }
    printf("Server listening on port %ld...\n", port);

    if (listen(listenerSocket, 5) < 0) {
        perror("listen failed");
        close(listenerSocket);
        exit(EXIT_FAILURE);
    }

    while (1) {
        socklen_t c = sizeof(client);
        clientSocket = accept(listenerSocket, (struct sockaddr *)&client, &c);
        if (clientSocket < 0) {
            perror("Accept()");
            continue;
        }
        printf("Port %ld: connection accepted\n", port);

        // 处理客户端消息,补充断开判断逻辑
        while (1) {
            ssize_t recv_len = recv(clientSocket, buf, sizeof(buf)-1, 0);
            if (recv_len < 0) {
                perror("Recv()");
                break;
            } else if (recv_len == 0) {
                printf("Port %ld: client disconnected\n", port);
                break;
            }
            buf[recv_len] = '\0'; // 确保字符串合法
            printf("Port %ld: Message : %s\n", port, buf);

            // 回显消息给客户端
            if (send(clientSocket, buf, recv_len, 0) < 0) {
                perror("Send()");
                break;
            }
        }
        close(clientSocket);
    }

    close(listenerSocket);
    exit(EXIT_SUCCESS);
}

int main(int argc, char const *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <base_port>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    long base_port = strtol(argv[1], NULL, 10);
    const int server_count = 5; // 要启动的服务器数量

    for (int i = 0; i < server_count; i++) {
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork failed");
            exit(EXIT_FAILURE);
        } else if (pid == 0) {
            // 子进程运行对应端口的服务器,避免继续循环创建子进程
            run_server(base_port + i);
            exit(EXIT_SUCCESS);
        }
        // 父进程继续创建下一个子进程
    }

    // 父进程等待所有子进程结束(可选,若要后台运行可删除此循环)
    while (wait(NULL) > 0);
    return 0;
}

关键说明

  • 父进程通过fork()创建子进程,每个子进程独立占用端口运行服务器
  • 子进程执行完服务器逻辑后会持续运行,直到被外部终止
  • 父进程可选择等待子进程结束,或直接退出让子进程在后台运行

方案二:用线程(pthread)实现

若不想创建多个进程,可使用POSIX线程,每个线程负责一个端口的服务器逻辑。

改造后代码示例

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

// 线程参数结构体,用于传递端口号
typedef struct {
    long port;
} ThreadArgs;

// 服务器线程执行函数
void* server_thread(void* arg) {
    ThreadArgs* args = (ThreadArgs*)arg;
    long port = args->port;
    free(arg); // 释放参数内存

    int listenerSocket;
    int clientSocket;
    struct sockaddr_in server;
    struct sockaddr_in client;
    char buf[100];

    listenerSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (listenerSocket == -1) {
        perror("erreur lors de la création du socket");
        pthread_exit(NULL);
    }

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);

    if (bind(listenerSocket, (struct sockaddr *)&server, sizeof(server)) < 0) {
        perror("bind failed");
        close(listenerSocket);
        pthread_exit(NULL);
    }
    printf("Server thread listening on port %ld...\n", port);

    if (listen(listenerSocket, 5) < 0) {
        perror("listen failed");
        close(listenerSocket);
        pthread_exit(NULL);
    }

    while (1) {
        socklen_t c = sizeof(client);
        clientSocket = accept(listenerSocket, (struct sockaddr *)&client, &c);
        if (clientSocket < 0) {
            perror("Accept()");
            continue;
        }
        printf("Port %ld: connection accepted\n", port);

        while (1) {
            ssize_t recv_len = recv(clientSocket, buf, sizeof(buf)-1, 0);
            if (recv_len < 0) {
                perror("Recv()");
                break;
            } else if (recv_len == 0) {
                printf("Port %ld: client disconnected\n", port);
                break;
            }
            buf[recv_len] = '\0';
            printf("Port %ld: Message : %s\n", port, buf);

            if (send(clientSocket, buf, recv_len, 0) < 0) {
                perror("Send()");
                break;
            }
        }
        close(clientSocket);
    }

    close(listenerSocket);
    pthread_exit(NULL);
}

int main(int argc, char const *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <base_port>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    long base_port = strtol(argv[1], NULL, 10);
    const int server_count = 5;
    pthread_t threads[server_count];

    for (int i = 0; i < server_count; i++) {
        ThreadArgs* args = malloc(sizeof(ThreadArgs));
        args->port = base_port + i;

        int ret = pthread_create(&threads[i], NULL, server_thread, args);
        if (ret != 0) {
            perror("pthread_create failed");
            free(args);
            exit(EXIT_FAILURE);
        }
        // 分离线程,避免资源泄漏
        pthread_detach(threads[i]);
    }

    // 主线程必须保持运行,否则进程退出后所有子线程会被终止
    while (1) {
        sleep(1);
    }

    return 0;
}

关键说明

  • 用ThreadArgs结构体传递端口参数给线程
  • 每个线程独立处理对应端口的服务器逻辑
  • 主线程需保持存活(如无限睡眠),否则进程退出会导致所有线程终止
  • 使用pthread_detach让线程结束时自动释放资源

注意事项

  1. 端口冲突:确保base_port + i对应的端口未被其他程序占用,否则bind会失败
  2. 资源管理:无论用进程还是线程,都要正确关闭socket,避免资源泄漏
  3. 连接处理:补充了客户端断开的判断逻辑(recv返回0),修复原代码的无限循环问题
  4. 错误处理:增强了错误捕获与处理,避免程序意外崩溃

内容的提问来源于stack exchange,提问作者ADL92

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 10:56:06