IO多路转接模型-----poll及poll版本的TCP服务器的实现

本文深入探讨了poll函数的工作原理,对比select函数,详细解释了其事件结构化监控的优势和性能限制。通过具体实例,展示了如何使用poll函数构建服务器,包括事件数组的定义、内核监控机制以及遍历查找就绪事件的过程。
摘要由CSDN通过智能技术生成

poll函数的实现及原理:

poll实际上和select类似,都是在内核中开辟一个空间,但是不是监控每种事件,poll监控的是事件结构化的事件集合;

1.函数模型:

struct pollfd{
	int fd;
	short events;//监控的事件
	short revents;//就绪的事件
}

常用的事件就是POLLIN和POLLOUT:


#include <poll.h>
 int poll(struct pollfd *fds, nfds_t nfds, int timeout);
 
2.poll函数原理:

1.用户定义事件数组,对描述符添加关心事件;
2.poll实现监控也是将数据拷贝到内核中,然后进行轮询遍历监控,性能随着文件描述符增多而下降;
3.用户根据返回的revents判断哪一个事件就绪然后重新操作;
4.poll也不会告诉用户哪一个描述符就绪,只是告诉用户有就绪事件,需要用户遍历查找;

3.poll的优点和缺点:

优点:
1)采用事件结构的方式对描述符进行监控,简化了多个事件集合的监控方式;
2)没有描述符的具体上限;

缺点:
1)不能跨平台;
2)随着描述符增多性能下降;

poll已经被淘汰了,因为他的功能可以使用其他模型替代,而且性能高,比如epoll;select比poll的优点就是poll不能跨平台;但是比起epoll来说poll一无是处。

4.实现poll版本的服务器:

MakeFile:

poll_server:poll_server.c
    gcc  $^ -o $@
    

poll_server.c :

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>


int startup( int port )
{
    // 1. 创建套接字
    int sock = socket(AF_INET,SOCK_STREAM,0);//这里第二个参数表示TCP
    if( sock < 0 )
    {
    perror("socket fail...\n");
    exit(2);
    }

    // 2. 解决TIME_WAIT时,服务器不能重启问题;使服务器可以立即重启
    int opt = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);// 地址为任意类型
    local.sin_port = htons(port);// 这里的端口号也可以直接指定8080
    // 3. 绑定端口号

    if( bind(sock,(struct sockaddr *)&local,sizeof(local)) < 0 )
    {
    perror("bind fail...\n");
    exit(3);
    }

    // 4. 获得监听套接字
    if( listen(sock,5) < 0 )
    {
    perror("listen fail...\n");
    exit(4);
    }
    return sock;
}

int main(int argc,char* argv[] )
{
    if( argc != 2 )
    {
    printf("Usage:%s port\n ",argv[0]);
    return 1;
    }

    // 1. 获得监听套接字
    int listen_sock = startup(atoi(argv[1]));//端口号传入的时候是以字符串的形式传入的,需要将其转为整型


    // 2. 初始化结构体----监听的结构列表
    struct pollfd fd_list[1024];
    int num = sizeof(fd_list)/sizeof(fd_list[0]);
    int i = 0;

    for(; i < num ; i++  )
    {
    fd_list[i].fd = -1;// 文件描述符
    fd_list[i].events = 0;// 要监听的事件集合
    fd_list[i].revents = 0;// 关心的描述符的就绪事件集合
    }


    // 3. 添加要关心的文件描述符的只读事件

     i = 0;
    for( ; i < num; i++ )
    {
    if( fd_list[i].fd == -1 )
    {
        fd_list[i].fd = listen_sock;
        fd_list[i].events = POLLIN;// 关心只读事件
        break;
    }
    }
    while(1)
    {

       //4 . 开始调用poll等待所关心的文件描述符集就绪
       switch( poll(fd_list,num,3000) )
       {
       case 0:// 表示词状态改变前已经超过了timeout的时间
           printf("timeout...\n");
           continue;
       case -1:// 失败了
           printf("poll fail...\n");
           continue;
       default: // 成功了
           {
           //   如果是监听文件描述符,则调用accept接受新连接
           //   如果是普通文件描述符,则调用read读取数据
           int i = 0;
           for( ;i < num; i++ )
           {
               if( fd_list[i].fd == -1 )
               {
               continue;
               }
               if( fd_list[i].fd == listen_sock && ( fd_list[i].revents & POLLIN ))
               {
               // 1. 如果监听套接字上读就绪,此时提供接受连接服务

               struct sockaddr_in client;
               socklen_t len = sizeof(client);
               int new_sock = accept(listen_sock,(struct sockaddr *)&client,&len);
               if(new_sock < 0)
               {
                   perror("accept fail...\n ");
                   continue;
               }
               //获得新的文件描述符之后,将该文件描述符添加进数组中,以供下一次关心该文件描述符
               int i = 0;
               for( ; i < num; i++ )
               {
                   if( fd_list[i].fd == -1 )//放到数组中第一个值为-1的位置
                   break;
               }
               if( i < num )
               {
                   fd_list[i].fd= new_sock;
                   fd_list[i].events = POLLIN;
               }
               else
               {
                   close(new_sock);
               }
               printf("get a new link![%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
               continue;
               }

                //2. 此时关心的是普通文件描述符
            //   此时提供读取数据的服务
            if( fd_list[i].revents & POLLIN  )
            {
                char buf[1024];
                ssize_t s = read(fd_list[i].fd,buf,sizeof(buf)-1);
                if( s < 0 )
                {
                printf("read fail...\n");
                continue;
                }
                else if( s == 0 )
                {
                printf("client quit...\n");
                close(fd_list[i].fd);
                fd_list[i].fd = -1;
                }
                else
                {
                buf[s] = 0;
                printf("client# %s\n",buf);
                }
            }
           }
           }
           break;
       }
    }
    returrn 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值