linux多路转接epoll—服务器代码

一、epoll多路转接简介

1、什么是多路转接:举个例子—如果有很多人要联系老板,需要先联系秘书,然后每隔一段时间,秘书就告知老板这段时间内有多少人联系了他,以及这些联系人的信息。

linux多路转接epoll---服务器代码

2、多路转接的三种实现方式的优缺点(注释:多路转接的三种实现方式的优缺点参考于:https://blog.csdn.net/angeldg/article/details/107203052

(1)select多路转接:

优点:遵循posix标准,跨平台移植性比较好

缺点:

文件描述符的最大数量有限制(最大1024,可以进行修改);

通过轮询遍历判断实现的,性能会随着文件描述符的增多而下降;

只返回就绪的文件描述符集合,需要进行遍历才能得知哪个文件描述符就绪了哪个事件

(2)poll多路转接:

优点:

简化了select中三种事件的操作流程

文件描述符的最大数量没有限制

缺点:

通过轮询遍历判断实现的,性能会随着文件描述符的增多而下降

平台移植性差

(3)epoll多路转接

优点:

文件描述符的最大数量没有限制

监控使用一步阻塞操作完成,性能不会随着文件描述符的增多而下降(前半句我也不太理解,就直接照搬结论了)

返回就绪事件的文件描述符,以及每个文件描述符的信息。

缺点:

跨平台移植性差

(4)三种多路转接方式的简单对比

linux多路转接epoll---服务器代码

二、epoll多路转接的函数原型

1、int epoll_creat(int size);

作用:生成一个epoll专用的文件描述符

size:epoll上能关注的最大描述符数(如果不够用,函数会自动扩展)

2、int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

作用:用于控制某个epoll文件描述符事件,可以注册、修改、删除

epfd:epoll_create生成的epoll专用描述符—即epoll的返回值

op:

EPOLL_CTL_ADD —注册

EPOLL_CTL_MOD —修改

EPOLL_CTL_DEL —删除

fd:关联的文件描述符

event:告诉内核要监听什么事件

EPOLLIN—读

EPOLLOUT—写

EPOLLERR—异常

相关结构体介绍

struct epoll_event{

uint32_t events;

epoll_data_t data;//联合体

}

typedef union epoll_data{

void *ptr;//描述更多的信息,用指针,

int fd;//只描述单一的信息,

uint32_t u32;

uint64_t u64;

}epoll_data_t;

3、int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

作用:等待IO事件发生—可以设置阻塞的函数,功能对应select/poll函数

epfd:要检测的句柄

events:用于回传待处理事件的数组

maxevents:告诉内核这个events的大小

timeout:为超时时间

-1:永久阻塞

0:立即返回

0:阻塞时长,单位毫秒

三、epoll多路转接服务器代码

  1 #include
  2 #include
  3 #include
  4 #include<string.h>
  5 #include
  6 #include
  7 #include
  8 #include
  9
 10
 11 int main(int argc,const char* argv[])
 12 {
 13     if(argc<2)
 14     {
 15         printf("please input:./a.out port\n");
 16         return -1;
 17     }
 18     int port=atoi(argv[1]);
 19     //1、创建套接字
 20     int lfd=socket(AF_INET,SOCK_STREAM,0);//AF_INET表示ipv4协议,SOCK_STREAM使用tcp通信,0使用对应的默认协议
 21     if(lfd==-1)//失败返回-1,系统会检测到errno错误
 22     {
 23         perror("socket error");
 24         exit(1);//在main函数中,exit(1)等价于return 1
 25     }
 26     //2、绑定
 27     struct sockaddr_in server;//存储使用的协议,port和ip
 28     server.sin_family=AF_INET;//ipv4协议
 29     server.sin_port=htons(port);//htons将端口号,从本机的小端顺序转化为网络的大端顺序
 30     server.sin_addr.s_addr=htonl(INADDR_ANY);//同理,INADDR_ANY表示本机任意可用IP,值为0,也可用在等式右边直接写一个0
 31     int ret=bind(lfd,(struct sockaddr*)&server,sizeof(server));
 32     if(ret==-1)
 33     {
 34         perror("bind error");
 35         exit(1);
 36     }
 37
 38     //3、监听
 39     listen(lfd,128);//128同时监听的最大客户端数量
 40
 41     //4、epoll_create创建红黑树,返回根节点的文件描述符
 42     int epfd=epoll_create(3333);//3333创建红黑树的节点数,如果全部用完,系统会自动分配更多的节点
 43
 44     //5、初始化红黑树,将用于监听的文件描述符lfd挂在epol树上
 45     struct epoll_event ev;
 46     ev.events=EPOLLIN;//默认为水平触发模式,还有边缘触发模式和边缘非阻塞触发模式
 47     ev.data.fd=lfd;
 48     epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
 49
 50     struct epoll_event events[3333];//创建结构体数组,用于存储红黑树的节点信息
 51     //6、循环中委托内核检测事件,epoll_wait
 52     while(1)
 53     {
 54         //epoll_wait最后一个参数设置为-1--永久阻塞,当有新的连接或者通信,则放弃阻塞
 55         int num=epoll_wait(epfd,events,sizeof(events)/sizeof(events[0]),-1);
 56         if(num==-1)//失败返回-1,并设置errno错误
 57         {
 58             perror("epoll_wait error");
 59             exit(1);
 60         }
 61         for(int i=0;i//成功,num返回准备好的文件描述符数量-官方解释
 62             //num返回二叉树上文件描述符的数量-自己的理解
 63         {//循环遍历二叉树节点
 64             int fd=events[i].data.fd;
 65
 66             if(fd==lfd) //7、如果有新的连接
 67             {
 68                 //accpet接收连接请求,此处的accpet不阻塞,已经监听成功
 69                 struct sockaddr_in cli_addr;//可以定义在循环外,为了偷懒定义这了
 70                 socklen_t cli_len=sizeof(cli_addr);
 71                 int cfd=accept(fd,(struct sockaddr*)&cli_addr,&cli_len);
 72                 if(cfd==-1)
 73                 {
 74                     perror("accept error");
 75                     exit(1);
 76                 }
 77                 //将新建立连接文件描述符cfd挂在树上,使用epoll_ctl
 78                 struct epoll_event event;
 79                 event.events=EPOLLIN;
 80                 event.data.fd=cfd;
 81                 epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&event);
 82
 83                 //打印新建立简介的客户端ip和端口port
 84                 char ip[64]={0};
 85                 printf("New client IP:%s,port:%d\n",
 86                         inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr,ip,sizeof(ip)),
 87                         ntohs(cli_addr.sin_port));
 88
 89
 90             }
 91             else //8、如果有新的通信
 92             {
 93                 //读数据
 94                 char buf[1024]={0};
 95                 int len=recv(events[i].data.fd,buf,sizeof(buf),0);
 96                 if(len==-1)//读错误
 97                 {
 98                     perror("recv error");
 99                     exit(1);
100                 }
101                 if(len==0)//客户端关闭了连接
102                 {
103                     close(fd);
104                     //将该客户端的文件描述符从二叉树上删除
105                     epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);//NULL
106                     printf("client disconnect\n");
107                 }else
108                 {
109                     printf("recv buf:%s\n",buf);
110                     //写数据
111                     send(events[i].data.fd,buf,sizeof(buf),0);
112                 }
113
114             }
115
116         }
117     }
118     //关闭文件描述符
119     close(lfd);
120     return 0;
121 };i++)

四、运行截图

1、服务器端运行截图

2、客户端A,B运行截图

注释一:客户端没有写代码,使用的是nc命令来测试的

nc+ip地址+port端口号—可以用来模拟客户端

192.128.50.129是我本机的ip,127.0.0.1是测试ip

注释二:使用vim -r filename可以恢复,因为误操作导致没有保存的vim文档

一入编程深似海,多学多查多动手

Original: https://www.cnblogs.com/asdzy/p/14375769.html
Author: 阿斯顿之意
Title: linux多路转接epoll—服务器代码

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/683294/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球