能否在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让线程结束时自动释放资源
注意事项
- 端口冲突:确保
base_port + i对应的端口未被其他程序占用,否则bind会失败 - 资源管理:无论用进程还是线程,都要正确关闭socket,避免资源泄漏
- 连接处理:补充了客户端断开的判断逻辑(
recv返回0),修复原代码的无限循环问题 - 错误处理:增强了错误捕获与处理,避免程序意外崩溃
内容的提问来源于stack exchange,提问作者ADL92
相关产品推荐
相关产品推荐

