Libevent源码分析 (1) hello-world

⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libevent的源码,凡是我觉得有必要的内容均一一记录,与君共勉。

首先要说说什么是libevent:

libevent是一个事件通知库,libevent API提供一种机制使得我们可以在一个文件描述符(file descriptor)发生特定事件时或者timeout发生时执行指定的回调函数。libevent意图代替事件驱动服务器上的事件循环。应用程序只需要调用event_dispatch(),然后动态添加或者删除事件,而不需要修改事件循环

1.构建环境

  • libevent 2.1.x
  • windows10(linux的epoll以后再说,先看看libevent对于IOCP的包装吧)

如何构建环境可以参见官方教程Build and Install,非常简单,基本上可以直接cmake。 由于我构建的环境不使用openssl,所以option(EVENT__DISABLE_OPENSSL OFF)要修改为 option(EVENT__DISABLE_OPENSSL ON),然后直接生成解决方案。

2. 相逢#pragram push_macro

如果你和我一样编译过程中遇到error: static declaration of 'strtok_r' follows non-static declaration可以在前面加上一行#ifndef EVENT__HAVE_STRTOK_R。 重点是这里我学到了一个没用过的功能


#define A "Hello"
std::cout << A << std::endl; //Hello
#pragma push_macro("A")
#define A "World"
#pragma push_macro("A")
#define A ":)"
std::cout << A << std::endl; //:)
#pragma pop_macro("A")
std::cout << A << std::endl; //World
#pragma pop_macro("A")
std::cout << A << std::endl; //Hello

注意别漏了A的双引号。可以看出push_macro/pop_macro的确是实现了栈的效果。为了再确认一下我们看看_clang_的实现:

///   #pragma push_macro("macro")
void Preprocessor::HandlePragmaPushMacro(Token &PushMacroTok) {
...
IdentifierInfo *IdentInfo = ParsePragmaPushOrPopMacro(PushMacroTok);
// Get the MacroInfo associated with IdentInfo.
MacroInfo *MI = getMacroInfo(IdentInfo);
// Push the cloned MacroInfo so we can retrieve it later.
PragmaPushMacroInfo[IdentInfo].push_back(MI);
}
/// #pragma pop_macro("macro")
void Preprocessor::HandlePragmaPopMacro(Token &PopMacroTok) {
...
// Find the vector<MacroInfo*> associated with the macro.
llvm::DenseMap<IdentifierInfo*, std::vector<MacroInfo*> >::iterator iter =
PragmaPushMacroInfo.find(IdentInfo);
if (iter != PragmaPushMacroInfo.end()) { // Pop PragmaPushMacroInfo stack.
iter->second.pop_back();
if (iter->second.empty())
PragmaPushMacroInfo.erase(iter); }
}

3. libevent的hello-world

回到主题,这系列文章从libevent/sample/hello-world.c开始。hello-world.c是一个日常socket IO程序,当客户端通过9995端口与服务器连接后服务器持续发送Hello, World!,不过现在用的是libevent事件回调实现的。

int
main(int argc, char **argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct event *signal_event; struct sockaddr_in sin;
#ifdef _WIN32 //在win上需要用WAStartup初始化winsock dll才能使用socket
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif ///////////////////////////////////////////////////////////////////////////
/// 1.event_base_new使用默认设置创建一个指向event_base的指针
///////////////////////////////////////////////////////////////////////////
base = event_base_new();
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
} memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT); ///////////////////////////////////////////////////////////////////////////
/// 2.evconnlistener_new_bind分配一个connection监听对象,当有新TCP连接时执行
/// listener_cb回调
///////////////////////////////////////////////////////////////////////////
listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
(struct sockaddr*)&sin,
sizeof(sin)); if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}
///////////////////////////////////////////////////////////////////////////
/// 3. evsignal_new是一个#define evsignal_new(b, x, cb, arg) \
/// event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
///////////////////////////////////////////////////////////////////////////
signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base); if (!signal_event || event_add(signal_event, NULL)<0) {
fprintf(stderr, "Could not create/add a signal event!\n");
return 1;
}
///////////////////////////////////////////////////////////////////////////
/// 4. event_base_dispatch等价于event_base_loop(event_base, 0),当一切准备就绪
/// 后就可以执行它,event_base_dispatch会一直运行,直到没有任何注册事件或者用户调用
/// event_base_loopexit
///////////////////////////////////////////////////////////////////////////
event_base_dispatch(base); ///////////////////////////////////////////////////////////////////////////
/// 5. 堆释放
///////////////////////////////////////////////////////////////////////////
evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base); printf("done\n");
return 0;
}

上面就是一个libevent的通用模板:首先创建event_base,然后绑定服务器socket监听端口并注册事件回调,最后开启事件循环,剩下的工作就是编写各个事件的回调。 当然别忘了释放内存。

4.提前结束

归纳一下hello-world用到的libevent APIs:



这系列文章要分析的就是上图的四个函数,它们包含了libevent一个完整的生命周期。最开始我准备所有内容写到一篇,后面发现实在太长了,读起来累,逻辑也混乱(函数调用栈太长了),所以我打算逐章分析它们。 enjoy!

Libevent源码分析 (1) hello-world的更多相关文章

  1. 【转】libevent源码分析

    libevent源码分析 转自:http://www.cnblogs.com/hustcat/archive/2010/08/31/1814022.html 这两天没事,看了一下Memcached和l ...

  2. Libevent源码分析系列【转】

    转自:https://www.cnblogs.com/zxiner/p/6919021.html 1.使用libevent库     源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了 ...

  3. Libevent源码分析系列

    1.使用libevent库     源码那么多,该怎么分析从哪分析呢?一个好的方法就是先用起来,会用了,然后去看底层相应的源码,这样比较有条理,自上向下掌握.下面用libevent库写个程序,每隔1秒 ...

  4. libevent源码分析

    这两天没事,看了一下Memcached和libevent的源码,做个小总结. 1.入门 1.1.概述Libevent是一个用于开发可扩展性网络服务器的基于事件驱动(event-driven)模型的网络 ...

  5. libevent源码分析二--timeout事件响应

    libevent不仅支持io事件,同时还支持timeout事件与signal事件,这篇文件将分析libevent是如何组织timeout事件以及如何响应timeout事件. 1.  min_heap ...

  6. libevent源码分析一--io事件响应

    这篇文章将分析libevent如何组织io事件,如何捕捉事件的发生并进行相应的响应.这里不会详细分析event与event_base的细节,仅描述io事件如何存储与如何响应. 1.  select l ...

  7. Libevent源码分析—event_init()

    下面开始看初始化event_base结构的相关函数.相关源码位于event.c event_init() 首先调用event_init()初始化event_base结构体 struct event_b ...

  8. Libevent源码分析—event, event_base

    event和event_base是libevent的两个核心结构体,分别是反应堆模式中的Event和Reactor.源码分别位于event.h和event-internal.h中 1.event: s ...

  9. Libevent源码分析—event_add()

    接下来就是将已经初始化的event注册到libevent的事件链表上,通过event_add()来实现,源码位于event.c中. event_add() 这个函数主要完成了下面几件事: 1.将eve ...

随机推荐

  1. tomcat集群日志切割和远程备份脚本分享

    笔者一共有3台tomcat服务器,一共4个tomcat服务,未来还会增加4个作为负载,笔者想通过在存储服务器对tomcat服务的日志进行远程切割和备份到存储上. 文中采用清空日志的方式,优点是不用重启 ...

  2. 快速拥有各种数据访问SqlHelper

    常加班食不按时,偶得清闲嘴溃疡. 美食一顿成泡汤,自此自认忙命人. 这就是此情此景的我,回来聊代码. 列举ADO.NET中的五个主要对象,并简单描述? 答:Connection连接对象,Command ...

  3. c语言贪吃蛇详解1.画出地图

    c语言贪吃蛇详解-1.画出地图 前几天的实验室培训课后作业我布置了贪吃蛇,今天有时间就来写一下题解.我将分几步来教大家写一个贪吃蛇小游戏.由于大家c语言未学完,这个教程只涉及数组和函数等知识点. 首先 ...

  4. java系列视频教程下载

    1.马士兵J2SE基础录屏视频 珍藏版 链接:https://pan.baidu.com/s/1eRMJqkq    密码:qa66 2.spring视频教程 链接:https://pan.baidu ...

  5. Jackson将json string转为Object,org.json读取json数组

    从json文件读取json string或者自定义json string,将其转为object.下面采用的object为map,根据map读取json的某个数据,可以读取第一级的数据name,后来发现 ...

  6. include、include_once、require、require_once其区别

    1.include: 载入文件.未找到文件,则产生E_WARNING 级别的警告错误,脚本继续运行. 2.include_once: 与include 语句作用相同,区别只是如果该文件已经被包含过,则 ...

  7. SQL Server学习之路(八):扩展SQL语句

    0.目录 1.问题描述 2.第一种方法 通过GROUP BY子句解决 3.第二种方法 通过聚合函数解决 4.第三种方法 在select...from...中的from后面嵌套一个表 5.第四种方法 在 ...

  8. springBoot系列教程02:mongodb的集成及使用

    1.安装mongodb mongdb的安装很简单,只需要下载解压后运行mongod就好了 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86 ...

  9. 数据库文件*.sdf文件定时备份,但是大小的增量在不断增长的问题排查

    在某项目上,使用SQL Server数据库,现场反馈每天定时备份数据库文件,每天的数据量是400多个申请单的量.之前每天增长量是50M,但是后来两天增长量是80M,每天的数据量差不多. 到底从什么地方 ...

  10. mybatis延迟加载一对多

    1.实体类 package cn.bdqn.bean; import java.util.Set; /** *国家的实体类 */ public class Country { private Inte ...