• 使用epoll实现聊天服务


    一、概述

      1.epoll函数及相关结构体介绍

    多路IO-epoll
        将检测文件描述符的变化委托给内核去处理, 然后内核将发生变化的文件描述符对应的事件返回给应用程序.
        (通俗点讲就是我们不用关心文件描述符的变化了,内核帮我们干了,并且内核把那些有变化的具体的文件描述符都会返回回来)
    
        函数介绍:
        int epoll_create(int size);
        函数说明: 创建一个树根
        参数说明:
                size: 最大节点数, 此参数在linux 2.6.8已被忽略, 但必须传递一个大于0的数.
        返回值:
                成功: 返回一个大于0的文件描述符, 代表整个树的树根.
                失败: 返回-1, 并设置errno值.
    
    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
        函数说明: 将要监听的节点在epoll树上添加, 删除和修改
        参数说明:
                epfd: epoll树根
                op:
                EPOLL_CTL_ADD: 添加事件节点到树上
                EPOLL_CTL_DEL: 从树上删除事件节点
                EPOLL_CTL_MOD: 修改树上对应的事件节点
                fd: 事件节点对应的文件描述符
                event: 要操作的事件节点
                           typedef union epoll_data {
                               void        *ptr;
                               int          fd;//文件描述符,可能是监听也可能是通讯
                               uint32_t     u32;
                               uint64_t     u64;
                           } epoll_data_t;
    
                           struct epoll_event {
                               uint32_t     events;      /* Epoll events */
                               epoll_data_t data;        /* User data variable */
                           };
                            event.events常用的有:
                             EPOLLIN: 读事件
                             EPOLLOUT: 写事件
                             EPOLLERR: 错误事件
                             EPOLLET: 边缘触发模式
                             event.data.fd: 要监控的事件对应的文件描述符
    
    
        int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
        函数说明:等待内核返回事件发生
        参数说明:
                epfd: epoll树根
                events: 传出参数, 其实是一个事件结构体数组
                maxevents: 数组大小
                timeout:
                -1: 表示永久阻塞
                0: 立即返回
                >0: 表示超时等待事件
        返回值:
                成功: 返回发生事件的个数
                失败: 若timeout=0, 没有事件发生则返回; 返回-1, 设置errno值, 
    
    
        epoll_wait的events是一个传出参数, 调用epoll_ctl传递给内核什么值, 当epoll_wait返回的时候, 内核就传回什么值,不会对struct event的结构体变量的值做任何修改.

      2.案例:使用epoll编写一个简单的聊天服务器,要求:客户端发送什么,服务端就回复什么,如果服务端发送的是消息字母则转换为大写。

    二、代码示例

    //EPOLL高并发服务器编写(只有Linux系统支持epoll)
    #include <sys/epoll.h>
    #include <ctype.h>
    #include "wrap.h"
    
    int main(){
        //0.定义变量
        int ret;
        int n;
        int i;
        int k;
        int nready;
        int lfd;
        int cfd;
        int sockfd;
        char buf[1024];
        socklen_t socklen;
        struct sockaddr_in svraddr;
        struct epoll_event ev;
        struct epoll_event events[1024];
    
        //1.创建socket
        lfd = Socket(AF_INET,SOCK_STREAM,0);
    
        //2.绑定端口
        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);
        Bind(lfd,(struct sockaddr *)&svraddr,sizeof(struct sockaddr_in));
    
        //3.监听
        Listen(lfd,128);
    
        //4.创建一棵树
        int epfd = epoll_create(1024);
        if(epfd<0){
            perror("create epoll error");
            return -1;
        }
    
        //5.将监听文件描述符上树
        ev.data.fd = lfd;
        ev.events = EPOLLIN;
        epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
    
        //6.while(1)中epoll_wait监控文件描述符变化
        while(1){
            nready = epoll_wait(epfd,events,1024,-1);
            if(nready<0){
                perror("epoll_wait error");
                if(errno=EINTR){//如果是信号中断就不视为错误
                    continue;
                }
                break;
            }
            for(i=0;i<nready;i++){
                //7.判断是监听文件描述符还是通讯文件描述符
                sockfd = events[i].data.fd;
                if(sockfd==lfd){//8.如果是监听文件描述符调用Accept创建通讯文件描述符并上树
                    cfd = Accept(lfd,NULL,NULL);
                    ev.data.fd = cfd;
                    ev.events = EPOLLIN;
                    epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
                    continue;
                }
    
                //9.如果是通讯文件描述符,就进行读写操作
                memset(buf,0x00,sizeof(buf));
                n = Read(sockfd,buf,sizeof(buf));
                if(n<0){
                    printf("n=[%d],buf=[%s]\n",n,buf);
                    close(sockfd);
                    //将sockfd对应的事件节点从epoll树上删除
                    epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
                }else{
                    printf("n=[%d],buf=[%s]\n",n,buf);
                    for(k=0;k<n;k++){
                        buf[k] = toupper(buf[k]);
                    }
                    Write(sockfd,buf,n);
                }
            }
    
        }
        
        
        
        //10.最后关闭监听文件描述符
        close(epfd);//关闭epoll根文件描述符
        close(lfd);
        return 0;
    }
  • 相关阅读:
    (转)把hadoop源码关联到eclipse工程
    windows 系统下git 的使用
    windows 系统下git 的安装
    windows 系统下TortoiseGit 的安装
    windows 安装 mysql
    windows 下 mySQL 镜像安装文件下载
    vs2012 有效产品密钥
    mac 安装 visual studio 配置
    mac 安装 nodeJs&npm 配置
    macOS Sierra 如何打开任何来源
  • 原文地址:https://www.cnblogs.com/tony-yang-flutter/p/15691592.html
Copyright © 2020-2023  润新知