通过多进程并发方式(fork)实现服务器(注意要回收子进程)

以下内容为视频学习记录。

1、父进程accept后返回的文件描述符为cfd以及用于创建连接的lfd;

调用fork()创建子进程后,子进程继承cfd,lfd,通过该cfd与连接过来的客户端通信,lfd对子进程来说没用,可以直接close(lfd);

对于父进程来说,只是用来建立连接的,故父进程中的cfd没有用,直接close(cfd);

2、子进程完成数据交互后,close(cfd);exit(1);此时成为僵尸进程,所以需要在父进程中收尸,回收进程描述符等资源。否则的话,当父进程一直没有退出的时候,僵尸进程会导致资源消耗殆尽。

但此时父进程在accept,无法waitpid(),此时使用signal的方式SIGCHILD来解决收尸问题,当子进程成僵尸进程后,由内核自动回收。

注意:进程只能由父进程来回收,兄弟进程、叔叔进程都不行。

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#include <unistd.h>

#include "wrap.h"

#define MAXLINE 8192
#define SERV_PORT 8000

void do_sigchild(int num)
{
    while (waitpid(0, NULL, WNOHANG) > 0)
        ;
}

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, connfd;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    int i, n;
    pid_t pid;
    struct sigaction newact;

    newact.sa_handler = do_sigchild;
    sigemptyset(&newact.sa_mask);
    newact.sa_flags = 0;
    sigaction(SIGCHLD, &newact, NULL);

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    Listen(listenfd, 20);

    printf("Accepting connections ...\n");
    while (1) {
        cliaddr_len = sizeof(cliaddr);
        connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
        pid = fork();
        if (pid == 0) {
            Close(listenfd);
            while (1) {
                n = Read(connfd, buf, MAXLINE);
                if (n == 0) {
                    printf("the other side has been closed.\n");
                    break;
                }
                printf("received from %s at PORT %d\n",
                        inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
                        ntohs(cliaddr.sin_port));

                for (i = 0; i < n; i++)
                    buf[i] = toupper(buf[i]);

                Write(STDOUT_FILENO, buf, n);
                Write(connfd, buf, n);
            }
            Close(connfd);
            return 0;
        } else if (pid > 0) {
            Close(connfd);
        }  else
            perr_exit("fork");
    }
    return 0;
}

总结:子进程负责与客户端通信,父进程负责接收客户端连接,但因为父进程还需要回收子进程,所以通过信号的方式来回收,

信号部分的代码也可以放在fork之后判断父进程位置。

else if (pid > 0) {
            Close(connfd);

signal(SIGCHLD,wait_child);
        }  

main函数外定义回调函数

void wait_child(int signo)
{
     while(watipid(0,NULL,WNOHANG)>0);  //大于0则继续回收
          return;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值