当前位置:   article > 正文

Linux C/C++之IO多路复用(poll,epoll)_c++ .events = pollin

c++ .events = pollin

目录

1. poll

1.1 poll与select

1.2 poll的编程模型

1.3 poll监视标准输入设备0

1.4 poll函数原型

1.5 poll实现多个(客户)client端连接(服务器)server端

2. epoll

2.1 epoll相对于poll的优势

2.2 epoll编程模型

2.3 epoll函数原型

2.4 epoll实现多个(客户)client端连接(服务器)server端

1. poll

1.1 poll与select

  • poll与select非常类似
  • poll没有最大描述符号限制
  • select在操作描述符号时使用描述符号集合fd_set, poll在操作描述符号时使用pollfd结构体链表或者数组

1.2 poll的编程模型

  1. //1. 创建fd结构体数组
  2. struct pollfd fds[300];
  3. int fdNum = 0; //当前描述符号数量
  4. //2. 把要监视的描述符号设置好
  5. fds[0].fd = 0;
  6. fds[0].events = POLLIN;
  7. fdNum++;
  8. //3. 监视
  9. int r = poll(fds,fdNum,0);
  10. if(-1 == r){
  11. //错误
  12. }else if(0 == r){
  13. //没有动静
  14. continue;
  15. }else{
  16. //有动静,检查对应事件
  17. if(fds[0].revents & POLLIN){
  18. }
  19. }

1.3 poll监视标准输入设备0

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <poll.h>
  4. #include <string.h>
  5. int main(){
  6. //1. 创建fd结构体数组
  7. struct pollfd fds[8];
  8. int fdNum = 0; //当前描述符号数量
  9. //2. 把要监视的描述符号设置好
  10. fds[0].fd = 0;
  11. fds[0].events = POLLIN;
  12. fdNum++;
  13. //3. 监视
  14. int r;
  15. char buff[1024];
  16. while(1){
  17. r = poll(fds,fdNum,0);
  18. if(-1 == r){
  19. //错误
  20. printf("监视出错!\n");
  21. }else if(0 == r){
  22. //没有动静
  23. continue;
  24. }else{
  25. printf("有动静!\n");
  26. //有动静,检查对应事件
  27. if(fds[0].revents & POLLIN){
  28. memset(buff,0,1024);
  29. scanf("%s",buff);
  30. printf(">> %s\n",buff);
  31. }
  32. }
  33. }
  34. return 0;
  35. }

1.4 poll函数原型

  1. //需要的头文件
  2. #include <poll.h>
  3. struct pollfd{
  4. int fd; //文件描述符号
  5. short events; //请求的事件
  6. short revents; //返回的事件
  7. }
  8. int poll(struct pollfd *fds, //链表头指针
  9. nfds_t nfds, //链表节点数
  10. int timeout); //延时
  1. //事件宏
  2. POLLIN //有数据可读
  3. POLLRDNORM //有普通数据可读
  4. POLLRDBAND //有优先数据可读
  5. POLLPRI //有紧急数据可读
  6. POLLOUT //写数据不会阻塞,可写数据
  7. POLLWRNORM //可以写普通数据
  8. POLLWRBAND //可以写优先数据
  9. POLLMSGSIGPOLL //消息可以使用
  10. POLLER //指定的fd出现错误
  11. POLLHUP //指定的fd被挂起
  12. POLLNVAL //指定的fd是非法的

1.5 poll实现多个(客户)client端连接(服务器)server端

服务器(server端)

  1. //服务器端
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <string.h>
  9. #include <signal.h>
  10. #include <poll.h>
  11. //最多允许的客户端数量
  12. #define NUM 100
  13. int serverSocket;
  14. void hand(int val){
  15. close(serverSocket);
  16. printf("bye bye!\n");
  17. exit(0);
  18. }
  19. int main(int argc,char* argv[]){
  20. if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
  21. printf("ip: %s port:%d\n",argv[1],atoi(argv[2]));
  22. signal(SIGINT,hand);
  23. //1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
  24. serverSocket = socket(AF_INET,SOCK_STREAM,0);
  25. if(-1 == serverSocket) printf("创建socket失败:%m\n"),exit(-1);
  26. printf("创建socket成功!\n");
  27. //2. 创建服务器协议地址簇
  28. struct sockaddr_in sAddr = { 0 };
  29. sAddr.sin_family = AF_INET; //协议类型 和socket函数第一个参数一致
  30. sAddr.sin_addr.s_addr = inet_addr(argv[1]); //将字符串转整数
  31. sAddr.sin_port = htons(atoi(argv[2])); //将字符串转整数,再将小端转换成大端
  32. //3. 绑定服务器协议地址簇
  33. int r = bind(serverSocket,(struct sockaddr*)&sAddr,sizeof sAddr);
  34. if(-1 == r) printf("绑定失败:%m\n"),close(serverSocket),exit(-2);
  35. printf("绑定成功!\n");
  36. //4. 监听
  37. r = listen(serverSocket,10); //数量
  38. if(-1 == r) printf("监听失败:%m\n"),close(serverSocket),exit(-3);
  39. printf("监听成功!\n");
  40. //监视serverSocket和ClientSocket
  41. struct sockaddr_in cAddr = {0};
  42. int len = sizeof(cAddr);
  43. int cfd; //临时保存一个客户端fd
  44. char buff[1024];
  45. int fdNum = 0;
  46. //准备一个pollfd数组
  47. struct pollfd fds[NUM];
  48. //初始化数组
  49. for(int i = 0;i < NUM; i++){
  50. fds[i].fd = -1;
  51. fds[i].events = POLLIN;
  52. }
  53. //将serverSocket放进去
  54. fds[0].fd = serverSocket;
  55. fds[0].events = POLLIN; //监视是否有数据可读
  56. fdNum++;
  57. while(1){
  58. r = poll(fds,fdNum,10); //延时10ms
  59. if(-1 == r){
  60. printf("监视失败:%m\n"),close(serverSocket),exit(-1);
  61. }else if(0 == r){
  62. continue;
  63. }else{
  64. //有动静
  65. if(fds[0].revents & POLLIN){
  66. //有客户端连接服务器的动作
  67. cfd = accept(serverSocket,(struct sockaddr*)&cAddr,&len);
  68. if(-1 == cfd){
  69. printf("server Error!\n");
  70. continue;
  71. }else{
  72. printf("客户端%d -- %s连接上服务器了!\n",cfd,inet_ntoa(cAddr.sin_addr));
  73. }
  74. //将客户端的描述符号添加到监视数组中
  75. for(int i = 1; i < NUM; i++){
  76. if(-1 == fds[fdNum].fd){
  77. fds[fdNum].fd = cfd;
  78. fds[fdNum].events = POLLIN;
  79. fdNum++;
  80. break;
  81. }
  82. }
  83. }
  84. }
  85. //检查客户端动静,看是否有数据发送过来
  86. for(int i = 1; i < NUM; i++){
  87. if(-1 != fds[i].fd && (fds[i].revents & POLLIN)){
  88. r = recv(fds[i].fd,buff,1023,0);
  89. if(r > 0){
  90. buff[r] = 0;
  91. printf("%d >> %s\n",fds[i].fd,buff);
  92. }else{
  93. //客户端掉线了
  94. fds[i].fd = -1;
  95. fdNum--;
  96. }
  97. }
  98. }
  99. }
  100. return 0;
  101. }

 客户(client)端

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <arpa/inet.h>
  7. #include <string.h>
  8. #include <signal.h>
  9. int clientSocket;
  10. void hand(int val){
  11. //5. 关闭连接
  12. close(clientSocket);
  13. printf("bye bye!\n");
  14. exit(0);
  15. }
  16. int main(int argc,char* argv[]){
  17. if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
  18. printf("ip: %s port:%d\n",argv[1],atoi(argv[2]));
  19. signal(SIGINT,hand);
  20. //1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
  21. clientSocket = socket(AF_INET,SOCK_STREAM,0);
  22. if(-1 == clientSocket) printf("创建socket失败:%m\n"),exit(-1);
  23. printf("创建socket成功!\n");
  24. //2. 创建服务器协议地址簇
  25. struct sockaddr_in cAddr = { 0 };
  26. cAddr.sin_family = AF_INET;
  27. cAddr.sin_addr.s_addr = inet_addr(argv[1]); //将字符串转整数
  28. cAddr.sin_port = htons(atoi(argv[2])); //将字符串转整数,再将小端转换成大端
  29. //3.连接服务器
  30. int r = connect(clientSocket,(struct sockaddr*)&cAddr,sizeof cAddr);
  31. if(-1 == r) printf("连接服务器失败:%m\n"),close(clientSocket),exit(-2);
  32. printf("连接服务器成功!\n");
  33. //4. 通信
  34. char buff[256] = {0};
  35. while(1){
  36. printf("你想要发送:");
  37. scanf("%s",buff);
  38. send(clientSocket,buff,strlen(buff),0);
  39. }
  40. return 0;
  41. }

2. epoll

2.1 epoll相对于poll的优势

  • select与poll没有太大的区别, 均是轮循fd监视实现, 因此效率受描述符号数量影响(监视的fd越多,速率越慢) --- 就相当于一直盯着手机看是否有人打电话
  • epoll是优化后的poll,通过注册事件实现, 不需要再轮循监视,因此效率不受监视的描述符号数量影响 --- 就相当于设置了来电提示看是否有人打电话

2.2 epoll编程模型

  1. 创建epoll                   epoll_create

  2. 注册描述符号事件     epoll_ctl

  3. 等待,挨个处理事件    epoll_wait 检查 使用&EPOLLIN(与poll相同)

2.3 epoll函数原型

  1. //需要的头文件
  2. #include <sys/epoll.h>
  3. int epoll_create(int size); //数量
  4. int epoll_ctl(int epfd, //epoll_create的返回值,处理过的文件描述符
  5. int op, //操作方式
  6. int fd, //需要监视的文件描述符
  7. struct epoll_event *event); //监视的描述符号链表表头地址(或者数组首地址)
  8. //参数二op
  9. EPOLL_CTL_ADD //添加
  10. EPOLL_CTL_MOD //修改
  11. EPOLL_CTL_DEL //删除
  12. int epoll_wait(int epfd, //epoll_create的返回值,处理过的文件描述符
  13. struct epoll_event *events, //监视的描述符号链表表头地址(或者数组首地址)
  14. int maxevents, //事件结构体数量(第二个参数的数量)
  15. int timeout); //延时
  1. //使用的数据类型
  2. typedef union epoll_data{
  3. void *ptr;
  4. int fd;
  5. uint32_t u32;
  6. uint64_t u64;
  7. } epoll_data_t;
  8. struct epoll_event{
  9. uint32_t events;
  10. epoll_data_t data;
  11. };

2.4 epoll实现多个(客户)client端连接(服务器)server端

服务器(server)端

  1. //服务器端
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <string.h>
  9. #include <signal.h>
  10. #include <sys/epoll.h>
  11. //最多允许的客户端数量
  12. #define NUM 100
  13. int serverSocket;
  14. void hand(int val){
  15. close(serverSocket);
  16. printf("bye bye!\n");
  17. exit(0);
  18. }
  19. int main(int argc,char* argv[]){
  20. if(argc != 3) printf("请输入ip地址和端口号!\n"),exit(0);
  21. printf("ip: %s port:%d\n",argv[1],atoi(argv[2]));
  22. signal(SIGINT,hand);
  23. //1. 创建socket 参数一: 协议类型(版本) 参数二: 通信媒介 参数三: 保护方式
  24. serverSocket = socket(AF_INET,SOCK_STREAM,0);
  25. if(-1 == serverSocket) printf("创建socket失败:%m\n"),exit(-1);
  26. printf("创建socket成功!\n");
  27. //2. 创建服务器协议地址簇
  28. struct sockaddr_in sAddr = { 0 };
  29. sAddr.sin_family = AF_INET; //协议类型 和socket函数第一个参数一致
  30. sAddr.sin_addr.s_addr = inet_addr(argv[1]); //将字符串转整数
  31. sAddr.sin_port = htons(atoi(argv[2])); //将字符串转整数,再将小端转换成大端
  32. //3. 绑定服务器协议地址簇
  33. int r = bind(serverSocket,(struct sockaddr*)&sAddr,sizeof sAddr);
  34. if(-1 == r) printf("绑定失败:%m\n"),close(serverSocket),exit(-2);
  35. printf("绑定成功!\n");
  36. //4. 监听
  37. r = listen(serverSocket,10); //数量
  38. if(-1 == r) printf("监听失败:%m\n"),close(serverSocket),exit(-3);
  39. printf("监听成功!\n");
  40. //监视serverSocket和ClientSocket
  41. struct sockaddr_in cAddr = {0};
  42. int len = sizeof(cAddr);
  43. int cfd; //临时保存一个客户端fd
  44. char buff[1024];
  45. int fdNum = 0;
  46. //创建epoll 参数可缺省(只需设置最大描述符号即可)
  47. int epfd = epoll_create(NUM);
  48. if(-1 == epfd){
  49. printf("创建epoll失败:%m\n");
  50. close(serverSocket);
  51. exit(-1);
  52. }
  53. printf("创建epoll成功!\n");
  54. //注册描述符号事件
  55. struct epoll_event ev; //注册时使用
  56. struct epoll_event events[NUM]; //等待处理事件时使用
  57. ev.events = EPOLLIN;
  58. ev.data.fd = serverSocket;
  59. r = epoll_ctl(epfd,EPOLL_CTL_ADD,serverSocket,&ev);
  60. if(-1 == r){
  61. printf("注册serverSocket事件失败:%m\n");
  62. close(epfd);
  63. close(serverSocket);
  64. exit(-2);
  65. }
  66. printf("注册serverSocket事件成功!\n");
  67. //等待,挨个处理事件
  68. //epoll_wait 成功返回有动静的描述符号数量
  69. int curCfd; //用于保存当前发数据的客户端epoll_wait的返回值
  70. int nfds; //用于保存epoll_wait的返回值
  71. while(1){
  72. nfds = epoll_wait(epfd,events,NUM,1000); //延时1000ms
  73. if(nfds < 0){
  74. printf("epoll_wait失败:%m\n");
  75. close(epfd);
  76. close(serverSocket);
  77. exit(-3);
  78. }else if(0 == nfds){
  79. //没有动静
  80. continue;
  81. }else{
  82. //有动静
  83. printf("有动静发生----\n");
  84. for(int i = 0;i < nfds; i++){
  85. if(serverSocket == events[i].data.fd){
  86. cfd = accept(serverSocket,(struct sockaddr*)&cAddr,&len);
  87. if(-1 == cfd){
  88. printf("服务器崩溃:%m\n");
  89. close(epfd);
  90. close(serverSocket);
  91. exit(-4);
  92. }
  93. printf("有客户端%d连接上服务器了:%s\n",cfd,
  94. inet_ntoa(cAddr.sin_addr));
  95. //注册客户端描述符号事件
  96. ev.events = EPOLLIN;
  97. ev.data.fd = cfd;
  98. r = epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
  99. if(-1 == r){
  100. printf("注册clientSocket失败:%m\n");
  101. close(epfd);
  102. close(serverSocket);
  103. exit(-5);
  104. }
  105. printf("注册clientSocket事件成功!\n");
  106. }else if(events[i].events && EPOLLIN){
  107. //有客户端发数据过来
  108. curCfd = events[i].data.fd;
  109. r = recv(curCfd,buff,1023,0);
  110. if(r > 0){
  111. buff[r] = 0;
  112. printf("%d >> %s\n",curCfd,buff);
  113. }
  114. }
  115. }
  116. }
  117. }
  118. return 0;
  119. }

客户(client)端  (与上方poll代码相同)

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/489764
推荐阅读
相关标签
  

闽ICP备14008679号