首页 > 其他 > 详细

I/O多路复用 Poll模式

时间:2020-09-28 13:02:05      阅读:52      评论:0      收藏:0      [点我收藏+]

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

背景

  • 不需要不修改传入的参数数组
  • 可以监视任意个链接cat /proc/sys/fs/file-max

缺点

  • 不能确切指定有数据的socket
  • 线程不安全
    结构体
    技术分享图片

函数

int poll(struct pollfd *fdarray, unsigned long nfds, int timeout)

技术分享图片
技术分享图片
技术分享图片
技术分享图片
技术分享图片
概念
技术分享图片
编码流程
1.定义pollfd结构体数组
2.初始化pollfd结构体数组
3.设置监听poll事件
4.等待poll事件
5.判断触发事件的描述符,并做对应处理。
触发事件
技术分享图片
代码结构

// 定义pollfd结构体数组
struct pollfd pollfds[OPEN_MAX];

// 初始化pollfd结构体数组
int i;
for(i=0;i<OPEN_MAX;i++){
    pollfds[i].fd = -1;
}
int pollfds_cnt = 0;

// 设置监听事件
pollfds[0].fd = fd1;
pollfds[0].event = POLLRDNOM;
pollfds_cnt++;
pollfds[1].fd = fd1;
pollfds[1].event = POLLRDNORM;
pollfds_cnt++;

// 等待poll事件
if(poll(pollfds,pollfds_cnt,INFTIM)>0){
    int i;
    for(i=0;i<pollfds_cnt;i++){
        // 判断触发事件的描述符
        if(pollfds[i].fd == fd1 && pollfds[i].revent & POLLRDNORM){
            // do something
        }
        if(pollfds[i].fd == fd2 && pollfds[i].revent & POLLRDNORM){
            // do something
        }
    }
}

注意
struct pollfd数组的最大数是OPEN_MAX(或者linux/fs.h中的INR_OPEN_MAX )
还可以通过如下方式查看:
cat /proc/sys/fs/file-max
ulimit
客户端代码

#include<iostream>
#include<arpa/inet.h>
#nclude<unistd.h>
#include<sys/poll.h>
#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);
        }
//定义pollfd结构体数组,最大为OPEN_MAX
//fd要监听的fd
//events要监听的事件
//revents 发生的事件
struct pollfd fds[INR_OPEN_MAX];
//数组中的fd全部设置成-1
for(int i=0;i<INR_OPEN_MAX;i++){
  fds[i].fd=-1; //-1表示不监听
 }
 int  count=0;//当前监听的fd个数

 //设置监听
 fds[0].fd=STDIN_FILENO;
 fds[0].events=POLLRDNORM;
 fds[1].fd=connfd;
 fds[1].events=POLLRDNRM;
 count=2;

 for(;;){
    if(poll(fds,count,-1)>0){
           if(fds[0].revents & POLLRDNORM){
              //发送数据
                string message;
                getline(cin,message);
                message=name+":"+message;
                write(connfd,message.c_str().message.size()+1);
             } 
             if(fds[1].revents & POLLRDNORM){
               //接收数据
                 char buffer[1024]={0};
                 int len=read(connfd,buffer,sizeof(buffer));
                 if(len==0){
                  cout<<"server exit";
                    break;
                 }else{
                   cout<<buffer<<endl;
                 }
             }
        }

 }
 close(connfd);
 return 0;

}

服务器端

#include<iostream>
#include<arpa/inet.h>
#include<sys/poll.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[]){
        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;
        }
  struct pollfd fds[INR_OPEN_MAX];
    for(auto& fd:fds){
      fd.fd=-1;
    }
    fds[0].fd=STDIN_FILENO;
    fds[0].events=POLLRDNORM;
    fds[1].fd=listenfd;
    fds[1].events=POLLRDNORM;
    int count=2;

    while(true){
       Show(fds,count);
         if(poll(fds,count,-1)>0){
          if(fds[0].revents & POLLRDNORM){
               string message;
                 getline(cin,message);
                 cin.clear();//清空输入出错
                 if(!message.empty()){
                   message="广告"+message;
                     for(int i=2;i<count;i++){
                       write(fds[i].fd,message.c_str(),message.size()+1);
                     }

                 }

            }
            if(fds[1].revents & POLLRDNORM){
             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);
                    fds[count].fd=connfd;
                    fds[count].events=POLLRDNPRM;
                    fds[count].revents=0;//清零
                    ++count;
             }
            }
            for(int i=2;i<count;i++){
             if(fds[i].revents & POLLRDNORM){
                  int connfd=fds[i].fd;
                        char buffer[1024]={0};
                        int n=read(connfd,buffer,1024);//读取客户端数据
                        if(n==0){
                          cout<<"client exit"<<endl;
                            close(connfd);//关闭退出的链接
                            //把关闭的fd从数组中移除
                            for(int j=i;j<count-1;j++){
                               fds[j]=fds[j+1];
                            }
                            --count;
                            break;
                        }else{
                          for(int j=2j<count;j++){//发送消息给所有的客户端
                            if(i==j) continue;
                            write(fds[j].fd,buffer,1024);

                            }

                        }
             }
            }
         }
    }
  close(connfd);      
}

I/O多路复用 Poll模式

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

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