libevent使用IOCP网络模型的示例
这段时间抽空学习了一下强大的网络库 libevent,其使用标准C语言编写,支持Windows、Linux、Mac等等主流操作系统,早期版本不支持Windows的IOCP,最新版本已经添加上了,在网上找了一下资料,发现使用IOCP的libevent示例太少,于是结合网上的资料,自己整理编写了一下libevent使用IOCP的小例子。该示例同时支持IPV4以及IPV6的连接。
1#ifdef __cplusplus
2extern "C"
3{
4#endif
5
6//包含所需要的头文件
7#include "event2/event.h"
8#include "event2/listener.h"
9#include "event2/bufferevent.h"
10#include "event2/thread.h"
11#include "event2/buffer.h"
12
13#ifdef __cplusplus
14};
15#endif
16
17#ifdef _MSC_VER
18#pragma comment(lib,"ws2_32.lib")
19#pragma comment(lib,"libevent_core.lib")
20#endif
21
22//监听回调函数
23void listener_cb(evconnlistener *listener, evutil_socket_t fd,
24 struct sockaddr *sock, int socklen, void *arg);
25
26//从Socket接收消息的回调函数
27void socket_read_cb(bufferevent *bev, void *arg);
28
29//从Socket事件的回调函数
30void socket_event_cb(bufferevent *bev, short events, void *arg);
31
32int main()
33{
34 WORD wVersionRequested;
35 WSADATA wsaData;
36 int err;
37
38 /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
39 wVersionRequested = MAKEWORD(2, 2);
40 //这里必须初始化网络,不然会创建Socket失败
41 err = WSAStartup(wVersionRequested, &wsaData);
42 if (err != 0) {
43 /* Tell the user that we could not find a usable */
44 /* Winsock DLL. */
45 printf("WSAStartup failed with error: %d\n", err);
46 return 1;
47 }
48
49 struct sockaddr_in sin;
50 memset(&sin, 0, sizeof(struct sockaddr_in));
51 sin.sin_family = AF_INET;
52 sin.sin_port = htons(2000);
53
54 struct sockaddr_in6 sin6;
55 memset(&sin6, 0, sizeof(struct sockaddr_in6));
56 sin6.sin6_family = AF_INET6;
57 sin6.sin6_port = htons(2000);
58
59 //告诉libEvent使用Windows线程
60 //这句是必须的,不然会导致event_base_dispatch时一直处于Sleep状态,无法正常工作
61 evthread_use_windows_threads();
62
63 struct event_config* cfg = event_config_new();
64 event_config_set_flag(cfg,EVENT_BASE_FLAG_STARTUP_IOCP);
65 //根据CPU实际数量配置libEvent的CPU数
66 SYSTEM_INFO si;
67 GetSystemInfo(&si);
68 event_config_set_num_cpus_hint(cfg,si.dwNumberOfProcessors);
69
70 event_base *base;
71 base = event_base_new_with_config(cfg);
72 event_config_free(cfg);
73
74 // 绑定并监听IPV4端口
75 evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base,
76 LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,
77 10, (struct sockaddr*)&sin,
78 sizeof(sin));
79
80 // 绑定并监听IPV6端口
81 evconnlistener *listener6 = evconnlistener_new_bind(base, listener_cb, base,
82 LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,
83 10, (struct sockaddr*)&sin6,
84 sizeof(sin6));
85
86 //事件分发处理
87 event_base_dispatch(base);
88
89 evconnlistener_free(listener);
90 evconnlistener_free(listener6);
91 event_base_free(base);
92 WSACleanup();
93
94 return 0;
95}
96
97//一个新客户端连接上服务器了
98//当此函数被调用时,libevent已经帮我们accept了这个客户端。该客户端的
99//文件描述符为fd
100void listener_cb(evconnlistener *listener, evutil_socket_t fd,
101struct sockaddr *sock, int socklen, void *arg)
102{
103 char Buffer[256];
104 sockaddr_in* addr = (sockaddr_in*)sock;
105 evutil_inet_ntop(addr->sin_family,&addr->sin_addr,Buffer,sizeof(Buffer));
106 printf("accept a client %d,IP:%s\n", fd,Buffer);
107
108 event_base *base = (event_base*)arg;
109
110 //为这个客户端分配一个bufferevent
111 bufferevent *bev = bufferevent_socket_new(base, fd,
112 BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE);
113
114 bufferevent_setcb(bev, socket_read_cb, NULL, socket_event_cb, NULL);
115 bufferevent_enable(bev, EV_READ | EV_PERSIST);
116}
117
118void socket_read_cb(bufferevent *bev, void *arg)
119{
120 char msg[4096];
121
122 size_t len;
123 // 这里一行一行的读取
124 char* p = evbuffer_readln(bufferevent_get_input(bev),&len,EVBUFFER_EOL_ANY);
125 if(p)
126 {
127 // 如果输入exit或者quit则退出程序
128 // 可以使用event_base_loopexit或者event_base_loopbreak
129 // 它们的区别是前者会把事件处理完才退出,后者是立即退出
130 if(!strcmp(p,"exit"))
131 event_base_loopexit(bufferevent_get_base(bev),NULL);
132 else if (!strcmp(p,"quit"))
133 event_base_loopbreak(bufferevent_get_base(bev));
134
135 printf("recv data:%s\n", p);
136
137 int n = sprintf_s(msg,"srv recv data:%s\n",p);
138 //发送消息给客户端
139 bufferevent_write(bev, msg, n );
140
141 // 这里记得把分配的内存释放掉,不然会内存泄漏
142 free(p);
143 }
144}
145
146void socket_event_cb(bufferevent *bev, short events, void *arg)
147{
148 if (events & BEV_EVENT_EOF)
149 printf("connection closed\n");
150 else if (events & BEV_EVENT_ERROR)
151 printf("some other error\n");
152
153 //这将自动close套接字和free读写缓冲区
154 bufferevent_free(bev);
155}
- 原文作者:Witton
- 原文链接:https://wittonbell.github.io/posts/2016/2016-07-01-libevent使用IOCP网络模型的示例/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。