随笔 - 632  文章 - 17  评论 - 54  阅读 - 93万

使用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;
}
复制代码

 

posted on   飘杨......  阅读(257)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
历史上的今天:
2020-12-15 Android onInterceptTouchEvent的move和up事件不执行
2020-12-15 RelativeLayout中layout_centerInParent不起作用
2014-12-15 Android 由 android:launchMode="singleInstance“引发的界面无法返回的情况
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示