libevent学习之网络通信
服务器端
要实现网络通信,肯定会用到socket等函数,这几个函数应该没什么问题。libevent默认情况下是单线程的,可以配置成多线程,每个线程有一个event_base,对应一个struct event _base结构体以及一个事件管理器,调度托管给它的一系列事件。所以当一个事件发生后,先创建一个event_base,再创建一个事件,将这个事件绑定,然后添加到event_base中,启动event_base的循环,开始处理事件。大概流程如下:
1 参数解析;
2. 创建socket连接;
3. struct event_base *base = event_base_new();创建一个event_base;
event_base_new()函数分配并且返回一个新的具有默认设置的 event_base。函数会检测环境变量,返回一个到 event_base 的指针。如果发生错误,则返回 NULL。选择各种方法时,函数会选择 OS 支持的最快方法。
4struct event* listen_event;创建一个监听事件;
5event_new(base,listenfd,EV_READ | EV_PERSIST,accept_cb,base);使用event_new()函数将监听事件绑定;
参数:event_base监听的对象,需要监听的事件,事件发生后的回调函数,传给回调函数的参数。libevent支持的事件及属性包括(使用bitfield实现)
EV_TIMEOUT:超时;
EV_READ:只要网络缓冲中还有数据,回调函数就会被触发;
EV_WRITE:只要塞给网络缓冲的数据被写完,回调函数就会被触发;
EV_SIGNAL:POSIX信号量;
EV_PERSIST:不指定这个属性,回调函数被触发后事件会被删除;
EV_ET:Edge-Trigger边缘触发
6event_add(listen_event,NULL);将监听事件绑定到event_base中;
7event_base_dispatch(base);启动循环开始处理事件;
8事件发生时的回调函数typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)
传给callback_func的是一个监听的事件类型fd,以及event_new中最后一个参数。
如下是代码:
int main(int argc,char **argv)
{
int listenfd;
int ch;
int port;
struct option opt[]={
{"port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{NULL,0,NULL,0}
};
while( (ch=getopt_long(argc,argv,"p:h",opt,NULL))!=-1 )
{
switch(ch)
{
case 'p':
port=atoi(optarg);
break;
case 'h':
print_help(argv[0]);
return 0;
}
}
printf("port:%d\n",port);
if( !port )
{
print_help(argv[0]);
return 0;
}
listenfd=socket_init(NULL,port);
if( listenfd<0 )
{
printf("socket_init failure!\n");
return -1;
}
printf("socket_init successfully!\n");
/*创建一个event_base*/
struct event_base *base = event_base_new();
assert(base != NULL);//设置event_base不为空
/*创建并绑定一个event*/
struct event* listen_event;
listen_event=event_new(base,listenfd,EV_READ | EV_PERSIST,accept_cb,base);
/*添加监听事件*/
event_add(listen_event,NULL);
/*启动循环,开始处理事件*/
event_base_dispatch(base);
return 0;
}
/*回调函数accept_cb*/
void accept_cb(int fd, short events, void* arg)
{
struct sockaddr_in cliaddr;
evutil_socket_t clifd;
socklen_t len=sizeof(cliaddr);
clifd=accept(fd,(struct sockaddr*)&cliaddr,&len);
if( clifd<0 )
{
printf("accept client %d failure!\n",clifd);
close(clifd);
}
evutil_make_socket_nonblocking(clifd);//设置为非阻塞模式
printf("accept client %d successfully!\n",clifd);
struct event_base* base = (struct event_base*)arg;
/*动态创建一个event结构体,并将其作为回调参数传递给*/
struct event* cli_event = event_new(NULL, -1, 0, NULL, NULL);
event_assign(cli_event,base, clifd, EV_READ | EV_PERSIST,read_cb, (void*)cli_event);
event_add(cli_event,NULL);
}
/*accept的回调函数read_cb*/
void read_cb(int fd, short events, void* arg)
{
int rv;
char buf[1024];
struct event* ev = (struct event*)arg;
rv=read(fd,buf,sizeof(buf));
if( rv<=0 )
{
printf("read message failure!\n");
event_free(ev);
close(fd);
return ;
}
printf("read message successfully:%s\n",buf);
char reply_buf[1024] = "I have received the msg: ";
strcat(reply_buf + strlen(reply_buf), buf);
write(fd,reply_buf,sizeof(reply_buf));
}
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
客户端
客户端的实现与服务器端基本相似,先实现socket的连接,然后创建event_base,创建并绑定监听事件,添加监听事件,启动循环,开始处理事件。代码如下:
int main(int argc, char** argv)
{
int ch;
int sockfd;
char *server_ip=NULL;
int server_port=0;
struct option opt[]={
{"server_ip",required_argument,NULL,'i'},
{"server_port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{NULL,0,NULL,0}
};
while( (ch=getopt_long(argc,argv,"i:p:h",opt,NULL))!=-1 )
{
switch(ch)
{
case 'i':
server_ip=optarg;
break;
case 'p':
server_port=atoi(optarg);
break;
case 'h':
print_help(argv[0]);
return 0;
}
}
if(!server_port)
{
print_help(argv[0]);
return 0;
}
sockfd=socket_connect(server_ip,server_port);
if( sockfd<0 )
{
printf("connect to server failure!\n");
return -1;
}
printf("connect to server successfully!\n");
struct event_base* base = event_base_new();
struct event *sockevent = event_new(base, sockfd, EV_READ | EV_PERSIST, read_cb, NULL);
event_add(sockevent, NULL);
//监听终端输入事件
struct event* ev_input = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, input_cb, (void*)&sockfd);
event_add(ev_input, NULL);
event_base_dispatch(base);
printf("finished \n");
return 0;
}
/*读回调函数*/
void read_cb(int fd, short events, void *arg)
{
char buf[1024];
int rv;
rv=read(fd,buf,sizeof(buf));
if( rv<=0 )
{
printf("read data from server %dfailure!\n",fd);
exit(1);
}
printf("read %d data from server:%s\n",rv,buf);
}
/*输入信息回调函数*/
void input_cb(int fd, short events, void* arg)
{
char buf[1024];
int rv;
rv=read(fd,buf,sizeof(buf));
if( rv<=0 )
{
printf("read failure!\n");
exit(1);
}
//把终端的消息发送给服务器端
int sockfd = *((int*)arg);
write(sockfd,buf,rv);
}
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
下面是运行结果:
客户端:
服务器端:
---------------------
libevent学习之网络通信的更多相关文章
- libevent学习之二:Windows7(Win7)下编译libevent
Linux下编译参考源码中的README文件即可,这里主要记录Windows下的编译. 一.准备工作 去官网下载最新的稳定发布版本libevent-2.0.22-stable 官网地址:http:// ...
- 【传智播客】Libevent学习笔记(一):简介和安装
目录 00. 目录 01. libevent简介 02. Libevent的好处 03. Libevent的安装和测试 04. Libevent成功案例 00. 目录 @ 01. libevent简介 ...
- PHP中的Libevent学习
wangbin@2012,1,3 目录 Libevent在php中的应用学习 1. Libevent介绍 2. 为什么要学习libevent 3. Php libeven ...
- libevent学习笔记 一、基础知识【转】
转自:https://blog.csdn.net/majianfei1023/article/details/46485705 欢迎转载,转载请注明原文地址:http://blog.csdn.net/ ...
- Libevent学习之SocketPair实现
Libevent设计的精化之一在于把Timer事件.Signal事件和IO事件统一集成在一个Reactor中,以统一的方式去处理这三种不同的事件,更确切的说是把Timer事件和Signal事件融合到了 ...
- libevent学习笔记 —— 牛刀小试:简易的服务器
回想起之前自己用纯c手动写epoll循环,libevent用起来还真是很快捷啊!重写了之前学习的时候的一个例子,分别用纯c与libevent来实现.嗯,为了方便对比一下,就一个文件写到黑了. 纯c版: ...
- Libevent学习笔记(五) 根据例子学习bufferevent
libevent中提供了一个Hello-world.c 的例子,从这个例子可以学习libevent是如何使用bufferevent的. 这个例子在Sample中 这个例子之前讲解过,这次主要看下buf ...
- libevent学习文档(三)working with event
Events have similar lifecycles. Once you call a Libevent function to set up an event and associate i ...
- Libevent 学习笔记 (1)——Libevent 2.0安装与简单演示样例
今天開始学习Libevent . Libevent 是开源社区的一款高性能I/O框架库. 主要特点有: 1 跨平台. 2 统一事件源 3 线程安全 4 基于Reactor 今天主要进行了Libeven ...
随机推荐
- UVA11183 Teen Girl Squad —— 最小树形图
题目链接:https://vjudge.net/problem/UVA-11183 You are part of a group of n teenage girls armed with cell ...
- VMware克隆虚拟机后网络配置
修复克隆虚拟机文件 # vi /etc/udev/rules.d/70-persistent-net.rules # PCI device 0x8086:0x100f (e1000) SUBSYSTE ...
- 测试SQL基础知识
SQL DML 和 DDL 可以把 SQL 分为两个部分:数据操作语言 (DML) 和 数据定义语言 (DDL). SQL (结构化查询语言)是用于执行查询的语法.但是 SQL 语言也包含用于更新.插 ...
- C# QRCode 二维码
/*********************************************************************** * C# QRCode 二维码 * 说明: * 本文记 ...
- 使用Python操作Redis应用场景
1. 安装pyredis 首先安装pip 1 2 3 4 5 6 7 8 <SHELL># apt-get install python-pip ...... <SHELL> ...
- bzoj1566 [NOI2009]管道取珠——DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1566 一眼看上去很懵... 但是答案可以转化成有两个人在同时取珠子,他们取出来一样的方案数: ...
- 简单记录CentOS服务器配置JDK+Tomcat+MySQL
项目需要部署到一台CentOS的服务器之上,之前这台服务器上面已经安装了一个Nginx和MySQL,跑的是PHP环境,有一个项目正在运行.而我们最新的项目是用Java写的,服务器是用的Tomcat,所 ...
- 转载别人的ognl
一.循环遍历集合 1.在jsp中引入标准函数声明<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix=" ...
- MySQL基础 -- 关系代数
MySQL基础 -- 关系代数 关系代数是一种抽象的查询语言,它用对关系的运算来表达查询. 任何一种运算都是将一定的运算符作用于一定的运算对象上,得到预期的结果.所以运算对象.运算符.运算结果是运算 ...
- bzoj 1680: [Usaco2005 Mar]Yogurt factory【贪心】
贪心,一边读入一边更新mn,用mn更新答案,mn每次加s #include<iostream> #include<cstdio> using namespace std; in ...