socket epoll模型

linux 使用epoll主要目的是啥 为了实现非阻塞么?

socket本来就有阻塞和非阻塞两种模式,与epoll无关。

epoll是针对多socket操作(从select升级到poll再到epoll都是解决这个目的)。

如果不用poll方法,在阻塞模式下,操作多socket,要么用多线程,要么用多进程,都会带来一定的开发复杂度和性能降低。在非阻塞模式下,就要使用轮询,浪费处理能力很厉害。

所以,epoll是为了让程序只在一个线程中就能操作大量socket而提供的一个核心功能,同时还提供了很高的处理性能。

socket select、epoll模型本身是为了解决多客户端连接的问题。

同时,epoll由于机制的设置,在一点程度上降低了资源的使用,提高了效率。

一个进程内,select能打开的fd是有限制的,由宏FD_SETSIZE设置。默认值是1024。在某些时候,这个数值是远远不够用的。

解决的方法有两种,一是改动宏然后又一次编译内核,但与此同一时候会引起网络效率的下降;二是使用多进程来解决,可是创建多个进程是有代价的,并且进程间数据同步没有多线程间方便。

扫描二维码关注公众号,回复: 13440668 查看本文章

而epoll没有这个限制,它所支持的最大FD上限远远大于1024,在1GB内存的机器上是10万左右(详细数目能够cat/proc/sys/fs/file-max查看);

select函数每次都当监听的套接组有事件产生时就会返回。但却不能将有事件产生的套接字筛选出来。而是改变其在套接组的标志量,所以每次监听到事件,都须要将套接组整个遍历一遍。时间复杂度是O(n)。当FD数目添加时。效率会线性下降。

而epoll,每次会将监听套结字中产生事件的套接字加到一列表中,然后我们能够直接对此列表进行操作,而没有产生事件的套接字会被过滤掉,极大的提高了IO效率。

这一点尤其在套接字监听数量巨大而活跃数量非常少的时候非常明显。

//server.c
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#define MAXLINE 10   //最大长度
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 8000
#define INFTIM 1000
#define IP_ADDR "127.0.0.1"

int main()
{
    struct epoll_event ev, events[20];
    struct sockaddr_in clientaddr, serveraddr;
    int epfd;
    int listenfd;//监听fd
    int maxi;
    int nfds;
    int i;
    int sock_fd, conn_fd;
    char buf[MAXLINE];

    epfd = epoll_create(256);//生成epoll句柄
    listenfd = socket(AF_INET, SOCK_STREAM, 0);//创建套接字
    ev.data.fd = listenfd;//设置与要处理事件相关的文件描写叙述符
    ev.events = EPOLLIN;//设置要处理的事件类型

    epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);//注冊epoll事件

    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(SERV_PORT);
    bind(listenfd,(struct sockaddr*)&serveraddr, sizeof(serveraddr));//绑定套接口
    socklen_t clilen;
    listen(listenfd, LISTENQ);//转为监听套接字
    int n;
    while(1)
    {
        nfds = epoll_wait(epfd,events,20,500);//等待事件发生
        //处理所发生的全部事件
        for(i=0;i<nfds;i++)
        {
            if(events[i].data.fd == listenfd)//有新的连接
            {
                clilen = sizeof(struct sockaddr_in);
                conn_fd = accept(listenfd, (struct sockaddr*)&clientaddr, &clilen);
                printf("accept a new client : %s\n",inet_ntoa(clientaddr.sin_addr));
                ev.data.fd = conn_fd;
                ev.events = EPOLLIN;//设置监听事件为可写
                epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev);//新增套接字
            }
            else if(events[i].events & EPOLLIN)//可读事件
            {
                if((sock_fd = events[i].data.fd) < 0)
                    continue;
                if((n = recv(sock_fd, buf, MAXLINE, 0)) < 0)
                {
                    if(errno == ECONNRESET)
                    {
                        close(sock_fd);
                        events[i].data.fd = -1;
                    }
                    else
                    {
                        printf("readline error\n");
                    }
                }
                else if(n == 0)
                {
                    close(sock_fd);
                    printf("关闭\n");
                    events[i].data.fd = -1;
                }

                printf("%d -- > %s\n",sock_fd, buf);
                ev.data.fd = sock_fd;
                ev.events = EPOLLOUT;
                epoll_ctl(epfd,EPOLL_CTL_MOD,sock_fd,&ev);//改动监听事件为可读
            }

            else if(events[i].events & EPOLLOUT)//可写事件
            {
                sock_fd = events[i].data.fd;
                printf("OUT\n");
                scanf("%s",buf);
                send(sock_fd, buf, MAXLINE, 0);

                ev.data.fd = sock_fd;
                ev.events = EPOLLIN;
                epoll_ctl(epfd, EPOLL_CTL_MOD,sock_fd, &ev);
            }
        }
    }

    return 0;   
}

参考:https://www.cnblogs.com/cynchanpin/p/7261539.html

猜你喜欢

转载自blog.csdn.net/xiadeliang1111/article/details/112326199