服务器端
要实现网络通信,肯定会用到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
",port);
if( !port )
{
print_help(argv[0]);
return 0;
}
listenfd=socket_init(NULL,port);
if( listenfd<0 )
{
printf("socket_init failure!
");
return -1;
}
printf("socket_init successfully!
");
/*创建一个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!
",clifd);
close(clifd);
}
evutil_make_socket_nonblocking(clifd);//设置为非阻塞模式
printf("accept client %d successfully!
",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!
");
event_free(ev);
close(fd);
return ;
}
printf("read message successfully:%s ",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!
");
return -1;
}
printf("connect to server successfully!
");
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
");
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!
",fd);
exit(1);
}
printf("read %d data from server:%s ",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!
");
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
下面是运行结果:
客户端:
服务器端:
---------------------