多进程实现并发服务器

目录

1.多进程函数原型

1.1waitpid

1.2sigaction

1.3SIGCHILD信号

2.端口复用

3.案例源码

3.1服务器


1.多进程函数原型

1.1waitpid


    #include <sys/types.h>
    #include <sys/wait.h>
    pid_t waitpid(pid_t pid, int *wstatus, int options);

功能:回收指定进程号的子进程,可以设置是否阻塞
参数:
            pid:
                pid>0:某个子进程pid
                pid=0:回收当前进程组的任意子进程
                pid=-1:回收所有的子进程,相当于wait() 最常用
                pid<-1:回收某个进程组的组id的绝对值,回收指定进程组的子进程 如回收2组子进程资源传递(-2)
            int *wstatus
            进程退出时的状态信息,传入的是一个int类型的地址,传出参数
 
            options:设置是否阻塞
                0:阻塞
                WNOHANG:非阻塞
            返回值
                >0:返回子进程的id
                =0:options=WNOHANG,表示还有子进程
                =-1:错误,或者没有子进程

1.2sigaction

    #include <signal.h>
    int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

 功能:检查或者改变信号的处理。信号捕捉。
 参数:
      signum:需要捕捉的信号的编号或者宏值(信号的名称)
      act:捕捉到信号之后的处理动作
      oldact:上一次对信号捕捉相关的设置,一般不适用,传递NULL
 返回值:
      成功 0
      失败 -1

    struct sigaction {
        //函数指针,指向的函数就是信号捕捉到之后的处理函数
        void     (*sa_handler)(int);
        //不常用
        void     (*sa_sigaction)(int, siginfo_t *, void *);
        //临时阻塞信号集,信号捕捉函数指向过程中,临时阻塞某些信号
        sigset_t   sa_mask;
        // 使用哪一个信号对捕捉到的信号进行处理
        //这个值可以是0,表示使用sa_handler,也可以是SA_SIGINFO表示使用sa_sigaction
        int        sa_flags;
        //被废弃了
        void     (*sa_restorer)(void);
}

1.3SIGCHILD信号

代表子进程结束,父进程会收到这个信号量

2.端口复用

作用:

1.防止服务器重启时之前绑定的端口还未释放

2.程序突然退出而系统没有释放端口


#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t
optlen);    

//设置端口复用
int optval=1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));

3.案例源码

3.1服务器

#include<sys/types.h>
#include<sys/wait.h>
#include<signal.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<errno.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
void recyleChild(int arg){
    while(1){
        int ret = waitpid(-1,NULL,WNOHANG);
        if(ret==-1){
            //所有子进程都被回收
            break;
        }else if(ret==0){
            //还有子进程活着
            break;
        }else if(ret>0){
            //被回收了
            printf("子进程 %d 被回收了\n",ret);
        }
    }
}
int main()
{
    struct sigaction act;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    act.sa_handler=recyleChild;
    //注册信号捕捉
    sigaction(SIGCHLD,&act,NULL);
    
    //1.创建socket
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    if(lfd==-1){
        perror("socket");
        exit(-1);
    }
    printf("服务器socket创建成功\n");


    //2.设置端口复用
    int optval=1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
    
    //3.绑定服务器套接字端口
    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    saddr.sin_addr.s_addr=INADDR_ANY;
    saddr.sin_port=htons(9999);

    int ret=bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret==-1){
        perror("bind");
        exit(-1);
    }
    printf("服务器绑定端口成功\n");

    //4.监听
    ret = listen(lfd,128);
    if(ret==-1){
        perror("listen");
        exit(-1);
    }
    printf("服务器监听套接字成功\n");
    int cnt=0;
    while(1){
        //5.接受客户端连接
        struct sockaddr_in cliaddr;
        int len=sizeof(cliaddr);
        int cfd=accept(lfd,(struct sockaddr*)&cliaddr,&len);
        if(cfd==-1){
            if(errno==EINTR)//软中断产生的错误忽略避免子进程结束父进程也随之结束
            {
                continue;
            }
            perror("accept");
            exit(-1);
        }
        printf("服务器成功接受客户端连接:%d\n",++cnt);
        
        //每一个连接创建一个对应的子进程
        pid_t pid=fork();
        if(pid==0){
            //子进程
            //通信
            char recvBuf[1024];
            while(1){
                memset(recvBuf,0,sizeof(recvBuf));
                int data=read(cfd,recvBuf,sizeof(recvBuf));
                if(data<0){
                    perror("read");
                    exit(0);
                }else if(data==0){
                    printf("客户端断开连接...\n");
                    close(cfd);
                    exit(0);
                }
                printf("收到客户端数据:%s\n",recvBuf);
                memset(recvBuf,0,sizeof(recvBuf));
                sprintf(recvBuf,"成功收到客户端信息");
                data=write(cfd,recvBuf,strlen(recvBuf)+1);
                if(data<0){
                    perror("write");
                    exit(0);
                }
            printf("服务端发送数据成功\n");   
        }
    }
    }
    close(lfd);
    return 0;
}

3.2客户端

#include<arpa/inet.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h> // read write close
#include<stdlib.h>
int main()
{
    //1.创建套接字
    int cfd=socket(AF_INET,SOCK_STREAM,0);
    if(cfd==-1){
        perror("socket");
        exit(0);
    }
    printf("1.客户端创建套接字成功\n");
 
    //2.连接服务器
    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(9999);
    inet_pton(AF_INET,"192.168.108.128",&saddr.sin_addr);//服务器的ip地址
    /*
    p:点分十进制的IP字符串,n:表示network,网络字节序的整数
        int inet_pton(int af, const char *src, void *dst);
        af:地址族: AF_INET AF_INET6
        src:需要转换的点分十进制的IP字符串
        dst:转换后的结果保存在这个里面
    */
    int ret=connect(cfd,(struct sockaddr*)&saddr,sizeof(saddr));
 
    if(ret==-1){
        perror("connect");
        exit(-1);
    }
    printf("2.客户端服务器连接成功\n");
 
    //3.通信
    char recvBuf[1024]={0};
    while(1){
        printf("请输入你想要发给客户端的内容\n");
        memset(recvBuf,0,sizeof(recvBuf));
        scanf("%s",recvBuf);
        write(cfd,recvBuf,strlen(recvBuf)+1);
        memset(recvBuf,0,sizeof(recvBuf));
        int data=read(cfd,recvBuf,sizeof(recvBuf));
        if(data>0)
        {
            printf("服务端返回数据:%s\n",recvBuf);
        }
        else if(data==0){
            printf("服务端断开连接\n");
        }else if(data<0){
            perror("read");
            exit(-1);
        }
    }
    close(cfd);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值