- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Windows下实现简单的libevent服务器由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
最近再学习Libevent由于自己使用的是windows系统,遗憾的是有关在vs下可以参考的程序少之又少。在参考了许多的博客文章后。自己摸索写了一个简单的Libevent Server程序。并且在网上找了一个简单的客户端程序,测试该代码成功。今天在此做一个记录.
Libevent的确是一个非常好用的东西,还在继续学习中,后续还要在windows下实现Libevent的多线程使用。今天先把自己搞出来的东西贴上来,仅供学习参考。在vs2015上编译通过.
默认情况下是单线程的(可以配置成多线程,如果有需要的话),每个线程有且只有一event base,对应一个struct event_base结构体(以及附于其上的事件管理器),用来schedule托管给它的一系列event,可以和操作系统的进程管理类比,当然,要更简单一点。当一个事件发生后,event_base会在合适的时间(不一定是立即)去调用绑定在这个事件上的函数(传入一些预定义的参数,以及在绑定时指定的一个参数),直到这个函数执行完,再返回schedule其他事件.
1
2
3
|
//创建一个event_base
struct
event_base *base = event_base_new();
assert
(base != NULL);
|
event_base内部有一个循环,循环阻塞在epoll / kqueue等系统调用上,直到有一个 / 一些事件发生,然后去处理这些事件。当然,这些事件要被绑定在这个event_base上。每个事件对应一个struct event,可以是监听一个fd或者POSIX信号量之类(这里只讲fd了,其他的看manual吧)。struct event使用event_new来创建和绑定,使用event_add来启用:
1
2
3
4
5
6
7
|
//创建并绑定一个event
struct
event *listen_event;
//参数:event_base, 监听的fd,事件类型及属性,绑定的回调函数,给回调函数的参数
listen_event = event_new(base, listener, EV_READ | EV_PERSIST, callback_func, (
void
*)base);
//参数:event,超时时间(struct timeval *类型的,NULL表示无超时设置)
event_add(listen_event, NULL);
|
注:libevent支持的事件及属性包括(使用bitfield实现,所以要用 | 来让它们合体) 。
(a) EV_TIMEOUT: 超时 (b) EV_READ : 只要网络缓冲中还有数据,回调函数就会被触发 (c) EV_WRITE : 只要塞给网络缓冲的数据被写完,回调函数就会被触发 (d) EV_SIGNAL : POSIX信号量,参考manual吧 (e) EV_PERSIST : 不指定这个属性的话,回调函数被触发后事件会被删除 (f) EV_ET : Edge - Trigger边缘触发,参考EPOLL_ET 然后需要启动event_base的循环,这样才能开始处理发生的事件。循环的启动event base dispatch,循环将一直持续,直到不再有需要关注的事件,或者是遇到event_loopbreak() / event_loopexit()函数。 //启动事件循环 event_base_dispatch(base),
接下来关注下绑定到event的回调函数callback_func:传递给它的是一个socket fd、一个event类型及属性bit_field、以及传递给event_new的最后一个参数(去上面几行回顾一下,把event_base给传进来了,实际上更多地是分配一个结构体,把相关的数据都撂进去,然后丢给event_new,在这里就能取得到了)。其原型是: typedef void(*event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg) 。
对于一个服务器而言,上面的流程大概是这样组合的: 1. listener = socket(),bind(),listen(),设置nonblocking(POSIX系统中可使用fcntl设置,windows不需要设置, 实际上libevent提供了统一的包装evutil_make_socket_nonblocking) 2. 创建一个event_base 3. 创建一个event,将该socket托管给event_base,指定要监听的事件类型,并绑定上相应的回调函数(及需要给它的参数) 。对于listener socket来说,只需要监听EV_READ | EV_PERSIST 4. 启用该事件 5. 进入事件循环 -------------- - 6. (异步)当有client发起请求的时候,调用该回调函数,进行处理。 /*接下来关注下绑定到event的回调函数callback_func:传递给它的是一个socket fd、一个event类型及属性bit_field、以及传递给event_new的最后一个参数(去上面几行回顾一下,把event_base给传进来了,实际上更多地是分配一个结构体,把相关的数据都撂进去,然后丢给event_new,在这里就能取得到了)。*/ 。
服务器端代码:Server.cpp 。
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
|
#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include<iostream>
#include<cassert>
#pragma comment (lib,"ws2_32.lib")
#include<ws2tcpip.h>
#define LISTEN_PORT 9999
#define LIATEN_BACKLOG 32
using
namespace
std;
/*********************************************************************************
* 函数声明
**********************************************************************************/
//accept回掉函数
void
do_accept_cb(evutil_socket_t listener,
short
event,
void
*arg);
//read 回调函数
void
read_cb(
struct
bufferevent *bev,
void
*arg);
//error回调函数
void
error_cb(
struct
bufferevent *bev,
short
event,
void
*arg);
//write 回调函数
void
write_cb(
struct
bufferevent *bev,
void
*arg);
/*********************************************************************************
* 函数体
**********************************************************************************/
//accept回掉函数
void
do_accept_cb(evutil_socket_t listener,
short
event,
void
*arg)
{
//传入的event_base指针
struct
event_base *base = (
struct
event_base*)arg;
//socket描述符
evutil_socket_t fd;
//声明地址
struct
sockaddr_in
sin
;
//地址长度声明
socklen_t slen =
sizeof
(
sin
);
//接收客户端
fd = accept(listener, (
struct
sockaddr *)&
sin
, &slen);
if
(fd < 0)
{
perror
(
"error accept"
);
return
;
}
printf
(
"ACCEPT: fd = %u\n"
, fd);
////注册一个bufferevent_socket_new事件
struct
bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
////设置回掉函数
bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
////设置该事件的属性
bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
}
////read 回调函数
void
read_cb(
struct
bufferevent *bev,
void
*arg)
{
#define MAX_LINE 256
char
line[MAX_LINE + 1];
int
n;
//通过传入参数bev找到socket fd
evutil_socket_t fd = bufferevent_getfd(bev);
//
while
(n = bufferevent_read(bev, line, MAX_LINE))
{
line[n] =
'\0'
;
printf
(
"fd=%u, read line: %s\n"
, fd, line);
//将获取的数据返回给客户端
bufferevent_write(bev, line, n);
}
}
////error回调函数
void
error_cb(
struct
bufferevent *bev,
short
event,
void
*arg)
{
//通过传入参数bev找到socket fd
evutil_socket_t fd = bufferevent_getfd(bev);
//cout << "fd = " << fd << endl;
if
(event & BEV_EVENT_TIMEOUT)
{
printf
(
"Timed out\n"
);
//if bufferevent_set_timeouts() called
}
else
if
(event & BEV_EVENT_EOF)
{
printf
(
"connection closed\n"
);
}
else
if
(event & BEV_EVENT_ERROR)
{
printf
(
"some other error\n"
);
}
bufferevent_free(bev);
}
////write 回调函数
void
write_cb(
struct
bufferevent *bev,
void
*arg)
{
char
str[50];
//通过传入参数bev找到socket fd
evutil_socket_t fd = bufferevent_getfd(bev);
//cin >> str;
printf
(
"输入数据!"
);
scanf_s(
"%d"
, &str);
bufferevent_write(bev, &str,
sizeof
(str));
}
int
main()
{
int
ret;
evutil_socket_t listener;
WSADATA Ws;
//Init Windows Socket
if
(WSAStartup(MAKEWORD(2, 2), &Ws) != 0)
{
return
-1;
}
listener = socket(AF_INET, SOCK_STREAM, 0);
assert
(listener > 0);
evutil_make_listen_socket_reuseable(listener);
struct
sockaddr_in
sin
;
sin
.sin_family = AF_INET;
sin
.sin_addr.s_addr = 0;
sin
.sin_port = htons(LISTEN_PORT);
if
(bind(listener, (
struct
sockaddr *)&
sin
,
sizeof
(
sin
)) < 0) {
perror
(
"bind"
);
return
1;
}
if
(listen(listener, 1000) < 0) {
perror
(
"listen"
);
return
1;
}
printf
(
"Listening...\n"
);
evutil_make_socket_nonblocking(listener);
struct
event_base *base = event_base_new();
assert
(base != NULL);
struct
event *listen_event;
listen_event = event_new(base, listener, EV_READ | EV_PERSIST, do_accept_cb, (
void
*)base);
event_add(listen_event, NULL);
event_base_dispatch(base);
printf
(
"The End."
);
return
0;
}
|
客户端代码:Client.cpp 。
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
|
/******* 客户端程序 client.c ************/
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include<winsock2.h>
#include<ws2tcpip.h>
#include<iostream>
#pragma comment (lib,"ws2_32.lib")
int
main(
int
argc,
char
*argv[])
{
WSADATA Ws;
//Init Windows Socket
if
(WSAStartup(MAKEWORD(2, 2), &Ws) != 0)
{
return
0;
}
int
sockfd;
char
buffer[1024];
struct
sockaddr_in server_addr;
struct
hostent *host;
int
portnumber, nbytes;
if
((host = gethostbyname(
"127.0.0.1"
)) == NULL)
{
fprintf
(stderr,
"Gethostname error\n"
);
exit
(1);
}
if
((portnumber =
atoi
(
"9999"
))<0)
{
fprintf
(stderr,
"Usage:%s hostname portnumber\a\n"
, argv[0]);
exit
(1);
}
/* 客户程序开始建立 sockfd描述符 */
if
((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf
(stderr,
"Socket Error:%s\a\n"
,
strerror
(
errno
));
exit
(1);
}
/* 客户程序填充服务端的资料 */
memset
(&server_addr,0,
sizeof
(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portnumber);
server_addr.sin_addr = *((
struct
in_addr *)host->h_addr);
/* 客户程序发起连接请求 */
if
(connect(sockfd, (
struct
sockaddr *)(&server_addr),
sizeof
(
struct
sockaddr)) == -1)
{
fprintf
(stderr,
"Connect Error:%s\a\n"
,
strerror
(
errno
));
exit
(1);
}
while
(
true
)
{
char
MESSAGE[] =
"hello server..\n"
;
//bufferevent_write(buf_ev,MESSAGE,strlen(MESSAGE));
//
if
(-1 == (::send(sockfd, MESSAGE,
strlen
(MESSAGE), 0)))
{
printf
(
"the net has a error occured.."
);
break
;
}
if
((nbytes = recv(sockfd, buffer, 1024,0)) == -1)
{
fprintf
(stderr,
"read error:%s\n"
,
strerror
(
errno
));
exit
(1);
}
buffer[nbytes] =
'\0'
;
printf
(
"I have received:%s\n"
, buffer);
memset
(buffer, 0, 1024);
Sleep(2);
}
/* 结束通讯 */
closesocket(sockfd);
exit
(0);
return
0;
}
|
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
最后此篇关于Windows下实现简单的libevent服务器的文章就讲到这里了,如果你想了解更多关于Windows下实现简单的libevent服务器的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
这两个库都是为异步 I/O 调度而设计的,并且都在 Linux 上使用 epoll,在 FreeBSD 上使用 kqueue 等。 除了表面上的差异,我的意思是这两个库之间真正的差异是什么?关于架构,
假设我有 2 个正在监视事件的套接字,我真的希望套接字 1 成为首选(即使以耗尽套接字 2 为代价)。我如何用 libev 做到这一点(我问了关于 libuv 的同样问题 here 但看起来 libu
我想使用事件驱动库之一制作基本的聊天服务器。但是选择什么? libevent 或 libev 哪个更好?哪个有更好的 C++ 支持?哪个 rune 在 Windows 中更好?什么更快?更快地开发和理
我正在尝试监控一个 unix 套接字(“/tmp/mysocket”)。 我可以在 Node.js 中很好地做到这一点:当 1) 套接字被绑定(bind),2) 有人连接到套接字,3) 数据被发送到套
我正在尝试向后移植基于 libevent 的库以使用 ASIO 后端(从而避免在单个应用程序中出现多个事件循环)。还有其他方法可以解决“问题”,但我对这个感兴趣 我在 Boost::ASIO 文档中没
我目前正在使用libevent编写多线程应用程序。 某些事件是由IO触发的,但我需要使用event_active()由代码本身跨线程触发的几个事件。 我试图编写一个简单的程序来显示我的问题所在: 使用
我花了一些时间在互联网上搜索寻找更好的方法来分析和调试我的问题,但我似乎找不到解决方案。所以我想我会问。 简要地。我正在尝试创建一个非阻塞 ssl 转发代理。代理的服务器部分使用自签名服务器证书,我使
我有以下代码: #include #include #include #include #include void fd_cb(int fd,short event,void *arg){
我有几个关于 libevent2 及其多线程支持的问题。 libevent 是否支持多线程? 我想实现的是这样的: 在单个线程中创建一个 event_base。 在这个单线程中设置事件并将它们关联到事
我正在遵循this示例。 Line#37表示工作线程数应等于cpu核心数。为什么? 如果有1万个连接,而我的系统有8个核心,这是否意味着8个辅助线程将处理1万个连接?为什么我不应该增加这个数字? 最佳
我正在学习如何使用 Libevent。虽然我无法理解 pending 和 active 之间的区别。在我看来,当一个事件被添加到 event_base 并且事件还没有发生时,它就处于 pending
我想知道是否有关于如何编译 libev-dev 的说明在 Linux (CentOS) 64 位上。我找不到与 libev 相关的开发包,也找不到任何有关其编译的教程。p.s:请不要使用包管理器 -
我想知道是否有关于如何编译 libev-dev 的说明在 Linux (CentOS) 64 位上。我找不到与 libev 相关的开发包,也找不到任何有关其编译的教程。p.s:请不要使用包管理器 -
我正在学习 libev 并且偶然发现了这个问题。假设我想尽快处理某些事情,但不是现在(即不在当前执行函数中)。例如,我想将一些大型同步作业分成多个部分,这些部分将排队,以便其他回调可以在两者之间触发。
我有干净的centos6系统,尝试从源代码编译tor。第一种方式(通过 yum 安装 libevent)。我愿意: yum install libevent2 yum install libevent
我在代码中有结构指针 struct evkeyvalq *headers; 现在我调用一个函数,该函数将在结构中填充一些信息。 evhttp_parse_query(uri, headers) 我在这
Rant:我真的不喜欢 boost::asio,所以我一直在寻找替代方案并遇到了 libev。这对我来说似乎很简单,但正在做一些我无法理解的事情。如果一个线程中的问题太多,请告诉我。 1) 我将监听套
如果我们主要想要一个基于 epoll 的文件描述符循环,那么 libevent 还提供什么功能(对 http 或dns 东西)?? 我知道这是一个很大的项目,但对我来说编写一个 epoll 包装器 A
我在我的 Linux 'c' 应用程序中将 libev 用于计时器和 io 事件,我想将自定义数据与事件相关联。在 libev 文档中,它暗示了 associating custom data但没有给
在 C 中使用 libev 创建小型网络服务器时,在不阻塞 react 器的情况下提供文件服务(打开、就绪、写入套接字)的最佳策略是什么? 我有一些建议一次阅读几个区 block ,但我不确定这是正确
我是一名优秀的程序员,十分优秀!