libevent多线程使用事项

2023-11-12

  在linux平台上使用c开发网络程序的同志们一般情况下都对鼎鼎大名的libevent非常的熟悉了。但是一些新进入此领域的new new people们对此都是一头雾水。原本的迷茫再加上开源软件一贯的“帮助文件”缺失作风,让我们这些新手们显的非常的无助。幸好有一些热心的朋友们帮忙,才化险为夷啊!

    前几天一直在开发一个locker server,虽然公司现有的locker server能很好的运转,但是毕竟是net的,通用性不广,当我们要在linux上开发多集群系统的时候现有的locker server就未免显得有点捉襟见肘了。正是在开发locker server的过程中使用到了libevent。

    总体上,libevent是很好用的。一两个函数就能搞定你复杂的网络通讯工作了。当然了,这句话得用在你使用的是“单线程”的情况下。虽然在linux系统中,进程的资源和window系统中进程的资源相比轻量级很多,代价也相当的没有那么昂贵,所以很多的软件都是使用“多进程”方式实现的,比如大名鼎鼎的apache。但是在我们的系统中,我们使用了“单进程多线程”的方式,这样,我们就能在单机上启动多个进程,以达到“伪分布式”的效果来达到测试的目的。

      那么这个时候就要注意libevent的使用了,因为对于event_base来说,不是线程安全的。也就是说多线程不能share同一个event_base,就算是加锁操作也不行。那么这个时候就只能采取“单线程单event_base”的策略了。我的做法是做一个task pool(任务对象池),每个任务会被一个thread执行,当然了,thread肯定也是从thread pool拿出来的,而在task pool初始化的时候,我就给每个task中的event_base初始化了对象,这样,万事大吉了。

      这个地方注意了以后,就开始说网络通讯了。在使用libevent的时候,触发事件是在接收到网络连接(或者timeout事件超时)的时候。所以你需要在事件处理函数中判断时间源,其次libevent接收网络通讯的字节流时是使用了libevnet中自带的缓冲的,所以当你接收的时候一定要注意累加,并且多次loop或者注册 event_event中的事件。所以在我的task中,会有接收的data。当然了如果你的协议是分为header和body的,通常header比较短,body比较长,而且在client,header和body通常是连续发送的,这样,在使用libevent的时候,header和body是同时被接收到的,这点一定要注意,所以提醒你在接收数据的函数中,需要区分接收header部分还是body部分;当body非常长,超过libevent的缓冲时,是需要多次多次触发接收函数的,这点也要注意,就是让你需要在接收的时候除了区分header和body以外,还要注意一次接收不完全的情况下,对于数据需要累加。

      当你在使用libevent时,event_set事件时,只要不是使用EV_PERSIST注册的事件是不需要在接收完一次数据后多次event_add的,只有当你不使用EV_PERSIST时,你的事件才需要多次event_add到event_base中;当然了,使用了EV_PERSIST注册的函数在event_base被task pool回收时是要显式的event_del该注册事件的,没有使用EV_PERSIST注册的事件是不需要显式的使用event_del删除该事件的。

      

static void  read_buffer(int client_socket_fd,short event_type,void *arg)
{
	if(NULL == arg)
	{
		log_error("File:"__FILE__",Line:%d.event base arg is NULL.",__LINE__);
		return;
	}
	task_info_t *task_info = (task_info_t *) arg;

	if(event_type == EV_TIMEOUT)
	/*
	这个地方注意需要判断是否超时
	因为我event_add事件的时候没有使用ev_persist
	所以当超时时需要再add一次事件到event_base的loop中
	*/
	{
		if(0 != event_add(&task_info->on_read,&task_info->timeout))
		{
			log_error("File:"__FILE__",Line:%d.repeart add read header event to event_base is error.");
			close(task_info->on_read.ev_fd);
			task_pool_push(task_info);

		}
		return;
	}

	int bytes;
	/*
	这个地方就是开始接收头部
	接收头部时,可能分为好几次从缓冲中取得,所以需要一个while累加
	*/
	while(header == task_info->read_type)//recv header
	{
		bytes = recv(client_socket_fd,task_info->header_buffer+task_info->offset,REQUEST_LENGTH -task_info->offset,0);
		if(0 > bytes )
		{
			if (errno == EAGAIN || errno == EWOULDBLOCK)
			{
				if(0 != event_add(&task_info->on_read, &task_info->timeout))
				{
					close(task_info->on_read.ev_fd);
					task_pool_push(task_info);

					log_error("File: "__FILE__", line: %d, "\
						"event_add fail.", __LINE__);
					return;
				}
			}
			else
			{
				log_error("File: "__FILE__", line: %d,recv failed,errno: %d, error info: %s",
					__LINE__, errno, strerror(errno));

				close(task_info->on_read.ev_fd);
				task_pool_push(task_info);
			}
			return;
		}
		else if(0 == bytes)
		{
			log_warning("File:"__FILE__",Line:%d.recv buffer form network is error.disconnection the network.",
					__LINE__);
			close(task_info->on_read.ev_fd);
			task_pool_push(task_info);
			return;
		}

		if(REQUEST_LENGTH > bytes+task_info->offset)
		{
			log_warning("File:"__FILE__",Line:%d.recv header is not over.",__LINE__);
			task_info->offset += bytes;
			if(0 != event_add(&task_info->on_read, &task_info->timeout))
			{
				close(task_info->on_read.ev_fd);
				task_pool_push(task_info);
				log_error("File: "__FILE__", line: %d, "\
					"event_add fail.", __LINE__);
				return;
			}
		}
		else
		{
			task_info->read_type = body;
			deal_request_header(task_info);
			task_info->body_buffer = (char *) malloc(task_info->request_info.length);
			if(NULL == task_info->body_buffer)
			{
				log_error("File:"__FILE__",Line:%d.alloc mem to task_info data is error.",__LINE__);
				close(client_socket_fd);
				task_pool_push(task_info);
				return;
			}
			memset(task_info->body_buffer,0,task_info->request_info.length);
			task_info->offset = 0;//set recv body buffer offset to 0
			break;
		}
	}

	/*
	这个地方就是开始接收body,
	和header一样,也要考虑body多次接收累加的情况。
	*/
	while(body == task_info->read_type)
	{
		bytes = recv(client_socket_fd,task_info->body_buffer+task_info->offset,task_info->request_info.length-task_info->offset,0);
		if(0 > bytes )
		{
			if (errno == EAGAIN || errno == EWOULDBLOCK)
			{
				if(0 != event_add(&task_info->on_read, &task_info->timeout))
				{
					close(task_info->on_read.ev_fd);
					task_pool_push(task_info);

					log_error("File: "__FILE__", line: %d, "\
						"event_add fail.", __LINE__);
					return;
				}
			}
			else
			{
				log_error("File: "__FILE__", line: %d,recv failed,errno: %d, error info: %s",
					__LINE__, errno, strerror(errno));

				close(task_info->on_read.ev_fd);
				task_pool_push(task_info);
			}
			return;
		}
		else if(0 == bytes)
		{
			log_warning("File:"__FILE__",Line:%d.recv buffer form network is error.disconnection the network.",
					__LINE__);
			close(task_info->on_read.ev_fd);
			task_pool_push(task_info);
			return;
		}

		if(task_info->request_info.length-task_info->offset > bytes)
		{
			log_warning("File:"__FILE__",Line:%d.recv body is not over.",__LINE__);
			task_info->offset += bytes;
			if(0 != event_add(&task_info->on_read, &task_info->timeout))
			{
				close(task_info->on_read.ev_fd);
				task_pool_push(task_info);
				log_error("File: "__FILE__", line: %d, "\
					"event_add fail.", __LINE__);
				return;
			}
		}
		else
		{
			task_info->read_type = unspecified;
			break;
		}
	}
	deal_request_body(client_socket_fd,task_info);
	return;
}

 

void deal_working_thread(void *arg)
{
	log_info("debug to this.");
	int client_socket_fd = (int) arg;
	if(0 > client_socket_fd)
	{
		log_error("File:"__FILE__",Line:%d.the arg means client socket filedesc is less 0!",__LINE__);
		return;
	}
	/*
	设置网络为非阻塞,libevent必须的
	*/
	if(!set_nonblocking(client_socket_fd))
	{
		log_error("File:"__FILE__",Line:%d.set client socket filedesc is error.error info is %s!",
				__LINE__,strerror(errno));
		close(client_socket_fd);
		return;
	}

	task_info_t *task_info;
	task_info = task_pool_pop();
	/*
	对event_base注册事件回调函数,
	注意没有使用EV_PERSIST
	*/
	do
	{
		task_info->read_type = header;
		event_set(&task_info->on_read,client_socket_fd,EV_READ,read_buffer,(void *) task_info);
		if(0 != event_base_set(task_info->event_base,&task_info->on_read))
		{
			log_error("File:"__FILE__",Line:%d.Associate the read header event to  event_base is error.",__LINE__);
			task_info->read_type = unspecified;
			close(client_socket_fd);
			task_pool_push(task_info);
			break;
		}

		event_set(&task_info->on_write,client_socket_fd,EV_WRITE,response_handle,(void *) task_info);
		if(0 != event_base_set(task_info->event_base,&task_info->on_write))
		{
			log_error("File:"__FILE__",Line:%d.Associate the write hander to event_base is error.",__LINE__);
			task_info->read_type = unspecified;
			close(client_socket_fd);
			task_pool_push(task_info);
			break;
		}


		if(0 != event_add(&task_info->on_read,&task_info->timeout))
		{
			log_error("File:"__FILE__",Line:%d.add the read header event to  event_base is error.",__LINE__);
			task_info->read_type = unspecified;
			close(client_socket_fd);
			task_pool_push(task_info);
			break;
		}

		event_base_loop(task_info->event_base,EVLOOP_NONBLOCK);
	}while(false);
	return;
}



FROM: http://www.cnblogs.com/Seapeak/archive/2010/04/08/1707807.html

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

libevent多线程使用事项 的相关文章

  • Fedora的启动方式(命令行启动)

    Linux有6种不同的运行级别 默认的情况下Fedora安装完成后是从X Window启动的 X Window占用系统资源很大 所以对于我们仅仅想使用命令行模式的人来说 界面那么大 耗费资源太多有些浪费 那如何让Fedora从命令行启动而不
  • 卷麻了,00后测试用例写的比我还好,简直无地自容......

    经常看到无论是刚入职场的新人 还是工作了一段时间的老人 都会对编写测试用例感到困扰 例如 如何编写测试用例 作为一个测试新人 刚开始接触测试 对于怎么写测试用例很是头疼 无法接触需求 只能站在用户角度去做测试 但是这样情况会导致不能全方位测
  • parallel scavenge 与parnew 区别:

    Parallel Scavenge收集器是一个新生代收集器 它也是使用复制算法的收集器 又是并行的多线程收集器 看上去和ParNew都一样 那它有什么特别之处呢 Parallel Scavenge收集器的特点是它的关注点与其他收集器不同 C
  • 一款盲盒的交友软件叫什么(微信恋爱脱单交友盲盒小程序制作开发介绍)

    盲盒的交友软件一般叫做叫 盲盒脱单神器 月老交友盲盒或者是叫做一元交友等名称都是运营商自己随便起的 微信恋爱脱单交友盲盒小程序 一般情况是以H5网页的形式进行使用 做成微信小程序的形式需要相关资质 主要功能有 幻灯片 放入盒子 随机匹配 星
  • git clone指定分支拉代码、版本回退、log/reflog对比

    指定分支clone代码 1 git clone 不指定分支 默认就是master git clone http 10 1 1 11 service tmall service git 2 git clone 指定分支 git clone b
  • 【2022/2023年硕士研究生408计算机学科考试大纲原文】+【2009-2021年408统考真题+解析PDF】

    文章目录 2009 2021年408统考真题 解析 PDF版 I 考试性质 II 考查目标 III 试形式和试卷结构 一 试卷满分及考试时间 二 答题方式 三 试卷内容结构 四 试卷题型结构 IV 考查内容 数据结构 一 线性表 二 栈 队
  • CAS 5.3自定义 登录

    自定义认证校验策略 我们知道CAS为我们提供了多种认证数据源 我们可以选择JDBC File JSON等多种方式 但是如果我想在自己的认证方式中可以根据提交的信息实现不同数据源选择 这种方式就需要我们去实现自定义认证 自定义策略主要通过现实
  • 网页中插入图片的代码

    本文转载至 http www luke99 com celuechuangyi 2011 05 6912 html 如何在网页中插入图片呢 只要有图片的地址 就可以通过代码设置而放入我们的网页的 代码具体如下 img src 其中蓝色部分为
  • 牛客网题集——Min Value(逻辑)

    Min Value 牛客网测试平台 题意 一个由 N 个数组成的序列 a1 a2 a3 an 1 an 从中任选两个数 ai 和 aj 使得 ai aj 的绝对值最小 并且计算出 i j 的值 其中 i j 输入描述 输入第一行包含一个正整
  • 调用高德地图展示车辆行驶轨迹

    如何在页面中使用高德地图并分页展示多段历史轨迹 引入高德地图的JavaScript API 打开index html key 后面的内容是你自己在高德上申请 的key 引入高德组件 配置webpack 找到webpack base conf
  • 【Java日期时间】@JsonFormat与@DateTimeFormat注解的区分和使用

    目录标题 JsonFormat与 DateTimeFormat注解的区分和使用 1 背景 2 JsonFormat代码示例 步骤 注意 3 DateTimeFormat代码示例 步骤 注意 总结 JsonFormat与 DateTimeFo
  • QWizardPage、QWizard

    QWizardPage 一 描述 QWizard 代表一个向导 每个页面都是一个 QWizardPage Page 提供了五个可以重新实现以提供自定义行为的虚函数 当用户单击向导的 Next 按钮时 将调用 initializePage 来
  • 连接数据库超时设置autoReconnect=true

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 1 问题现象 com MySQL jdbc CommunicationsException The last packet successfully received fr
  • 2021-07-26

    解决 Action client not connected arm gripper controller follow joint trajectory ERROR 1627267012 953273779 3804 152000000

随机推荐

  • cin中输入空格断开的解决方法

    cin中输入空格断开的解决方法 cin gt gt a 此时输入 hello world cout lt
  • LaTeX添加包

    将包文件夹放入 CTEX MiKTeX tex latex目录中
  • Head First的MVC之歌(英文版)

    MVC之歌 歌名 模型 视图 控制器 ModelViewController 词曲 James Dempsey https pan baidu com s 1PXDVDqRQVpKcZ1bQwCLNLQ 请大佬 翻译并唱 出来
  • 和为 K 的最少斐波那契数字数目(贪心)

    题目描述 给你数字 k 请你返回和为 k 的斐波那契数字的最少数目 其中 每个斐波那契数字都可以被使用多次 斐波那契数字定义为 F1 1 F2 1 Fn Fn 1 Fn 2 其中 n gt 2 数据保证对于给定的 k 一定能找到可行解 示例
  • 增强网关设计与使用

    增强网关 目的 整合错误码 对外显示友好 对内便于快速定位问题 记录出错请求 依照错误码制定处理策略 设计 状态码格式 示例 E01001B002 解析 E 统一前缀 表明异常 01 应用标识 001 功能域 B 错误类型 002 错误码
  • vue 3.0新特性之reactive与ref

    vue 3 0新特性 参考 https www cnblogs com Highdoudou p 9993870 html https www cnblogs com ljx20180807 p 9987822 html 性能优化 观察者机
  • Allegro自动备份PCB设计文件的方法

    受到误删原理图的影响 立刻把PCB的自动备份功能设置一下 和原理图备份不一样的是PCB备份文件和源文件的格式相同 只是名称不一样 这个名称是自己设置的 步骤如下 点击 Setup gt User Preferences 弹出 User Pr
  • Linux 端 Kaggle 数据集下载:API 下载

    Linux 端 Kaggle 数据集下载 API 下载 一 准备好 kaggle json 文件 1 登录 Kaggle 官网 2 点击右上角头像 gt Your Profile gt Account gt Create New Token
  • Pandas_设置单元格条件格式1——指定值字体变色、指定值设置背景色

    转载于 https www cnblogs com wodexk p 10801344 html
  • 普通工程师和高级工程师的差别在哪里?如何快速突破?

    作者 王拥军 编辑 迷鹿 王拥军 毕业于天津大学计算机系 拥有从计算机硬件到操作系统安全 从后台服务器到客户端的全平台工作经历 目前在腾讯自选股从事互联网证券软件研发管理 对上市公司及创业团队的产品 文化 经营等具有独到的见解 个人公众号
  • linux设置系统时间

    我们一般使用 date s 命令来修改系统时间 比如将系统时间设定成20066年10月19日的命令如下 date s 10 19 2006 将系统时间设定成下午1点12分0秒的命令如下 date s 13 12 00 注意 这里说的是系统时
  • 【字节面试题】小于n的最大数

    标题 小于n的最大数 题目描述 给定一个数你 入23121 给定一个数组A如 2 4 9 求由A中元素组成的 小于n的最大数 如小于23121的最大数是22999 思路 1 把数组排序 2 把int转换成字节数组 从第一个开始变量 如2 从
  • App6种常见的数据加载设计

    设计师在进行APP设计的设计时 往往会更加专注于界面长什么样 界面和界面之间怎么跳转 给予用户什么样的操作反馈 却偏偏特别容易忽略掉一个比较重要的环节 就是APP数据加载中的设计 所以会导致我们看到的APP 往往有着华丽的启动界面 然后就是
  • Python3, 19行代码,让微信登录页面地球转起来,涨见识了。

    19行代码动态展示微信地图 1 引言 2 代码实战 2 1 思路 2 2 示例 2 2 1 经纬度 2 2 2 制作gif 3 总结 1 引言 小屌丝 鱼哥 最近在干啥嘞 小鱼 干活呗 不然能干啥 小屌丝 嘿嘿 小鱼 你这笑的 怎么 那么
  • FPGA_分频(信号使能分频与计数器分频)(奇偶分频)

    时钟对于 FPGA 是非常重要的 但板载晶振提供的时钟信号频率是固定的 不一定满 足工程需求 所以分频和倍频还是很有必要的 一 计数器分频 这里通过计数的方式来实现分频 1 通过计数器来实现6分频 两种方式 第一种直接通过计数方式直接获取获
  • 华为OD机试 Java 实现【整型数组合并】【牛客练习题】

    一 题目描述 将两个整型数组按照升序合并 并且过滤掉重复数组元素 输出时相邻两数之间没有空格 二 输入描述 输入说明 按下列顺序输入 输入第一个数组的个数 输入第一个数组的数值 输入第二个数组的个数 输入第二个数组的数值 三 输出描述 输出
  • Qt添加第三方字体

    最近开发项目时 据说不能用系统自带的微软雅黑字体 于是找一个开源的字体 思源黑体 这个是google和Adobe公司合力开发的可以免费使用 本篇记录一下Qt使用第三方字体的方式 字体从下载之家下载http www downza cn sof
  • C#文件后缀名详解

    sln 解决方案文件 为解决方案资源管理器提供显示管理文件的图形接口所需的信息 csproj 项目文件 创建应用程序所需的引用 数据连接 文件夹和文件的信息 aspx Web 窗体页由两部分组成 视觉元素 HTML 服务器控件和静态文本 和
  • 什么是P = NP?问题

    文章目录 引言 天才基本法 什么是P NP问题 P NP 成立吗 总结 提示 以下是本篇文章正文内容 Java系列学习将会持续更新 引言 今天我们先放松一下 这篇文章并不是Java课程的学习 而是带大家认识一个学术问题 但是请大家放心 这里
  • libevent多线程使用事项

    在linux平台上使用c开发网络程序的同志们一般情况下都对鼎鼎大名的libevent非常的熟悉了 但是一些新进入此领域的new new people们对此都是一头雾水 原本的迷茫再加上开源软件一贯的 帮助文件 缺失作风 让我们这些新手们显的