使用Epoll相较select的好处

  1. 没有文件描述符的限制(MAX_FILES),select默认的1024.
  2. 工作效率不会随着描述符的增加而下降(没有改变的描述符不会返回,也就是不会遍历)
  3. 经过内核级优化的

Epoll的事件触发模式

Level Trigger 水平触发,如果数据没有一次性处理完毕,会再次发送
Edge Trigger 边沿触发,不管数据有没有一次性处理完毕,都不会再次发送(效率最高,难度大)

Epoll重要的API

int epoll_create();              //创建一个epoll实例
int epoll_ctl(epfd,op,fd,struct epoll_event *event);//向epoll添加事件
int epoll_wait(epfd,events,maxevents,timeout);//等待事件跟seclect差不多

Epoll重要的事件

EPOLLET 通过它可以设置边沿模式
EPOLLIN
EPOLLOUT
EPOLLPRI 出现中断
EPOLLERR 读写出现异常
EPOLLHUP 挂起

epoll_ctl 相关操作

EPOLL_CTL_ADD 将文件描述符add到epoll中去
EPOLL_CTL_MOD 对已经加入到epoll的文件描述符进行修改
EPOLL_CTL_DEL 将已经加入到epoll中的文件描述符删除掉

Epoll重要的结构体

typedef union epoll_data{//用union修饰,一次只能使用其中一项属性
	void     *ptr;
	int      fd;
	uint32_t u32;
	uint64_t u64;
} epoll_data_t;

struct epoll_event {
	uint32_t      events;//in out err等事件
	epoll_data_t  data;//可以放自己的数据,比如文件描述符
};

代码实例(服务端)

#include <iostream>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define PORT 8111
#define MESSAGE_LEN 1024
#define MAX_EVENTS 20
#define TIMEOUT 500
int main(int argc,char* argv[]){
        int socketfd,epoll_fd,event_number,flags;
        int on=1;
        int ret=-1;
        int backlog = 10;
        int accept_fd = -1;
        struct epoll_event ev,events[MAX_EVENTS];
        struct sockaddr_in localaddr,remoteaddr;

        char in_buff[MESSAGE_LEN]={0,};
        socketfd = socket(AF_INET,SOCK_STREAM, 0);

        if(socketfd == -1){
                std::cout<<"failed to create socket"<<std::endl;
                exit(-1);
        }
        flags = fcntl(socketfd,F_GETFL,0);
        fcntl(socketfd,F_SETFL,flags| O_NONBLOCK);
        ret = setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
        if(ret == -1){
                std::cout<<"failed to set socket options"<<std::endl;
        }

        localaddr.sin_family = AF_INET;
        localaddr.sin_port = PORT;
        localaddr.sin_addr.s_addr = INADDR_ANY;
        bzero(&(localaddr.sin_zero),8);

        ret = bind(socketfd,(struct sockaddr *)&localaddr,sizeof(struct sockaddr));

        if(ret == -1){
                std::cout<<"fail to bind"<<std::endl;
                exit(-1);
        }

        ret = listen(socketfd,backlog);

        if(ret == -1){
                std::cout<<"fail to listen"<<std::endl;
                exit(-1);
        }
        epoll_fd = epoll_create(256);
        ev.events = EPOLLIN;
        ev.data.fd = socketfd;
        epoll_ctl(epoll_fd,EPOLL_CTL_ADD,socketfd,&ev);
        for(;;){
                event_number = epoll_wait(epoll_fd,events,MAX_EVENTS,TIMEOUT);
                for(int i=0;i<event_number;i++){
                        if(events[i].data.fd == socketfd){
                                socklen_t addr_len = sizeof(struct sockaddr);
                                accept_fd = accept(socketfd,(struct sockaddr *) &remoteaddr,
                                                &addr_len
                                                );
                                flags = fcntl(accept_fd,F_GETFL,0);
                                fcntl(accept_fd,F_SETFL,flags| O_NONBLOCK);
                                ev.events = EPOLLIN | EPOLLET;
                                ev.data.fd = accept_fd;
                                epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_fd,&ev);

                        }else if(events[i].events & EPOLLIN){
                                do{
                                        memset(in_buff,0,MESSAGE_LEN);
                                        ret = recv(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);
                                        if(ret == 0){
                                                close(events[i].data.fd);
                                        }
                                        if(ret == MESSAGE_LEN){
                                                std::cout<< "maybe have more data"<<std::endl;
                                        }}while(ret < 0 && errno == EINTR);
                                if(ret < 0){
                                        switch(errno){
                                                case EAGAIN:
                                                        std::cout<<"no hava more data..."<<std::endl;
                                                        break;
                                                default:
                                                        break;

                                        }
                                }
                                std::cout<<"rev:"<<in_buff<<std::endl;
                                send(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);

                        }
                }

        }
        close(socketfd);
        return 0;
}

Epoll+fork优化

当然cpu还得充分利用才nice,因此可以引入fork优化

代码(服务端)

#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define PORT 8111
#define MESSAGE_LEN 1024
#define MAX_EVENTS 20
#define TIMEOUT 500
#define MAX_PROCESS 4
int main(int argc,char* argv[]){
        int socketfd,epoll_fd,event_number,flags;
        pid_t pid=-1;
        int on=1;
        int ret=-1;
        int backlog = 10;
        int accept_fd = -1;
        struct epoll_event ev,events[MAX_EVENTS];
        struct sockaddr_in localaddr,remoteaddr;

        char in_buff[MESSAGE_LEN]={0,};
        socketfd = socket(AF_INET,SOCK_STREAM, 0);

        if(socketfd == -1){
                std::cout<<"failed to create socket"<<std::endl;
                exit(-1);
        }
        flags = fcntl(socketfd,F_GETFL,0);
        fcntl(socketfd,F_SETFL,flags| O_NONBLOCK);
        ret = setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
        if(ret == -1){
                std::cout<<"failed to set socket options"<<std::endl;
        }

        localaddr.sin_family = AF_INET;
        localaddr.sin_port = PORT;
        localaddr.sin_addr.s_addr = INADDR_ANY;
        bzero(&(localaddr.sin_zero),8);

        ret = bind(socketfd,(struct sockaddr *)&localaddr,sizeof(struct sockaddr));

        if(ret == -1){
                std::cout<<"fail to bind"<<std::endl;
                exit(-1);
        }

        ret = listen(socketfd,backlog);

        if(ret == -1){
                std::cout<<"fail to listen"<<std::endl;
                exit(-1);
        }
        epoll_fd = epoll_create(256);
        ev.events = EPOLLIN;
        ev.data.fd = socketfd;
        epoll_ctl(epoll_fd,EPOLL_CTL_ADD,socketfd,&ev);
        std::cout<<"fork foreifn"<<std::endl;
        for(int i=0;i< MAX_PROCESS;i++){
                if(pid != 0){
                        pid = fork();
                }
        }
        std::cout<<"fork end"<<std::endl;
        if(pid == 0){



                for(;;){
                        event_number = epoll_wait(epoll_fd,events,MAX_EVENTS,TIMEOUT);
                        for(int i=0;i<event_number;i++){
                                if(events[i].data.fd == socketfd){
                                        socklen_t addr_len = sizeof(struct sockaddr);
                                        accept_fd = accept(socketfd,(struct sockaddr *) &remoteaddr,
                                                        &addr_len
                                                        );
                                        flags = fcntl(accept_fd,F_GETFL,0);
                                        fcntl(accept_fd,F_SETFL,flags| O_NONBLOCK);
                                        ev.events = EPOLLIN | EPOLLET;
                                        ev.data.fd = accept_fd;
                                        epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_fd,&ev);

                                }else if(events[i].events & EPOLLIN){
                                        do{
                                                memset(in_buff,0,MESSAGE_LEN);
                                                ret = recv(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);
                                                if(ret == 0){
                                                        close(events[i].data.fd);
                                                }
                                                if(ret == MESSAGE_LEN){
                                                        std::cout<< "maybe have more data"<<std::endl;
                                                }}while(ret < 0 && errno == EINTR);
                                        if(ret < 0){
                                                switch(errno){
                                                        case EAGAIN:
                                                                std::cout<<"no hava more data..."<<std::endl;
                                                                break;
                                                        default:
                                                                break;

                                                }
                                        }
                                        std::cout<<"rev:"<<in_buff<<std::endl;
                                        send(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);

                                }
                        }

                }
                close(socketfd);
        }else{
                do{
                        pid = waitpid(-1,NULL,0);
                }while(pid != -1);
        }
        return 0;
}

启动后会看见这样几行代码,足以说明,fork的子进程的变量与父进程的是一样的

root@t:/opt/mediaserver# ./tcp_server
fork foreifn
fork end
fork end
fork end
fork end
fork end

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐