libevent Windows平台安装及使用

安装

下载libevent源码

git clone https://github.com/libevent/libevent.git

使用camke构建

1
2
3
$ mkdir build && cd build
$ cmake .. -DEVENT__DISABLE_OPENSSL=ON -DEVENT__LIBRARY_TYPE=STATIC -DEVENT__DISABLE_DEBUG_MODE=ON -G "CodeBlocks - MinGW Makefiles" -DCMAKE_MAKE_PROGRAM=C:/Mingw64/bin/make.exe -DCMA
KE_C_COMPILER=C:/Mingw64/bin/gcc.exe -DCMAKE_CXX_COMPILER=C:/Mingw64/bin/g++.exe -G "CodeBlocks - MinGW Makefiles" -S "D:\code\c\libevent" -B "D:\code\c\libevent\build"

cmake参数说明:

EVENT__DISABLE_OPENSSL:关闭ssl,这样可以避免下载openssl并设置环境变量。

EVENT__LIBRARY_TYPE:编译成静态库,方便后续使用时直接引用进行编译链接。

EVENT__DISABLE_DEBUG_MODE:关闭debug模式。

-G:指定构建系统生成器,据此生成不同平台下的Makefile格式。

CMAKE_MAKE_PROGRAM:make命令路径。

CMAKE_C_COMPILER:gcc路径。

CMAKE_CXX_COMPILER:g++地址。

-S:源目录。

-B:构建目录。

执行成功后会在当前目录生成响应的Makefile

make编译

1
$ make

该命令执行成功会生成对应静态库,如下所示:

整体流程

主要是三个静态文件libevent.a、libevent_core.a、libevent_extra.a

使用

在创建的项目里新建include目录。

  • 将libevent项目中include下的文件全部拷贝到include目录。
  • 将build/include/event2中的文件拷贝到include/event2文件夹,同时将build/include文件拷贝到include目录。
  • 将WIN32-Code目录文件拷贝到include目录。

在创建的项目里新建lib目录

将make生成的三个静态文件拷贝到lib目录。

实例

上一步准备了项目中需要include的头文件和静态库,接下来使用libevent完成最终代码的编写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
This example program provides a trivial server program that listens for TCP
connections on port 9995. When they arrive, it writes a short message to
each client connection, and closes each connection once it is flushed.

Where possible, it exits cleanly in response to a SIGINT (ctrl-c).
*/


#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>

#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
# include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif

#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 = 8080;

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(int argc, char **argv) {
struct event_base *base;
struct evconnlistener *listener;
struct event *signal_event;

struct sockaddr_in sin = {0};

#ifdef _WIN32
WSADATA wsa_data;
WSAStartup(0x0201, &wsa_data);
#endif

base = event_base_new();
if (!base) {
fprintf(stderr, "Could not initialize libevent!\n");
return 1;
}

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, -1,
(struct sockaddr *) &sin, sizeof(sin));

if (!listener) {
fprintf(stderr, "Could not create a listener!\n");
return 1;
}

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 s signal_event!\n");
return 1;
}

// 死循环处理事件,对于epoll调用的就是epoll_wait等待事件
event_base_dispatch(base);

evconnlistener_free(listener);
event_free(signal_event);
event_base_free(base);

printf("done\n");
return 0;
}

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;

// 每次有新的连接会分配一个新的fd处理客户端的事件,分配一个bufferevent调用回调函数处理
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
fprintf(stderr, "Error constructing bufferevebnt!\n");
event_base_loopbreak(base);
return;
}
// 设置readcb、writecb、eventcb回调
// readcb: 监听到新连接事件时的读事件处理,这里为NULL,因为不需要读额外的数据
// writecb: 监听到新连接事件时的写事件处理
// eventcb: 处理错误
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) == 0) {
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 = {2, 0};

printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");

event_base_loopexit(base, &delay);
}

编译运行后访问127.0.0.1:8080,可以访问即代表成功,完整代码https://github.com/Monokaix/unix-socket/tree/master/libevent-demo。