DEV Community

shuai991102
shuai991102

Posted on

epoll实现并发服务器代码

select/poll/epoll区别?

select:

        1、select监听的最大文件描述符是1024个;

        2、select需要反复构造表,反复从用户空间向内核空间拷贝表,效率低

        3、当数据准备好后,select需要再次遍历表,找出准备好的文件描述符,效率低

poll:

        1、poll监听的文件描述符没有限制

        2、poll不需要反复构造表,不需要反复拷贝表,效率高

        3、当数据准备好之后,poll需要遍历表,找出准备好的文件描述符,效率低

epoll:

        1、epoll监听的文件描述符没有限制

        2、epoll不需要反复构造表,不需要反复拷贝表,效率高

        3、当数据准备好之后,epoll直接能够找出准备好的文件描述符,不需要遍历表,效率高。
Enter fullscreen mode Exit fullscreen mode
#include "c.h"
#define ERR_MSG(msg)                          \
    do                                        \
    {                                         \
        fprintf(stderr, "line:%d", __LINE__); \
        perror(msg);                          \
    } while (0)
#define IP "192.168.250.100"
#define PORT 6666
#define BUFLEN 128
int deal_cli_msg(int newfd, struct sockaddr_in cin);

// 回收僵尸进程
void handler(int sig)
{
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
}

int main(int argc, const char *argv[])
{
    char buf[BUFLEN];
    // 捕获17号 SIGCHLD信号,回收僵尸进程
    __sighandler_t s = signal(17, handler);
    if (SIG_ERR == s)
    {
        ERR_MSG("signal");
        return -1;
    }
    // 创建流式套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("sfd=%d\n", sfd);
    // 允许端口快速重用
    int reuse = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        ERR_MSG("setsockopt");
        return -1;
    }
    printf("允许端口快速重用成功\n");

    // 填充地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);
    sin.sin_addr.s_addr = inet_addr(IP);
    // 将ip和端口绑定到套接字上
    if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("ip绑定套接字成功__%d__\n", __LINE__);
    // 将套接字设置为可监听状态
    if (listen(sfd, 128) < 0)
    {
        ERR_MSG("listen");
        return -1;
    }
    printf("将套接字设置为可监听状态成功__%d__\n", __LINE__);
    // 存储连接成功的客户端地址信息
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);
    int newfd = -1;
    int epfd,epct,i,ret;
    struct epoll_event event;       //定义epoll 事件
    struct epoll_event events[20];  //定义epoll 事件集合
    epfd = epoll_create(1); // 创建epoll 的fd //红黑树根节点

    event.data.fd = sfd;           //填充事件的fd
    event.events = EPOLLIN | EPOLLET;   //填充 事件类型 
    epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);  //把serverFd(监听FD)注册到epfd中


    while(1){

        epct = epoll_wait(epfd,events,20,-1); // 等待事件到来,阻塞模式 
        for(i=0;i<epct;i++){  //根据epoll返回的值来查询事件

            if(events[i].data.fd == sfd){ // 如果事件的fd是监听fd,调用accept处理
                newfd = accept(events[i].data.fd,(struct sockaddr *)&cin,&addrlen);
                event.data.fd = newfd;
                event.events = EPOLLIN | EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,newfd,&event);
                }
            else {   //如果不是serverFd,应是client数据事件,调用读数据

                memset(buf,0,BUFLEN);
                ret = read(events[i].data.fd,buf,BUFLEN);
                if(ret <=0){ //客户端断开 
                    printf("connect disconnected\n");
                    close(events[i].data.fd); //关闭文件描述符
                    epoll_ctl(epfd,EPOLL_CTL_DEL,events[i].data.fd,&event); //删除监听的客户端fd
                    continue;
                }
            }
        }
    }
    // 关闭进程的所有的文件描述符
    for (i = 0; i < getdtablesize(); i++)
    {
        close(i);
    }
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)