当前位置: 首页 > news >正文

学习伊圣雨老师的 epoll 编程:select是水平触发模式,回声客户端代码,epoll 服务器端,验证默认的水平触发模式,采用边缘触发模式

(1)书里提出了疑问,epoll 函数的工作方式,区分为水平触发与边缘触发 :

在这里插入图片描述

(2) 11 1 5-5 伊圣雨老师的 epoll 教学范例:回声客户端代码与错误处理函数 error_handling () :

在这里插入图片描述

++ 源代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BUF_SIZE   100void error_handling(char * str)//此函具有换行功能
{   // int fputs(const char *str,FILE *stream);fputs(str,stderr); // fputs()写入字符串到文件中fputc('\n',stderr);//     但不会主动添加换行符。exit(1); // int fputc(int char,FILE *stream);
}int main(int argc,char * argv[])
{   // 回声客户端,三个参数,argc = 3int  sock, str_len;    char message[BUF_SIZE];struct sockaddr_in  serv_adr;if(argc != 3) { printf("参数不是3个\n");exit(1); }sock = socket(PF_INET,SOCK_STREAM,0);if(sock == -1)error_handling("创建套接字socket() 失败\n");memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family = AF_INET;// serv_adr.sin_addr.s_addr = inet_addr(argv[1]);// inet_addr() 的语义不明,不好// 处理文本地址,只需使用 inet_pton() 与 inet_ntop() 即可。// int inet_pton(int af, const char *src, void *dst);inet_pton(AF_INET, argv[1], &serv_adr.sin_addr.s_addr);serv_adr.sin_port = htons(atoi(argv[2]));if(connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)error_handling("connect() 失败");elseputs("客户端套接字连接至服务器成功\n");while(1) // 函 fputs() 不会自动添加换行符{   // int fputs(const char *str,FILE *stream);fputs("Input message(Q to quit):", stdout);fgets(message, BUF_SIZE, stdin);// char *fgets(char *str, int n, FILE *stream);// 函 fgets 会保留换行符在字符串中。可手动去除if( !strcmp(message,"Q\n") || !strcmp(message,"q\n") )break;write(sock, message, strlen(message));str_len = read(sock, message, BUF_SIZE - 1);message[str_len] = 0; // read()不会主动添加空字符。printf("服务器端回响过来的信息:%s",message);}close(sock);return 0;}

(3) 11 2 5-5 伊圣雨老师的 epoll 教学范例:回声服务器端代码:使用 epoll ,大缓存,水平触发:

在这里插入图片描述

++

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>#define BUF_SIZE   100
#define EPOLL_SIZE  50void error_handling(char * str)//此函具有换行功能
{   // int fputs(const char *str,FILE *stream);fputs(str,stderr);fputc('\n',stderr);// int fputc(int char,FILE *stream);exit(1);
}int main(int argc,char * argv[])
{   // 最基本版本的 epoll() 实现的回声服务器端,argc = 2int serv_sock, clnt_sock, str_len, i, epfd, event_cnt;struct sockaddr_in serv_adr,clnt_adr;socklen_t adr_sz;    char buf[BUF_SIZE]; // 100struct epoll_event event, * ep_events;if(argc != 2) { printf("参数不是2个\n");exit(1); }serv_sock = socket(PF_INET,SOCK_STREAM,0);memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family = AF_INET; // 协议serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址serv_adr.sin_port = htons(atoi(argv[1])); //端口号if(bind(serv_sock,(struct sockaddr *)&serv_adr, sizeof(serv_adr))==-1)error_handling("bind() error"); // 不必再处理字符串换行问题if(listen(serv_sock,5)==-1)    // 绑定,开启监听error_handling("listen() error");epfd = epoll_create(EPOLL_SIZE); // EPOLL_SIZE = 50event.events  = EPOLLIN;event.data.fd = serv_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);ep_events = malloc(sizeof(struct epoll_event) * EPOLL_SIZE);while (1) // 此循环在正常情况下是不会退出的。{   event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);       if(-1 == event_cnt) { // 防止少写 ==puts("epoll_wait() 出错"); // 结束循环,进程退出break; //puts() 会自动换行}for(i = 0 ; i < event_cnt ; i++){   if(ep_events[i].data.fd == serv_sock)//监听套接字{   adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock,(struct sockaddr *)&clnt_adr,&adr_sz);event.events  = EPOLLIN;event.data.fd = clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event); printf("创建了通信套接字 id: %d\n", clnt_sock);} else {   // read() 是不会为接收的字符串添加空字符 '\0' 的str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);if(0 == str_len) // FIN 报文{   epoll_ctl(epfd, EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);close(ep_events[i].data.fd);printf("关闭了通信套接字 id: %d\n",ep_events[i].data.fd);}else   write(ep_events[i].data.fd, buf, str_len);}} // for(...)  } // while(...)close(serv_sock); // 如此,监听套接字 close(epfd); //serv_sock 会被关闭两次。return 0;}

++ 11 3 5-5 伊圣雨老师的 epoll 教学范例:图 2 的回声服务器的测试效果 :

在这里插入图片描述

(4) 11 4 5-5 伊圣雨老师的 epoll 教学范例:回声服务器端代码:使用 epoll ,小缓存,验证默认的水平触发模式:

在这里插入图片描述

++

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>#define BUF_SIZE     3 
#define EPOLL_SIZE  50void error_handling(char * str)//此函具有换行功能
{   // int fputs(const char *str,FILE *stream);fputs(str,stderr);fputc('\n',stderr);// int fputc(int char,FILE *stream);exit(1);
}int main(int argc,char * argv[])
{   //验证 epoll 的默认的水平触发模式的回声服务器端,argc = 2int serv_sock, clnt_sock, str_len, i, epfd, event_cnt;struct sockaddr_in serv_adr,clnt_adr;socklen_t adr_sz;    char buf[BUF_SIZE]; // 100struct epoll_event event, * ep_events;if(argc != 2) { printf("参数不是2个\n");exit(1); }serv_sock = socket(PF_INET,SOCK_STREAM,0);memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family = AF_INET; // 协议serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址serv_adr.sin_port = htons(atoi(argv[1])); //端口号if(bind(serv_sock,(struct sockaddr *)&serv_adr, sizeof(serv_adr))==-1)error_handling("bind() error"); // 不必再处理字符串换行问题if(listen(serv_sock,5)==-1)    // 绑定,开启监听error_handling("listen() error");epfd = epoll_create(EPOLL_SIZE); // EPOLL_SIZE = 50event.events  = EPOLLIN;event.data.fd = serv_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);ep_events = malloc(sizeof(struct epoll_event) * EPOLL_SIZE);while (1) // 此循环在正常情况下是不会退出的。{   event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);       if(-1 == event_cnt) { // 防止少写 ==puts("epoll_wait() 出错"); // 结束循环,进程退出break; //puts() 会自动换行} //增加 puts() 行统计 epoll_wait() 的返回次数,其余部分不变。puts("从 epoll_wait() 返回了");for(i = 0 ; i < event_cnt ; i++){   if(ep_events[i].data.fd == serv_sock)//监听套接字{   adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock,(struct sockaddr *)&clnt_adr,&adr_sz);event.events  = EPOLLIN;event.data.fd = clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event); printf("创建了通信套接字 id: %d\n", clnt_sock);} else {   // read() 是不会为接收的字符串添加空字符 '\0' 的str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);if(0 == str_len) // FIN 报文{   epoll_ctl(epfd, EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);close(ep_events[i].data.fd);printf("关闭了通信套接字 id: %d\n",ep_events[i].data.fd);} else   write(ep_events[i].data.fd, buf, str_len);}} // for(...)  } // while(...)close(serv_sock); // 如此,监听套接字 close(epfd); //serv_sock 会被关闭两次。return 0;}

++ 11 5 5-5 伊圣雨老师的 epoll 教学范例:图 4 的回声服务器的默认水平触发模式的测试效果:

在这里插入图片描述

(5) 11 6 5-5 伊圣雨老师的 epoll 教学范例:回声服务器端代码:使用 epoll ,小缓存,通信套接字使用非阻塞的边缘触发模式:

在这里插入图片描述

++

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h> //增加这俩头文件
#include <errno.h>#define BUF_SIZE     3 
#define EPOLL_SIZE  50void error_handling(char * str)//此函具有换行功能
{   // int fputs(const char *str,FILE *stream);fputs(str,stderr);fputc('\n',stderr);// int fputc(int char,FILE *stream);exit(1);
}void setnonblockingmode(int fd) //非阻塞
{   int flag = fcntl(fd, F_GETFL, 0);fcntl(fd, F_SETFL, flag | O_NONBLOCK); }int main(int argc,char * argv[])
{   //验证 epoll 的边缘触发模式的回声服务器端,argc = 2int serv_sock, clnt_sock, str_len, i, epfd, event_cnt;struct sockaddr_in serv_adr,clnt_adr;socklen_t adr_sz;    char buf[BUF_SIZE]; // 100struct epoll_event event, * ep_events;if(argc != 2) { printf("参数不是2个\n");exit(1); }serv_sock = socket(PF_INET,SOCK_STREAM,0);setnonblockingmode(serv_sock);//设置监听套接字为非阻塞memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family = AF_INET; // 协议serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址serv_adr.sin_port = htons(atoi(argv[1])); //端口号if(bind(serv_sock,(struct sockaddr *)&serv_adr, sizeof(serv_adr))==-1)error_handling("bind() error"); // 不必再处理字符串换行问题if(listen(serv_sock,5)==-1)    // 绑定,开启监听error_handling("listen() error");epfd = epoll_create(EPOLL_SIZE); // EPOLL_SIZE = 50event.events  = EPOLLIN; // 监听套接字仍为水平触发模式event.data.fd = serv_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);ep_events = malloc(sizeof(struct epoll_event) * EPOLL_SIZE);while (1) // 此循环在正常情况下是不会退出的。{   event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);       if(-1 == event_cnt) { // 防止少写 ==puts("epoll_wait() 出错"); // 结束循环,进程退出break;  //puts() 会自动换行 }       puts("从 epoll_wait() 返回了");//统计epoll_wait()的返回次数for(i = 0 ; i < event_cnt ; i++) //依次处理所有发生了事件的套接字{   if(ep_events[i].data.fd == serv_sock)//监听套接字{   adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock,(struct sockaddr *)&clnt_adr,&adr_sz);setnonblockingmode(clnt_sock);event.events  = EPOLLIN | EPOLLET; //通信套接字边缘触发event.data.fd = clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event); printf("创建了通信套接字 id: %d\n", clnt_sock);} else while(1) {   // 从通信套接字读取所有数据str_len = read(ep_events[i].data.fd, buf, BUF_SIZE);if(0 == str_len) // FIN 报文{   epoll_ctl(epfd, EPOLL_CTL_DEL,ep_events[i].data.fd,NULL);close(ep_events[i].data.fd);printf("关闭了通信套接字 id: %d\n",ep_events[i].data.fd);break;  // 下面的是读完了接收缓存中的数据,返回-1,并设置errno} else if(str_len < 0){ if(EAGAIN == errno) break; } //跳出内循环else   write(ep_events[i].data.fd, buf, str_len);} //内层 while(...)} // for(...)  } // while(...)close(serv_sock); // 如此,监听套接字 close(epfd); //serv_sock 会被关闭两次。return 0;}

++11 7 5-5 伊圣雨老师的 epoll 教学范例:图 6 的回声服务器的非阻塞边缘触发模式的测试效果:

在这里插入图片描述

(6)

谢谢


http://www.mrgr.cn/news/62404.html

相关文章:

  • Dexcap复现代码数据预处理全流程(四)——demo_clipping_3d.py
  • Linux-Ubuntu之SPI串行通信陀螺仪和加速度计
  • Ruby语言的软件开发工具
  • Android Studio 警告信息:Use start instead of left to ensure...
  • HarmonyOS中实现上拉加载下拉刷新
  • spring mvc源码学习笔记之八
  • 5G基础知识
  • 真题与解析 202209一级 青少年软件编程(Python)考级
  • 设备管理系统中的故障率监控与维修周期优化
  • 浅析HTTP协议
  • 静态数据加密:确保数据在存储中的安全性
  • Postman断言与依赖接口测试详解
  • 数据治理,数据提取,大数据中心建设,大数据治理总体解决方案书(word,ppt原件)
  • 人工智能算法之A*搜索算法
  • 【Docker知识】Docker进阶-容器镜像深度解读
  • AIGC学习笔记(4)——AI大模型开发工程师
  • 高效集成:SQLServer对接MySQL的实战案例
  • NR 5G 系统信息深度解析
  • 使用python提取日志里面的role_id、vip字段的值,(vip字段可能为空或者缺失,此时需要给默认值0):
  • 个人在ssm框架整合时犯的错误
  • 只尊重不教育,只筛选不改变
  • Threejs渲染3D字体介绍
  • gradio RuntimeError: async generator raised StopAsyncIteration
  • 阿里巴巴API返回值全解析:轻松掌握1688店铺商品信息
  • 【系统设计】高效的分布式系统:使用 Spring Boot 和 Kafka 实现 Saga 模式
  • SAP ABAP开发学习——第一代增强(包含增强演示)