Linux服务器程序处理的三类事件
I/O事件、信号事件和定时事件。
I/O框架库以库函数的形式,封装了较为底层的系统调用。
I/O框架库的组件
基于Reactor模式的I/O框架库包含的组件:句柄、事件多路分发器、事件处理器和具体的事件处理器。
-
句柄
I/O框架库要处理的对象,即I/O事件、信号事件和定时事件,统一称为事件源。
I/O事件对应的句柄是文件描述符,信号事件对应的句柄是信号值。 -
事件多路分发器
即事件循环。内部调用的是select、poll、epoll_wait. -
事件处理器和具体事件处理器
- 定义实例;
- 创建事件;
- 注册(添加)到libevent;
- 事件循环;
Libevent支持的事件类型:
代码实现
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<assert.h>
6 #include<event.h>
7 #include<signal.h>
8
9 void sig_cb(int fd,short ev,void*arg)//回调函数
10 {
11 printf("sig=%d\n",fd);
12 }
13 void time_cb(int fd,short ev,void*arg)
14 {
15 printf("time out\n");
16 }
17 int main()
18 {
19 struct event_base*base=event_init();//定义一个实例(libevent) 框架
20 assert(base!=NULL);
21
22 struct event*sig_ev=evsignal_new(base,SIGINT,sig_cb,NULL); //定义信号事> 件
23 assert(sig_ev!=NULL);
24
25 event_add(sig_ev,NULL);//添加(注册)事件到libevent实例
26
27 struct timeval tv={5,0};
28 struct event*time_ev=evtimer_new(base,time_cb,NULL);//定义定时事件
29 assert(time_ev!=NULL);
30 event_add(time_ev,&tv);//设置(添加)超时事件
31
32 event_base_dispatch(base); //启动事件循环
33
34 event_free(sig_ev);
35 event_free(time_ev);
36 event_base_free(base);
37
38 exit(0);
39 }
编译时,注意连接库,加上 -levent。
通过Libevent 检测多个客户端连接
自动调用I/O函数处理并发事件
客户端
#include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<assert.h>
6 #include<sys/socket.h>
7 #include<netinet/in.h>
8 #include<arpa/inet.h>
9
10 int main()
11 {
12 int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建套接字
13 assert(sockfd!=-1);
14
15 //bind()//可以绑定,但是一般不绑定
16
17 struct sockaddr_in saddr;
18 memset(&saddr,0,sizeof(saddr));
19 saddr.sin_family=AF_INET;
20 saddr.sin_port=htons(6000);
21 saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
22
23 int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//三次握手
24 assert(res!=-1);
25 while(1)
26 {
27 char buff[128]={0};
28 printf("input:\n");
29 fgets(buff,128,stdin);
30
31 if(strncmp(buff,"end",3)==0)
32 {
33 break;
34 }
35 send(sockfd,buff,strlen(buff),0);
36
37 memset(buff,0,128);
38 recv(sockfd,buff,127,0);
39 printf("buff=%s\n",buff);
40 }
41 close(sockfd);
42 }
服务器
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<assert.h>
6 #include<sys/socket.h>
7 #include<netinet/in.h>
8 #include<arpa/inet.h>
9 #include<event.h>
10
11 void recv_cb(int fd,short ev,void*arg)
12 {
13 if(ev&EV_READ)
14 {
15 char buff[128]={0};
16 int n=recv(fd,buff,127,0);
17 if(n<=0)
18 {
19 struct event**p_cev=(struct event**)arg;
20 event_free(*p_cev);
21 free(p_cev);
23 close(fd);
22 printf("cilent close\n");
23 return ;
24 }
25 printf("recv:%s\n",buff);
26
27 send(fd,"ok",2,0);//写事件就绪,不用注册写事件,直接send
28 }
29 }
30 void accept_cb(int fd,short ev,void* arg)
31 {
32 struct event_base* base=(struct event_base*)arg;
33 if(base==NULL)
34 {
35 return ;
36 }
37 if(ev&EV_READ)
38 {
39 struct sockaddr_in caddr;
40 int len=sizeof(caddr);
41 int c=accept(fd,(struct sockaddr*)&caddr,&len);//接受连接
42 if(c<0)
43 {
44 return ;
45 }
46 printf("accept c=%d\n",c);
47 struct event**p_cev=(struct event**)malloc(sizeof(struct event*));
48 if(p_cev==NULL)
49 {
50 return ;
51 }
52 *p_cev=event_new(base,c,EV_READ|EV_PERSIST,recv_cb,p_cev);//将c添加到libevent实例中
53 // struct event*c_ev=event_new(base,c,EV_READ|EV_PERSIST,recv_cb,base);
54
55 if(*p_cev==NULL)
56 {
57 return ;
58 }
59 event_add(*p_cev,NULL);
60 }
61
62 }
63
64 int socket_init();
65 int main()
66 {
67 struct event_base*base=event_init();
68 assert(base!=NULL);
69
70 int sockfd=socket_init();
71 assert(sockfd!=-1);
72
73 struct event*sock_ev=event_new(base,sockfd,EV_READ|EV_PERSIST,accept_cb,base);
74 event_add(sock_ev,NULL);
75
76 event_base_dispatch(base);//启动事件循环
77 event_free(sock_ev);
78 event_base_free(base);
79 }
80
81 int socket_init()
82 {
83 int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建套接字
84 if(sockfd==-1)
85 {
86 return -1;
87 }
88
89 struct sockaddr_in saddr;
90 memset(&saddr,0,sizeof(saddr));
91 saddr.sin_family=AF_INET;
92 saddr.sin_port=htons(6000);
93 saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
94 int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//指定监听> 套接子使用的是本主机的端口
95 if(res==-1)
96 {
97 return -1;
98 }
99
100 res=listen(sockfd,5);
101 if(res==-1)
102 {
103 return -1;
104 }
105 return sockfd;
106
107 }