• 使用poll实现聊天服务


    一、概述

      poll函数介绍:

    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
        函数说明: 跟select类似, 监控多路IO, 但poll不能跨平台.
        参数说明:
             fds: 传入传出参数, 实际上是一个结构体数组
    
             fds.fd: 要监控的文件描述符
    
             fds.events: 
                      POLLIN---->读事件
                      POLLOUT---->写事件
    
             fds.revents: 返回的事件
    
             nfds: 数组实际有效内容的个数
    
             timeout: 超时时间, 单位是毫秒.
                      -1:永久阻塞, 直到监控的事件发生
                      0: 不管是否有事件发生, 立刻返回
                      >0: 直到监控的事件发生或者超时
    
       返回值: 
             成功:返回就绪事件的个数
             失败: 返回-1
             若timeout=0, poll函数不阻塞,且没有事件发生, 此时返回-1, 并且errno=EAGAIN, 这种情况不应视为错误.
    
    
       struct pollfd 
       {
          int   fd;        /* file descriptor */   监控的文件描述符
          short events;     /* requested events */  要监控的事件---不会被修改
          short revents;    /* returned events */   返回发生变化的事件 ---由内核返回
       };
    
    
    说明: 
       1 当poll函数返回的时候, 结构体当中的fd和events没有发生变化, 究竟有没有事件发生由revents来判断, 所以poll是请求和返回分离.
       2 struct pollfd结构体中的fd成员若赋值为-1, 则poll不会监控.
       3 相对于select, poll没有本质上的改变; 但是poll可以突破1024的限制.
    
       在/proc/sys/fs/file-max查看一个进程可以打开的socket描述符上限.
       如果需要可以修改配置文件: /etc/security/limits.conf
       加入如下配置信息, 然后重启终端即可生效.
       * soft nofile 1024  软限制不能超过硬件限制,即:soft nofile的值不能超过hard nofile的值
       * hard nofile 100000 
       soft和hard分别表示ulimit命令可以修改的最小限制和最大限制

      案例:使用poll函数实现高并发服务器

    二、代码示例

    //IO多路复用技术poll函数的使用
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>
    #include <poll.h>
    #include "wrap.h"
    
    int main(){
        int i;//for循环的索引
        int n;//读取数据的个数
        int lfd;//监控文件描述符
        int cfd;//通讯文件描述符
        int ret;
        int nready;//就绪的时间个数
        int maxfd;//最大文件描述符
        char buf[1024];//读写缓冲区大小
        socklen_t len;//
        int sockfd;//通信文件描述符
        fd_set tmpfds,rdfs;
        struct sockaddr_in svraddr,cliaddr;
    
        //创建socket,并返回监听文件描述符
        lfd = Socket(AF_INET,SOCK_STREAM,0);
    
        //允许端口复用
        int opt;
        setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
    
        //绑定地址和端口
        svraddr.sin_family = AF_INET;
        svraddr.sin_port = htons(8888);
        svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
        ret = Bind(lfd,(struct sockaddr *)&svraddr,sizeof(struct sockaddr_in));
    
        //监听
        ret = Listen(lfd,128);
    
        //填充要监控的结构体数组client[i].fd=-1表示内核不监控
        struct pollfd client[1024];
        for(i=0;i<1024;i++){
            client[i].fd = -1;
        }
    
    
        //将监听文件描述符委托给内核监控----监控读事件
        client[0].fd = lfd;
        client[0].events = POLLIN;//表示读事件
    
        maxfd = 0;//maxfd表示内核监控的范围
    
        while(1){
            nready = poll(client,maxfd+1,-1);//监控多路IO,返回就绪的事件个数
            if(nready<0){//如果事件个数小于0说明出错了,直接退出进程即可
                perror("poll error");
                exit(1);
            }
    
            //有客户端连接请求(监听文件描述符=lfd,并且事件类型是读事件)
            if(client[0].fd = lfd &&(client[0].revents&POLLIN)){
                //接收客户端连接并返回一个通讯文件描述符
                cfd = Accept(lfd,NULL,NULL);
    
                //寻找client数组中可用位置(找到位置后将cfd加入数据交给内核监控)
                for(i=1;i<1024;i++){
                    if(client[i].fd ==-1){
                        client[i].fd = cfd;
                        client[i].events = POLLIN;
                        break;
                    }
                }
    
                //如果没有可用位置则关闭连接
                if(i==1024){
                    Close(cfd);
                    continue;
                }
                if(maxfd<i){//可用就绪的文件描述符的最大索引
                    maxfd = i;
                }
                if(--nready==0){//如果就一个就绪的文件描述符,那就不用往下执行了
                    continue;
                }
            }
            //下面是有数据到来的情况(只用循环可用的文件描述符的最大索引即可)
            for(i=1;i<=maxfd;i++){
                //若fd为-1 表示连接已经关闭或者没有连接
                if(client[i].fd == -1){
                    continue;
                }
                sockfd = client[i].fd;
                memset(buf,0x00,sizeof(buf));
                n = Read(sockfd,buf,sizeof(buf));//从内核中读取缓冲区数据
                if(n<=0){
                    printf("read error or client closed,n==[%d]\n",n);
                    Close(sockfd);
                    client[i].fd = -1;//fd为-1,表示不再让内核监控
                }else{
                    printf("read over ,n=[%d],buf=[%s]\n",n,buf);
                    write(sockfd,buf,n);//给客户端回复数据
                }
                if(--nready==0){
                    break;
                }
    
            }
        }
        Close(lfd);//最后要关闭监听文件描述符
        return 0;
    }
  • 相关阅读:
    [编程] 正则表达式
    [游戏] PhysX物理引擎(编程入门)
    [PHP] visitFile()遍历指定文件夹
    [D3D] 用PerfHUD来调试商业游戏
    [C,C++] 妙用0元素数组实现大小可变结构体
    [D3D] DirectX SDK 2006学习笔记1——框架
    [JS] 图片浏览器(兼容IE,火狐)
    [C#(WinForm)] 窗体间传值方法
    [ASP.NET] 提示错误:The server has encountered an error while loading an application during the processing your request
    [JS] 火狐得到文件的绝对路径(暂时的方法)
  • 原文地址:https://www.cnblogs.com/tony-yang-flutter/p/15691309.html
Copyright © 2020-2023  润新知