hello-world是libevent自带的一个例子,这个例子的作用是启动后监听一个端口,对于所有通过这个端口连接上服务器的程序发送一段字符:hello-world,然后关闭连接。

 /*
* gcc -g -o hello-world hello-world.c -levent_core
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h> #include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h> static const char MESSAGE[] = "Hello, World!\n"; static const int PORT = ; static void listener_cb(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *); int main(void)
{
struct event_base *base;
struct evconnlistener *listener;
struct event *signal_event; struct sockaddr_in sin; base = event_base_new();
if (!base)
{
fprintf(stderr, "Could not initialize libevent\n");
return ;
} memset(&sin, , sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT); listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, -, (struct sockaddr *)&sin, sizeof(sin)); if (!listener)
{
fprintf(stderr, "Could not create a listener!\n");
return ;
}
printf("Listening on %d\n", PORT); signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base); if (!signal_event || event_add(signal_event, NULL) < )
{
fprintf(stderr, "Could not create/add a signal event!\n");
return ;
} event_base_dispatch(base); evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base); printf("Done!\n");
return ;
} static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sa, int socklen, void *user_data)
{
struct event_base *base = user_data;
struct bufferevent *bev;
struct sockaddr_in *sa_in = (struct sockaddr_in*)sa; bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev)
{
fprintf(stderr, "Error construction bufferevent!");
event_base_loopbreak(base);
return;
}
printf("Recv a new connection, ip[%s], port[%d]\n", inet_ntoa(sa_in->sin_addr), ntohs(sa_in->sin_port));
bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
bufferevent_enable(bev, EV_WRITE);
bufferevent_disable(bev, EV_READ); bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
} static void conn_writecb(struct bufferevent *bev, void *user_data)
{
struct evbuffer *output = bufferevent_get_output(bev);
if (evbuffer_get_length(output) == )
{
printf("flushed answer\n");
bufferevent_free(bev);
}
} static void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
if (events & BEV_EVENT_EOF)
{
printf("Connection closed.\n");
}
else if (events & BEV_EVENT_ERROR)
{
printf("Got an error on the connection: %s\n", strerror(errno));
} bufferevent_free(bev);
} static void signal_cb(evutil_socket_t sig, short events, void *user_data)
{
struct event_base *base = user_data;
struct timeval delay = { , };
printf("Caught an interrupt signal; exiting cleanly in two seconds\n");
event_base_loopexit(base, &delay);
}

下面就通过分析这个例子来看一下libevent对于IO事件是如何处理的:

1、调用event_base_new获得event_base对象。

2、调用evconnlistener_new_bind,返回一个struct evconnlistener对象指针,evconnlistener_new_bind函数内部实现如下:

1)调用evutil_socket_函数获取一个socket。

2)调用evconnlistener_new函数获取一个struct evconnlistener对象指针,并返回。

在evconnlistener_new函数内部,首先调用malloc函数分配一个struct evconnlistener_event对象,然后利用传入的形参fd调用listen函数,然后初始化base的各个参数,调用event_assign初始化成员listener。

其实这些函数归根结底还是会调用最基本的libevent函数,只是这些函数对基本的函数做了一些封装提供更高级、更方便的使用方式。

struct evconnlistener和struct evconnlistener_event的定义如下:

 struct evconnlistener {
const struct evconnlistener_ops *ops;
void *lock;
evconnlistener_cb cb;
evconnlistener_errorcb errorcb;
void *user_data;
unsigned flags;
short refcnt;
int accept4_flags;
unsigned enabled : ;
}; struct evconnlistener_event {
struct evconnlistener base;
struct event listener;
};

关于这里,我有些不明白的是listener是如何被调用event_add函数的?答案在这里

3、调用evsignal_new函数获取一个信号事件对象,其实这个函数也是对event_new函数的封装。

 #define evsignal_new(b, x, cb, arg)                \
event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))

4、调用event_base_dispatch函数进入事件循环。

5、调用evconnlistener_free释放struct evconnlistener对象。

然后看一下回调函数,在创建evconnlistener对象时传入了一个回调函数:listener_cb,该函数会在监听对象有新连接到来时被调用,在它的内部,首先创建一个struct bufferevent对象,然后设置该对象的回调函数,然后就是调用bufferevent_write函数将要发送的数据写入到该对象对应的buffer中,就不用管了。

由此可见使用libevent库来开发网络服务器是多么的方便,高效。

至此一个基于libevent的简单服务器就完成了,只能对连接上的客户端发送“hello world”然后关闭连接。

libevent源码分析:hello-world例子的更多相关文章

  1. libevent源码分析:http-server例子

    http-server例子是libevent提供的一个简单web服务器,实现了对静态网页的处理功能. /* * gcc -g -o http-server http-server.c -levent ...

  2. libevent源码分析:signal-test例子

    signal-test是libevent自带的一个例子,展示了libevent对于信号事件的处理方法. #include <sys/types.h> #include <event2 ...

  3. libevent源码分析:time-test例子

    time-test例子是libevent自带的一个例子,通过libevent提供的定时事件来实现,间隔固定时间打印的功能. /* * gcc -g -o time-test time-test.c - ...

  4. 【转】libevent源码分析

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

  5. Libevent源码分析 (1) hello-world

    Libevent源码分析 (1) hello-world ⑨月份接触了久闻大名的libevent,当时想读读源码,可是由于事情比较多一直没有时间,现在手头的东西基本告一段落了,我准备读读libeven ...

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

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

  7. Libevent源码分析系列

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

  8. libevent源码分析

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

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

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

随机推荐

  1. Image模块

    1.创建一个新的图片 Image.new(mode,size) Image.new(mode,size,color) 2.层叠图片 层叠两个图片,img1和img2,alpha是一个介于[0,1]的浮 ...

  2. 如何利用python监控主机存活并邮件、短信通知

    功能: 1.使用定时任务执行脚本,检查主机存活信息2.将主机存活信息写入指定文件3.发现宕机的主机后给用户发邮件提醒备注:因为139邮箱在接受到邮件后会自动给用户发送条短信告知(且此服务免费),所以间 ...

  3. javaScript中值类型通过typeof直接进行检测

    通过试验,对图像处理有了进一步深入了解和认知,基于第一次的滤波的处理和这次灰度线性变换和直方图处理图像,知道了图像的成像原理,都是一个个的像素点,就是矩阵的值.以后可以利用MATLAB进行图像处理,运 ...

  4. iOS缓存功能

    之前做缓存,没有考虑过这个具体的实现. 移动应用在处理网络资源时,一般都会做离线缓存处理,其中以图片缓存最为典型,其中很流行的离线缓存框架为SDWebImage. 但是,离线缓存会占用手机存储空间,所 ...

  5. Asp.net 加载事件(转载)

    using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Secu ...

  6. JAVASE02-Unit010: 多线程基础 、 TCP通信

    多线程基础 . TCP通信 * 当一个方法被synchronized修饰后,那么 * 该方法称为同步方法,即:多个线程不能同时 * 进入到方法内部执行. package day10; /** * 当多 ...

  7. MySQL的几个概念:主键,外键,索引,唯一索引

    概念: 主键(primary key) 能够唯一标识表中某一行的属性或属性组.一个表只能有一个主键,但可以有多个候选索引.主键常常与外键构成参照完整性约束,防止出现数据不一致.主键可以保证记录的唯一和 ...

  8. python 类型大小

    返回单位:字节 sys.getsizeof() import sys>>> sys.getsizeof(') >>> sys.getsizeof(') >&g ...

  9. sql关联表查询结果并插入

    这里涉及三个表,AA,BB,CC,将AA的数据更新到CC表中,将AA中LABEL_ID分别截取字段与BB表中的label_id对应查询到LABEL_NAME作为CC表的一个字段插入,这里分成四段查询 ...

  10. TortoiseGit 连接oschina不用每次输入用户名和密码的方法

    每次git clone 和push 都要输入用户名和密码.虽然安全,但在本机上每次都输有些麻烦,如何记住用户名和密码呢? 在网上看了各种方法,太杂,很多可能环境不一样,一直行不通.最后找到一种有效的方 ...