linux网络编程(五)epoll进阶

2023-10-31

1. 事件模型

EPOLL事件有两种模型:
Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。
Light Triggered (LT) 水平触发只要有数据都会触发。

思考如下步骤:
1.假定我们已经把一个用来从管道中读取数据的文件描述符(rfd)添加到epoll描述符。
2.管道的另一端写入了2KB的数据
3.调用epoll_wait,并且它会返回rfd,说明它已经准备好读取操作
4.读取1KB的数据
5.调用epoll_wait…… 在这个过程中,有两种工作模式:

1.1 ET模式

ET模式即Edge Triggered工作模式。

如果我们在第1步将rfd添加到epoll描述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候ET工作模式才会汇报事件。因此在第5步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。最好以下面的方式调用ET模式的epoll接口,在后面会介绍避免可能的缺陷。

1)基于非阻塞文件句柄
2)只有当read或者write返回EAGAIN(非阻塞读,暂时无数据)时才需要挂起、等待。但这并不是说每次read时都需要循环读,直到读到产生一个EAGAIN才认为此次事件处理完成,当read返回的读到的数据长度小于请求的数据长度时,就可以确定此时缓冲中已没有数据了,也就可以认为此事读事件已处理完成。

1.2 LT模式

LT模式即Level Triggered工作模式。

与ET模式不同的是,以LT方式调用epoll接口的时候,它就相当于一个速度比较快的poll,无论后面的数据是否被使用。

1.3 比较

LT(level triggered):LT是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。

ET(edge-triggered):ET是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once).

2. 实例一

基于管道epoll ET触发模式

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>

#define MAXLINE 10

int main(int argc, char *argv[])
{
	int efd, i;
	int pfd[2];
	pid_t pid;
	char buf[MAXLINE], ch = 'a';

	pipe(pfd);
	pid = fork();
	if (pid == 0) {
		close(pfd[0]);
		while (1) {
			for (i = 0; i < MAXLINE/2; i++)
				buf[i] = ch;
			buf[i-1] = '\n';
			ch++;

			for (; i < MAXLINE; i++)
				buf[i] = ch;
			buf[i-1] = '\n';
			ch++;

			write(pfd[1], buf, sizeof(buf));
			sleep(2);
		}
		close(pfd[1]);
	} else if (pid > 0) {
		struct epoll_event event;
		struct epoll_event resevent[10];
		int res, len;
		close(pfd[1]);

		efd = epoll_create(10);
		/* event.events = EPOLLIN; */
		event.events = EPOLLIN | EPOLLET;		/* ET 边沿触发 ,默认是水平触发 */
		event.data.fd = pfd[0];
	epoll_ctl(efd, EPOLL_CTL_ADD, pfd[0], &event);

		while (1) {
			res = epoll_wait(efd, resevent, 10, -1);
			printf("res %d\n", res);
			if (resevent[0].data.fd == pfd[0]) {
				len = read(pfd[0], buf, MAXLINE/2);
				write(STDOUT_FILENO, buf, len);
			}
		}
		close(pfd[0]);
		close(efd);
	} else {
		perror("fork");
		exit(-1);
	}
	return 0;
}

3. 实例二

基于网络C/S模型的epoll ET触发模式

server
/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>

#define MAXLINE 10
#define SERV_PORT 8080

int main(void)
{
	struct sockaddr_in servaddr, cliaddr;
	socklen_t cliaddr_len;
	int listenfd, connfd;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];
	int i, efd;

	listenfd = socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	listen(listenfd, 20);

	struct epoll_event event;
	struct epoll_event resevent[10];
	int res, len;
	efd = epoll_create(10);
	event.events = EPOLLIN | EPOLLET;		/* ET 边沿触发 ,默认是水平触发 */

	printf("Accepting connections ...\n");
	cliaddr_len = sizeof(cliaddr);
	connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
	printf("received from %s at PORT %d\n",
			inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
			ntohs(cliaddr.sin_port));

	event.data.fd = connfd;
	epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);

	while (1) {
		res = epoll_wait(efd, resevent, 10, -1);
		printf("res %d\n", res);
		if (resevent[0].data.fd == connfd) {
			len = read(connfd, buf, MAXLINE/2);
			write(STDOUT_FILENO, buf, len);
		}
	}
	return 0;
}
client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>

#define MAXLINE 10
#define SERV_PORT 8080

int main(int argc, char *argv[])
{
	struct sockaddr_in servaddr;
	char buf[MAXLINE];
	int sockfd, i;
	char ch = 'a';

	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
	servaddr.sin_port = htons(SERV_PORT);

	connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	while (1) {
		for (i = 0; i < MAXLINE/2; i++)
			buf[i] = ch;
		buf[i-1] = '\n';
		ch++;

		for (; i < MAXLINE; i++)
			buf[i] = ch;
		buf[i-1] = '\n';
		ch++;

		write(sockfd, buf, sizeof(buf));
		sleep(10);
	}
	Close(sockfd);
	return 0;
}

4.实例三

基于网络C/S非阻塞模型的epoll ET触发模式

server
/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>

#define MAXLINE 10
#define SERV_PORT 8080

int main(void)
{
	struct sockaddr_in servaddr, cliaddr;
	socklen_t cliaddr_len;
	int listenfd, connfd;
	char buf[MAXLINE];
	char str[INET_ADDRSTRLEN];
	int i, efd, flag;

	listenfd = socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	listen(listenfd, 20);

	struct epoll_event event;
	struct epoll_event resevent[10];
	int res, len;
	efd = epoll_create(10);
	/* event.events = EPOLLIN; */
	event.events = EPOLLIN | EPOLLET;		/* ET 边沿触发 ,默认是水平触发 */

	printf("Accepting connections ...\n");
	cliaddr_len = sizeof(cliaddr);
	connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
	printf("received from %s at PORT %d\n",
			inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
			ntohs(cliaddr.sin_port));

	flag = fcntl(connfd, F_GETFL);
	flag |= O_NONBLOCK;
	fcntl(connfd, F_SETFL, flag);
	event.data.fd = connfd;
	epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event);

	while (1) {
		printf("epoll_wait begin\n");
		res = epoll_wait(efd, resevent, 10, -1);
		printf("epoll_wait end res %d\n", res);

		if (resevent[0].data.fd == connfd) {
			while ((len = read(connfd, buf, MAXLINE/2)) > 0)
				write(STDOUT_FILENO, buf, len);
		}
	}
	return 0;
}
client
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>

#define MAXLINE 10
#define SERV_PORT 8080

int main(int argc, char *argv[])
{
	struct sockaddr_in servaddr;
	char buf[MAXLINE];
	int sockfd, i;
	char ch = 'a';

	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
	servaddr.sin_port = htons(SERV_PORT);

	connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	while (1) {
		for (i = 0; i < MAXLINE/2; i++)
			buf[i] = ch;
		buf[i-1] = '\n';
		ch++;

		for (; i < MAXLINE; i++)
			buf[i] = ch;
		buf[i-1] = '\n';
		ch++;

		write(sockfd, buf, sizeof(buf));
		sleep(10);
	}
	Close(sockfd);
	return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux网络编程(五)epoll进阶 的相关文章

随机推荐

  • ElasticSearch 实践过程中遇到的几个小问题

    ulimit 不生效 有一台机器的在启动 ES 的时候始终报错 1 max file descriptors 65000 for elasticsearch process is too low 但是我已经在 etc security li
  • git使用-去除merge branch ’master‘提交

    git去除merge branch master 提交 问题现象 在项目开发中 经常会有这样的情况发生 更新上游项目代码时操作 Tom localhost dev gw ac git remote add upstream http Tom
  • 关于百度地图marker的点击事件

    在最开始学习使用百度地图 marker的点击事件 很容易实现点击弹出框 像这样 baiduMap setOnMarkerClickListener new BaiduMap OnMarkerClickListener Override pu
  • 关于MinGW和MSYS

    MinGW是Minimalist GNU for Windows的缩写 是本地Windows应用的极简开发环境 MinGW为本地MS Windows应用的开发提供了完整的开源编程工具集 而且不依赖于第三方C运行时DLL 它确实依赖于很多由微
  • gitee新建仓库并上传项目

    目录 一 新建仓库 二 克隆仓库到本地 三 本地添加项目文件 四 上传至gitee仓库 五 提交代码 更新至远程仓库 一 新建仓库 1 点击右上角的 号 新建仓库 2 填写仓库信息 点击创建即可 二 克隆仓库到本地 1 点击 克隆 下载 S
  • python 用随机森林模型补充数值变量缺失值

    对数据建模之前 填补缺失值是必不可少的一步 这里把用随机森林模型快速预测缺失值的方法总结如下 以方便日后的工作 data df DataFrame类型的数据 obj column 待填补缺失值的列名 missing other column
  • Android开发 亲测可用--多种方式获取手机短信验证码自动填入

    Android开发 静态注册 动态注册 短信中心库监控获取手机验证码 自动复制到剪切板或或填入输入框 友情提醒初学者 这是广播接收器的类 写在xml中静态注册或写在启动类的Oncreate方法下动态注册即可 有新短信通知就会触发 若使用正常
  • Latex常用数学符号

    相关参考 常用数学符号的 LaTeX 表示方法 Latex所有常用数学符号整理 CSDN latex插入图片的文章 Side by side figures in LaTex 可以复制的特殊符号表 数学模式重音符号 hat a hat a
  • 电路分析笔记-电阻电路的等效变换

    电路的等效变换 两端网络 网络 任何一个复杂的电路 向外引出两个端钮 且从一个端子流入的电流等于从另一端子流出的电流 则称这一电路为二端网络 或一端口网络 两端电路等效的概念 两个两端电路 端口具有相同的电压 电流关系 则称它们是等效的电路
  • T5的整体介绍【代码实战】

    T5的整体介绍 代码实战 0 前言 1 Header 2 summary 3 T5 model 3 1 forward 3 2 预训练任务 3 2 1 multi sentence pairs 3 3 完成 tasks 0 前言 本文是对T
  • python语法(高阶)-工厂模式

    参考内容 黑马程序员
  • 李宏毅 机器学习2020 作业5 hw5-explainAI

    本次作业做的是解释机器学习是如何识别一张图片的 用到了上一次CNN作业训练好的模型 先导入需要用得到的库 其中lime需要提前安装 我在安装的时候 conda install命令不能使用 但是使用pip命令就可以安装了 import os
  • 关于代码的注释的几种方法

    注释就是对代码的解释和说明 其目的是让人们能够更加轻松地了解代码 注释是编写程序时 写程序的人给一个语句 程序段 函数等的解释或提示 能提高程序代码的可读性 注释只是为了提高可读性 不会被计算机编译 注释一般分为行注释和块注释 行注释是在某
  • 那些上线MES系统的企业都怎么样了?

    MES系统即制造企业生产过程执行系统 是一套面向制造企业车间执行层的生产信息化管理系统 在智能制造 工业4 0的时代背景下 MES生产管理系统成为制造业纷纷上线的管理软件 旨在提高企业整体生产效率和降低成本 那么企业上线MES系统后的具体有
  • 开源情报分析(OSINT)CTF社工类2万字题详细教程,请不要利用本文章做不道德的事,后果概不负责

    简介 现在国内外最新的ctf比赛都有这个项目了 列如给你一个照片找地址或者人名 给你一个名字找他的社交账号什么的 考验选手的信息收集与社工能力 这篇文章对这类题型做一个基础的总结 以后遇到这种题型就知道该怎么做了 本文章会教你们关于查找有关
  • Protobuf(Protocol Buffers)超详细入门教程(跨平台序列化, Java)——更新于2022.01

    目录 相关教程 相关文献 安装 C Installation Unix 环境 Protobuf3 0基础上手例子 相关教程 Protobuf Protocol Buffers 超详细入门教程 跨平台序列化 C CMake 更新于2022 0
  • 经典的图像去噪算法_NeuNet2020:BRDNet(开源)使用深度CNN和批量归一化进行图像去噪...

    最近哈工大的研究团队于2020年在国际著名的人工智能杂志 神经网络 上发表了 利用深度CNN进行批量重正化的图像去噪 作者信息 在论文中 作者提出了一种新的图像去噪网络 称为批重归一化去噪网络 BRDNet 可以解决硬件资源受限条件处理数据
  • 开发板的上电锁存

    1 开发板的软启动 开发板的电源开关用两种 1 拨码开关 打到一侧则接通 打到另一侧则关闭 开发板只有关闭和运行两种状态 电路设计简单 但是不能实现休眠模式 低功耗模式 2 按键 按下时通电 弹起时断电 但是我们可以给供电电路增加置锁电路
  • windbg双机调试的连接设置和常见问题(真机和VMware虚拟机间的连接)

    一些废话 在做内核开发的时候 windbg给我们提供了方便 但是windbg要求内核调试是双机调试 所以我们首先面对的是调试机和被调试机的连接问题 相信大部分开发人员都是用实体机开发好代码 放到虚拟机中运行 并在实体机中运行windbg对虚
  • linux网络编程(五)epoll进阶

    文章目录 1 事件模型 1 1 ET模式 1 2 LT模式 1 3 比较 2 实例一 3 实例二 4 实例三 1 事件模型 EPOLL事件有两种模型 Edge Triggered ET 边缘触发只有数据到来才触发 不管缓存区中是否还有数据