【Epoll多进程】惊群实例

不使用epoll时,惊群效应在linux内核在大于2.6的版本中已经不会出现。

使用epoll还是会出现惊群

看了一圈只看见有多线程下的惊群

放一个多进程下的惊群,作为demo,sleep很重要!

#include "my_socket.h"
#include <sys/epoll.h>
#define MAX_PROCESS 4
#define SERV_PORT 9527
#define OPEN_MAX 1024
#define MESSAGE_SIZE 10
int main(int argc,char *argv[]){
    int listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt,sizeof(opt));
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERV_PORT);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    Bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    Listen(listenfd, 128);
    //将新建立的socket设置成非阻塞,经测试,这里设置成阻塞不影响下面依然会惊群。
    int flags= fcntl(listenfd,F_GETFL,0);
    flags |= O_NONBLOCK;
    fcntl(listenfd,F_SETFL,flags);
    
    //fork创建多个子进程,
    //这里先用一个for把需要的子进程创建出来,再使用。(饿汉)
    pid_t subpid = -1;
    int k=0;
    for(k =0;k<MAX_PROCESS;++k){
        if(subpid!=0) {
            subpid=fork();
            printf("%d\n",subpid);
        }
    }

    if(subpid==0){//在子进程中进行监听并对监听到的事件进行处理。
        //epoll初始化,每个子进程都有自己的epoll实例,且监听同一个socket:listenfd。
        epfd = epoll_create(256);
        if(epfd == -1) perr_exit("epoll_create error:");
        tempep.events = EPOLLIN;
        tempep.data.fd = listenfd;

        //向epfd监听红黑树加入对listenfd的读监听
        int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &tempep);
        if(ret == -1) perr_exit("epoll_ctl_init error:");

        //while中所需参数
        int i,j,connfd,nready,tempfd,readsize;
        struct sockaddr_in client_addr;
        int client_addr_len = sizeof(client_addr);
        char client_ip[BUFSIZ], readbuf[MESSAGE_SIZE];
        while(1){
            //阻塞监听epfd监听红黑树中的fd
            nready = epoll_wait(epfd, eps, OPEN_MAX, -1);
            if(nready == -1) perr_exit("epoll_wait error:"); 

            sleep(1);//需要sleep给各个子进程一个反应时间,否则不会引起惊群效应。

            for(i=0;i< nready;++i){
                //if(!(eps[i].events & EPOLLIN)) continue;  //对返回的结构体一次判断是否有读事件,没有则直接continue
                if(eps[i].data.fd == listenfd){  //判断所返回的结构体(监听到的事件)的fd是不是listenfd    是的话,说明该事件希望建立新的tcp连接。
                        printf("listen event... \n");
                    connfd = Accept(listenfd, (struct sockaddr*)&client_addr,&client_addr_len);

                    //将新建立的socket设置成非阻塞
                    int flag = fcntl(connfd,F_GETFL,0);
                    flag |= O_NONBLOCK;
                    fcntl(connfd,F_SETFL,flag);

                    //返回的已建立连接的connfd,加入到epfd监听红黑树中
                    printf("received from %s at PORT %d\n",
                           inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)),
                           ntohs(client_addr.sin_port));
                    //                printf("cfd %d --- client %d \n",connfd, ++num);
                    tempep.events = EPOLLIN | EPOLLET; tempep.data.fd = connfd;
                    ret = epoll_ctl(epfd, EPOLL_CTL_ADD,connfd,&tempep);
                    if(ret == -1) perr_exit("epoll_ctl_add error:");
                }   
                else if(eps[i].events & EPOLLIN){//说明eps[i]监听到的事件不是读事件,应该是已经建立连接的fd发来的数据通信事件。
                    do{
                    tempfd = eps[i].data.fd;
                    memset(readbuf,0,MESSAGE_SIZE);
                    readsize = Readn(tempfd,readbuf, MESSAGE_SIZE);
                    //readsize = Read(tempfd,readbuf,sizeof(readbuf));
                    if(readsize ==0){
                        ret = epoll_ctl(epfd, EPOLL_CTL_DEL, tempfd, NULL);
                        if(ret==-1) perr_exit("epoll_ctl_Del error:");
                        Close(tempfd);
                        printf("client[%d] closed connection\n",tempfd);

                    }
                    else{
                        if(readsize == MESSAGE_SIZE){printf("need more buffer");}//缓冲区满了,用其他的缓冲方法,比如缓冲队列。
                        Write(STDOUT_FILENO, readbuf, readsize );
                        for(j = 0;j<readsize;++j) readbuf[j] = toupper(readbuf[j]);
                        Write(tempfd, readbuf, readsize);
                        Write(STDOUT_FILENO, readbuf, readsize);
                    }
                    }while(readsize<0 && errno==EINTR);
                    if(readsize<0){
                        switch(errno){
                            case EAGAIN:
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
        }
        Close(epfd);
    }else{//subpid!=0,即在主线程中回收所有子进程。  
        do{
            subpid = waitpid(-1,NULL,0);
        }while(subpid!=-1);
    }
        Close(listenfd);
    return 0;  
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值