IO多路复用之POLL【内附可运行代码注释完整】

POLL和SELECT的最大区别就是处理文件句柄的上限,SELECT 有文件句柄上线设置,值为FD_SETSIZE,而poll 理论上没有限制!(这一点还是很有必要的,要不然在生产环境下有相关限制的话那就会耽误事(虽然生产环境也不会用POLL😂))

头文件:

#include <poll.h>

定义:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
  • pollfd:要监视的所有事件都放到这里面
struct pollfd {
                int fd;           /*文件描述符   open打开的那个*/
                /*********************
                events的取值有如下:
                POLLIN           有数据可读
        		POLLRDNORM 有普通数据可读,等效与POLLIN
       			POLLPRI         有紧迫数据可读
        		POLLOUT        写数据不会导致阻塞
        		POLLER          指定的文件描述符发生错误
        		POLLHUP        指定的文件描述符挂起事件
        		POLLNVAL      无效的请求,打不开指定的文件描述符
                **********************/
                short events;     /*请求的事件类型,监视驱动文件的事件掩码*/  POLLIN | POLLOUT
                short revents;    /*驱动文件实际返回的事件*/
}
  • nfds: //监测驱动文件的个数
  • timeout://超时时间,单位是ms
  • 返回值
    有事件发生 返回revents域不为0的文件描述符个数
    超时:return 0
    失败:return -1 错误:errno

本例子是客户端和服务器端进行交换信息(如果客户端发送字符‘A’,那么服务器端将回复一个‘B’),我将以此例子来说明POLL的用法(本例子可以直接在linux下运行,有兴趣的同学可以自己玩玩)
server.c:

#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <sys/time.h> 
#include <sys/ioctl.h> 
#include <unistd.h> 
#include <stdlib.h>
#include <poll.h>
#include <string.h>

#define MAX_FD 8192
struct pollfd fds[MAX_FD];
int cur_max_fd = 1;

void setMaxFD(int fd)
{
    if(fd >= cur_max_fd)
    {
        cur_max_fd = fd + 1;
    }

}


int main()
{
    int server_sockfd, client_sockfd;
    int server_len, client_len;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;
    int result;
    //fd_set readfds, testfds;
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立服务器端socket 
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(9001);
    server_len = sizeof(server_address);
    bind(server_sockfd, (struct sockaddr*)&server_address, server_len);
    listen(server_sockfd, 5); //监听队列最多容纳5个 
    //FD_ZERO(&readfds);
    //FD_SET(server_sockfd, &readfds);//将服务器端socket加入到集合中
    fds[server_sockfd].fd=server_sockfd;
    fds[server_sockfd].events=POLLIN;
    fds[server_sockfd].revents = 0;
    setMaxFD(server_sockfd);
    while (1)
    {
        char ch;
        int i,fd;
        int nread;
        printf("server waiting\n");
        /*有事件发生  返回revents域不为0的文件描述符个数
         *超时:return 0
         *失败:return  -1   错误:errno 
	     */
        result = poll(fds, cur_max_fd, 2000);
        if (result < 0)
        {
            perror("server5");
            exit(1);
        }
        /*扫描所有的文件描述符*/
        for (i = 0; i < cur_max_fd; i++)
        {
            /*找到相关文件描述符 如果是该fd发生事件则revent返回事件类型*/
            if (fds[i].revents)
            {
                fd = fds[i].fd;
                /*判断是否为服务器套接字,是则表示为客户请求连接。*/
                if (fd == server_sockfd)
                {
                    client_len = sizeof(client_address);
                    //记录客户端的fd
                    client_sockfd = accept(server_sockfd,
                        (struct sockaddr*)&client_address, &client_len);
                    //将客户端socket加入到集合中
                    fds[client_sockfd].fd=client_sockfd;
                    fds[client_sockfd].events=POLLIN;
                    fds[client_sockfd].revents = 0;
                    setMaxFD(client_sockfd);
                    printf("adding client on fd %d\n", client_sockfd);
                }
                else
                {
		            /*如果是触发客户端fd的读事件,即有"人"对client_fd进行write或者是close操作*/
                    if(fds[i].revents & POLLIN){
                        nread = read(fd, &ch, 1);//读一个字节,返回数据量
                        /*客户数据请求完毕,关闭套接字,从集合中清除相应描述符 如果数据量为0表示已经请求完毕*/
                        if (nread == 0)
                        {
                            close(fd);//关闭套接字
                            //去掉关闭的fd,其实将event直接设置为0也可以
                            memset(&fds[i], 0, sizeof(struct pollfd));
                            
                            printf("removing client on fd %d\n", fd);
                        }
                        /*处理客户数据请求*/
                        else
                        {
                            sleep(5);
                            printf("serving client on fd %d,receive:%c\n", fd,ch);
                            ch++;
                            fds[i].events = POLLOUT;
                        }
                    }else if(fds[i].revents & POLLOUT){//数据可写,写通道准备好了
                        write(fd, &ch, 1);
                        fds[i].events = POLLIN;//继续监听读数据
                    }
                }
            }
        }
    }

    return 0;
}

client.c:

#include <sys/types.h> 
#include <sys/socket.h> 
#include <stdio.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <stdlib.h>
#include <sys/time.h>

int main()
{
    int client_sockfd;
    int len;
    struct sockaddr_in address;//服务器端网络地址结构体 
    int result;
    char ch = 'A';
    client_sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立客户端socket 
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("127.0.0.1");
    address.sin_port = htons(9001);
    len = sizeof(address);
    result = connect(client_sockfd, (struct sockaddr*)&address, len);
    if (result == -1)
    {
        perror("oops: client2");
        exit(1);
    }
    //第一次读写
    write(client_sockfd, &ch, 1);
    read(client_sockfd, &ch, 1);
    printf("the first time: char from server = %c\n", ch);
    sleep(5);

    //第二次读写
    write(client_sockfd, &ch, 1);
    read(client_sockfd, &ch, 1);
    printf("the second time: char from server = %c\n", ch);

    close(client_sockfd);

    return 0;
}

运行结果:
在这里插入图片描述

这里就不过多分析代码了,代码里面有注释而且代码也不长,大家伙可以自己看看,其实和SELECT区别真不大,如果实在有一些问题的话,可以参考这个链接,里面有我之前分析SELECT代码,可以参考参考,如果本文有其他问题也欢迎同学们指出,谢谢!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值