Linux--高级IO--poll--0326

1. poll

#include 
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

poll只负责等。

参数介绍

fds

是一个结构体类型的地址,相比于select中的fd_set类型,pollfd结构体可以内部封装一些遍历,解决需要关系那些文件描述符,以及哪些文件描述符就绪了。从而解决在select中一直需要重新设定的问题。

nfds

表示fds数组的长度

timeout

单位是毫秒,不再是一个timeval类型的地址,变为了拷贝。所以也就不需要再对timeout重新设定,单位是毫秒。

返回值

返回就绪的文件描述符个数。

1.2 struct pollfd 结构体

struct pollfd
{
    int fd;//文教描述符
    short events;//告诉内核我要你关心哪些事件
    short revents;//告诉用户,内核中这些事件就绪了
};

 events和revents的取值

Linux--高级IO--poll--0326_第1张图片

Linux--高级IO--poll--0326_第2张图片 其中常用的 POLLIN POLLOUT POLLERR就对应着select中的那三个参数

1.3.demo

#include 
#include 
#include 
int main()
{
    struct pollfd poll_fd;
    //让poll帮我关心 0号文件描述符 关心事件为输入事件
    poll_fd.fd=0;
    poll_fd.events=POLLIN;
    for(;;)
    {
        int ret=poll(&poll_fd,1,1000);
        if(ret<0)
        {
            perror("poll");
            continue;
        }
        else if(ret==0)
        {
            printf("poll timeout\n");
            continue;
        }
        if(poll_fd.revents==POLLIN)
        {
            printf("poll event already\n");
            char buffer[1024];
            read(0,buffer,sizeof (buffer) -1);
            printf("%s",buffer);
        }
    }
    return 0;
}

Linux--高级IO--poll--0326_第3张图片

 2.基于select测试改写的poll代码

select版本

Linux--高级IO--select--0326_Gosolo!的博客-CSDN博客

 

#pragma once
#ifndef __POLL_SVR_H__
#define __POLL_SVR_H__

#include 
#include 
#include 
#include 
#include 
#include "log.hpp"
#include "Sock.hpp"

#define FD_NONE -1

using namespace std;

class PollServer
{
public:
    static const int nfds=100;
    PollServer(const uint16_t &port=8080)
        :_port(port)
        ,_nfds(nfds)
        ,_timeout(1000)
    {
        _listensock=Sock::Socket();
        Sock::Bind(_listensock,_port);//ip缺省值为 0.0.0.0
        Sock::Listen(_listensock);
        logMessage(DEBUG,"%s","create base socket success");
        _fds=new struct pollfd[_nfds];
        for(int i=0;i<_nfds;i++)
        {
            _fds[i].fd=FD_NONE;
            _fds[i].events=_fds[i].revents=0;

        }
        //做一个规定 _fd_array[0]=_listensock
        _fds[0].fd=_listensock;
        _fds[0].events=POLLIN;

    }
    void Start()
    {
        while(true)
        {
            int n=poll(_fds,_nfds,_timeout);
            switch(n)
            {   
            case 0:
                logMessage(DEBUG,"time out...");
                break;
            case -1:  
                logMessage(WARNING,"poll errno: %d : %s",errno,strerror(errno));
                break;
            default:
                //成功
                HandlerEvent();
                break;  
            }
        }
    }
    ~PollServer()
    {
        if(_listensock>=0) close(_listensock);
        if(_fds) delete[] _fds;
    }

private:
    void HandlerEvent()
    {
        for(int i=0;i<_nfds;i++)
        {
            //没让select关心这个文件
            if(_fds[i].fd==FD_NONE) continue; 

            //让关心了 但是需要知道他是否就绪
            if(_fds[i].revents & POLLIN)
            {
                if(_fds[i].fd==_listensock)
                {
                    Acceptr();
                }
                else
                {
                    Recver(i);
                }
            }
        }
    }
    void Acceptr()
    {
        string clientip;
        uint16_t clientport=0;
        int sock=Sock::Accept(_listensock,&clientip,&clientport);
        if(sock<0)
        {
            logMessage(WARNING,"accept error");
            return;
        }
        logMessage(DEBUG,"get a new link success :[%s:%d] : %d",clientip.c_str(),clientport,sock);

        //找一个位置添加 我刚刚得到的sock套接字 好让select帮我关心
        int pos=1;
        for(;pos<_nfds;pos++)
        {
            if(_fds[pos].fd==FD_NONE) break;
        }
        if(pos==_nfds)
        {
            //也可以在这里扩容
            logMessage(WARNING,"%s:%d","select server already full,close fd: %d",sock);
            close(sock);
        }
        else
        {
            _fds[pos].fd=sock;
            _fds[pos].events=POLLIN;
        }
    
    }
    void Recver(int pos)
    {
        // 读事件就绪:INPUT事件到来、recv,read
        logMessage(DEBUG, "message in, get IO event: %d", _fds[pos].fd);
        // 暂时先不做封装, 此时select已经帮我们进行了事件检测,fd上的数据一定是就绪的,即 本次 不会被阻塞
        // 这样读取有bug吗?有的,你怎么保证以读到了一个完整包文呢?
        char buffer[1024];
        int n = recv(_fds[pos].fd, buffer, sizeof(buffer)-1, 0);
        if(n > 0)
        {
            buffer[n] = 0;
            logMessage(DEBUG, "client[%d]# %s", _fds[pos].fd, buffer);
        }
        else if(n == 0)
        {
            logMessage(DEBUG, "client[%d] quit, me too...", _fds[pos].fd);
            // 1. 我们也要关闭不需要的fd
            close(_fds[pos].fd);
            // 2. 不要让poll帮我关心当前的fd了
            _fds[pos].fd = FD_NONE;
            _fds[pos].events=0;
        }
        else
        {
            logMessage(WARNING, "%d sock recv error, %d : %s", _fds[pos].fd, errno, strerror(errno));
            // 1. 我们也要关闭不需要的fd
            close(_fds[pos].fd);
            // 2. 不要让select帮我关心当前的fd了
           _fds[pos].fd = FD_NONE;
        }
    }
private:
    uint16_t _port;
    int _listensock;
    struct pollfd* _fds;
    int _nfds;//最大数量
    int _timeout;


};

#endif

 2.2测试及结果

Linux--高级IO--poll--0326_第4张图片

3.poll的优缺点

优点:

效率高。

适用于有大量连接,但只有少量的是活跃的。节省资源。

输入输出参数分离的,不需要进行大量的重置。

poll没有fd管理上限。

缺点:

poll依旧需要不少遍历,在用户层检测就绪,与内核检测fd就绪,都是一样。

poll需要内核到用户的拷贝。

poll代码也比较复杂。但比select容易。

你可能感兴趣的:(Linux,linux,运维,服务器)