Linux socket编程(二):多进程服务器

文章讲述了如何创建一个多进程服务器,以便同时处理多个客户端的连接请求。服务器通过创建子进程来为每个连接的客户端服务,主进程仅负责接受新的连接,子进程则处理通信。文章强调了在父子进程间正确管理文件描述符以及处理SIGCHLD信号以避免僵尸进程的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、多进程服务器

最简单的的服务器程序执行流程为:创建socket →\rightarrow 绑定监听的IP地址和端口 →\rightarrow 监听客户端连接 →\rightarrow 接受/发送数据。当服务端调用read阻塞等待一个客户端发来数据时,无法同时响应其它客户端的连接请求,无法接受其它客户端发来的数据。要想同时与多个客户端建立连接并处理请求,可以为每个连接的客户端创建一个子进程,在子进程中接受/发送客户端的数据,各个子进程之间互不干扰。

按照《Unix网络编程》中所述,多进程服务端程序的逻辑为:
多进程服务器端程序
主进程只负责Accept客户端连接,将得到的文件描述符交给子进程处理,自己不需要处理。需要注意两点:
父进程中关闭connfd,子进程中关闭listenfd: 每个文件的file_struct中保存了这个文件的引用计数,即有多少个进程持有这个文件的文件描述符,每次close(fd)将文件的引用计数-1,只有当引用计数 == 0时才真正关闭文件。子进程复制父进程的资源,因此fork()创建子进程时复制了connfd和listenfd,这两个socket文件的引用计数+1,在子进程中不需要listenfd,直接关闭,这个操作不会影响父进程中的listenfd,因为这时listenfd对应的socket文件的引用计数继续保持为1,socket不会关闭。同理,父进程中也不需要connfd,可以直接关闭。

子进程执行完成时调用eixt(0): 子进程执行的代码和父进程是一样的,并不是只会执行if((pid=fork()) == 0)后面这部分。因此子进程完成后显示调用exit(0)让子进程推出,否则子进程还会回到Accept这条语句上,这不是子进程需要做的。

以上流程写成代码就是:

// 子进程执行的函数
void client_handler(int clientfd, int sockfd, unsigned short port) {
    close(sockfd);
    char buffer[1024];
    int ret = 0;
    while (true) {
        memset(buffer, 0, sizeof(buffer));
        printf("receive message from port: %d\t", port);
        if (!receive_message(clientfd, buffer)) {
            break;
        }
    }
    printf("exit child process\n");
}

int main() {
    signal(SIGCHLD, SIG_IGN);
    int sockfd = open_socket();
    int clientfd = 0;
    struct sockaddr_in client_addr;
    int size = sizeof(struct sockaddr);
    while (true) {
        clientfd = accept(sockfd, (struct sockaddr*)&client_addr, (socklen_t*)&size);
        if (fork() > 0) {
            close(clientfd);
            continue;
        } else {
            client_handler(clientfd, sockfd, client_addr.sin_port);
            exit(0);
        }
    }
    close(sockfd);
    return 0;
}

为什么要signal(SIGCHLD,SIG_IGN): 子进程退出时,会给父进程发送SIGCHLD信号,如果我们没有指定父进程对于SIGCHLD信号的处理函数,会执行默认的处理方式——SIG_DFL,这种处理方式是不处理这个信号(注意:不处理信号不等于丢弃信号),子进程的PCB(task_struct)不会被清理,变为僵尸进程。我们指定signal(SIGCHLD, SIG_IGN)后,代表父进程丢弃SIGCHLD信号,让系统内核负责子进程的销毁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值