首页 > 其他 > 详细

I/O多路复用 --epoll模式

时间:2020-09-28 12:54:00      阅读:54      评论:0      收藏:0      [点我收藏+]

代码基本框架参考:https://blog.51cto.com/14569275/2538214

背景
能确切指定有数据的socket
线程安全
结构体
技术分享图片
结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件

typedef union epoll_data {
        void *ptr;
         int fd;
         __uint32_t u32;
         __uint64_t u64;
     } epoll_data_t;//保存触发事件的某个文件描述符相关的数据

         struct epoll_event {
         __uint32_t events;      /* epoll event */
         epoll_data_t data;      /* User data variable */
     };

技术分享图片
函数
技术分享图片

创建

int epoll_create(int size)  

参数:size监听的数目
返回值: 文件描述符 可以在/proc/PID/fd查看

控制

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

参数:
技术分享图片
操作:
技术分享图片
返回值:
技术分享图片

轮询I/O事件

 int epoll_wait(int epfd,struct epoll_event events,int maxevents,int timeout)

参数:
技术分享图片
返回值:
技术分享图片

编码流程
1.创建epoll描述符
2.注册epoll事件
3.等待epoll事件
4.判断触发epoll事件的描述符和事件
5.关闭epoll描述符
触发条件
技术分享图片
技术分享图片

客户端代码

#include<iostream>
#include<arpa/inet.h>
#nclude<unistd.h>
#include<sys/epoll.h> //epoll
#include<linux/fs.h> //OPEN_MAX
#include<strings.h>
using namespace std;

string name;

void show_connect(int fd){
//获取本地址和端口
        struct sockaddr_in  local_addr;
        socklen_t local_addr_len=sizeof(local_addr);
        bzero(&local_addr,local_addr_len);
        getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
            cout<<"local"<<inet_ntoa(local_addr.sin_addr)<<":"<<ntohs(local_addr.sin_port)<<endl;
        //获取远程地址和端口
            struct sockaddr_in remote_addr;
            socklen_t remote_addr_len=sizeof(remote_addr);
            bzero(&remote_addr,remote_addr_len);
            getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
            cout<<"remote"<<inet_ntoa(remote_addr.sin_addr)<<":"<<ntohs(remote_addr.sin_port)<<endl;
}

//./client ip port
int  main(int argc,char* argv[]){
        if(argc!=4){
            cout<<"usage:"<<argv[0]<<"Ip port name"<<endl;
            return 1;
        }
        name=argv[3];
        //1.创建套接字
        int connfd=socket(AF_INET,SOCK_STREAM,0);
        if(connfd==-1){
         cout<<" socket error"<<endl;
         return 1;
        }

        //2连接服务器

        struct sockaddr_in remote_addr;
        remote_addr.sin_family=AF_INET;//协议
        remote_addr.sin_addr.s_addr=inet_addr(argv[1]);//IP地址
        remote_addr.sin_port=htons(atoi(argv[2]));//端口号

        if(-1==connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))){
        cout<<"connect error"<<endl;
        return 1;
        }else{
          cout<<"connect success"<<endl;
            show_connect(connfd);
        }
//创建epoll描述符
 int epollfd=epoll_create(2);
 //注册事件
  struct epoll_event evt;
    evt.data.fd=STDIN_FILENO;
    evt.events=EPOLLIN;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,STDIN_FILENO,&evt);
    evt.data.fd=connfd;
    evt.events=EPOLLIN;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&evt);

    bool stop=false;
    while(!stop){
     int count=2;
     struct epoll_event revt[2];
     int revt_count=epoll_wait(epollfd,revt,count,-1);
     for(int i=0;i<revt_count;i++){
        if(revt[i].data.fd==STDIN_FILENO &&revt[i].events & EPOLLIN){
              //发送消息
                string message;
                getline(cin,message);
                message=name+":"+message;
                write(connfd,message.c_str(),message,szie()+1);
            }else if(revt[i].data.fd==connfd & revt[i].events & EPOLLIN){
              //接收数据
      char buffer[1024]={0};
          int len=read(connfd,buffer,sizeof(buffer));
            if(len==0){
              cout<<"server exit"<<endl;
                epoll_ctl(epollfd,EPOLL_CTL_DEL,connfd,revt+i);
                --count;
                stop=true;
                break;
            }else{
             cout<<buffer<<endl;
            }
            }
     }

    }
    close(epollfd);
    close(connfd);
    return 0;
}

服务器端

#include<iostream>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<linux/fs.h> //INR_OPEN_MAX
#nclude<unistd.h>
#include<strings.h>
using namespace std;

void show_connect(int fd){
        struct sockaddr_in  local_addr;
        socklen_t local_addr_len=sizeof(local_addr);
        bzero(&local_addr,local_addr_len);
        getsockname(fd,(struct sockaddr*)&local_addr,&local_addr_len);
            cout<<"local"<<inet_ntoa(local_addr.sin_addr)<<":"<<ntohs(local_addr.sin_port)<<endl;

            struct sockaddr_in remote_addr;
            socklen_t remote_addr_len=sizeof(remote_addr);
            bzero(&remote_addr,remote_addr_len);
            getpeername(fd,(struct sockaddr*)&remote_addr,&remote_addr_len);
            cout<<"remote"<<inet_ntoa(remote_addr.sin_addr)<<":"<<ntohs(remote_addr.sin_port)<<endl;
}
void Show(struct pollfd* fds,int n){
  for(int i=0;i<n;i++){
      cout<<fds[i].fd<<" ";
    }
    cout<<endl;
}

./server ip port
int  main(int argc,char* argv[]){
   cout<<FD_SETSIZE<<endl;

     if(argc!=3){
            cout<<"usage:"<<argv[0]<<"Ip port name"<<endl;
            return 1;
        }

        //1.监听套接字
        int listenfd=socket(AF_INET,SOCK_STREAM,0);
        if(listenfd==-1){
         cout<<"listen socket error"<<endl;
         return 1;
        }
        //设置端口重复利用
         int  flag=1; setsockopt(listenfd,SQL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));

        //2.绑定

        struct sockaddr_in local_addr;
        local_addr.sin_family=AF_INET;//协议
        local_addr.sin_addr.s_addr=inet_addr(argv[1]);//IP地址
        local_addr.sin_port=htons(atoi(argv[2]));//端口号

        if(-1==bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr))){
        cout<<"bind error"<<endl;
        return 1;
        }else{
          cout<<"bind success"<<endl;
        }

        //3.监听设置
        if(listen(listenfd,10)==-1){
            cout<<"listen error"<<endl;
            return 1;
        }else{
           cout<<"listen success"<<endl;
        }

    int epollfd=epoll_create(INR_OPEN_MAX);
    struct epoll_event evt;
    evt.data.fd=STDIN_FILENO;
    evt.events=EPOLLIN;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,STDIN_FILENO,&evt);
    evt.data.fd=listenfd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,listenfd,&evt);

    int count=2;
    list<int> fds;
    while(true){
      struct epoll_event revt[count];
        int revt_cnt=epoll_wait(epollfd,revt,count,-1);
        for(int i=0;i<revt_cnt;i++){
          if(revt[i].data.fd==STDIN_FILENO & revt[i].events & EPOLLIN){
              string message;
                getline(cin,message);
                cin.clear(); //清空输入出错
                if(!message.empty()){
                    message="广告"+message;
                        for(auto fd:fds){
                         write(fd,message.c_str(),message.size()+1);
                    }
                }
            }else if(revt[i].data.fd==listenfd && revt[i].events & EPOLLIN){
              struct sockaddr_in remote_addr;
                bzero(&remote_addr,sizeof(remote_addr));
                socklen_t remote_addr_len=sizeof(remote_addr);
                int connfd=accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
                if(connfd==-1){
                   cout<<"accept error"<<endl;
                     return 1;
                }else{
                  cout<<"accept success"<<endl;
                    cout<<inet_ntoa(remote_addr.sin_addr)<<":"<<ntohs(remote_addr.sin_port)<<endl;
                    show_connect(connfd);
                    struct epoll_event evt;
                    evt.data.fd=connfd;
                    evt.events=EPOLLIN;
                    epoll_ctl(epollfd,EPOLL_CTL_ADD,connfd,&evt);
                    fds.push_back(connfd);
                    ++count;

                }

            }else{

              int connfd=revt[i].data.fd;
                char buffer[1024]={0};
                int n=read(connfd,buffer,1024);//读取客户端发过来的数据
                if(n==0){
                  cout<<"client exit"<<endl;
                    close(connfd);//关掉退出连接
                    epoll_ctl(epollfd,EPOLL_CTL_DEL,connfd,revt+i);
                    fds.remove(connfd);
                    --count;
                    break;  
                }else{
                  for(auto fd:fds){//发送消息给所有客户端
                    if(fd==connfd) continue;
                    write(fd,buffer,1024);
                    }

                }

            }
        }
    }
    close(epollfd);
    close(listenfd)
}

汇总比较

select IO多路复用
1.只能监视FD_SETSIZE个连接
2.不能确切指定有数据的socket
3.每次需要修改传入的fd_set
4.线程不安全

Poll IO多路复用
优点
1.不需要不修改传入的pollfd数组
2.可以监视任意个连接
缺点
1.不能确切指定有数据的socket
2.线程不安全

Epoll IO多路复用
优点
1.能确切指定有数据的socket
2.线程安全

I/O多路复用 --epoll模式

原文:https://blog.51cto.com/14569275/2538607

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!