另一种实现非阻塞网络通信的方法———使用libev

来源:互联网 发布:耐克鞋淘宝网 编辑:程序博客网 时间:2024/06/06 18:47

背景:最近终于开始了我的实习生之路,本来在进公司之前还比较紧张,尽管拿到了offer,因为毕竟这是一个新的起点,一开始从学生到员工这个身份的转变让我有些不太适应,但是还好在公司里遇到了人超级好的软件经理Alex以及其他精明能干的小伙伴们,所以这个过渡时间也很快。

一开始Alex让我通过公司的一个项目了解libev这个库,我在看同事写的代码的过程中遇到的问题实在太多,由于我之前写的和这个项目类似的代码实现非阻塞网络连接都是通过select方法,但是同事写的代码中大量使用到了libev中的API,对于从未接触过的我来说想看懂真的是痴人说梦,但是我相信我的学习能力,于是我就开始了自己对libev的探索之路。

libev是一个事件驱动的编程框架,通过向libev中注册感兴趣的事件以及相应的回调函数,那么libev会对所注册的事件源进行管理,并在事件发生时调用对应的回调函数进行处理。我们的例子中就是对socket的连接事件,读写事件进行监听并调用相应的回调函数进行处理。

网上有许多介绍libev的博客和代码,博客一般都写得很容易懂,但是代码一般就只是伪码了,想要运行必须自己不断地debug,我在综合了各种博文中的方法之后写出了如下代码,功能是同一个服务端提供多个客户端连接,接收到客户端连接之后把消息回传,若客户端收到q,则断开连接。


服务器端代码如下

#include<stdio.h>#include<stdlib.h>#include<malloc.h>#include<ev.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/socket.h>#include<sys/types.h>#include<string.h>#define PORT 9999#define BUFFER_SIZE 1024int total_clients=0;void accept_cb(struct ev_loop *loop,struct ev_io *watcher,int revents);void read_cb(struct ev_loop *loop,struct ev_io *watcher,int revents);int main(){struct ev_loop *loop=ev_default_loop(0);int sd;struct sockaddr_in addr;//int addr_len=sizeof(addr);struct ev_io socket_accept;if((sd=socket(AF_INET,SOCK_STREAM,0))<0){printf("socket error");return -1;}bzero(&addr,sizeof(addr));addr.sin_family=AF_INET;addr.sin_port=htons(PORT);addr.sin_addr.s_addr=INADDR_ANY;if(bind(sd,(struct sockaddr*)&addr,sizeof(addr))!=0){printf("bind error");}if(listen(sd,0)<0){printf("listen error");return -1;}ev_io_init(&socket_accept,accept_cb,sd,EV_READ);ev_io_start(loop,&socket_accept);while(1){ev_loop(loop,0);}return 0;}void accept_cb(struct ev_loop *loop,struct ev_io *watcher,int revents){//struct sockaddr_in client_addr;int client_sd;struct ev_io *w_client=(struct ev_io*)malloc(sizeof(struct ev_io));if(EV_ERROR & revents){printf("error event in accept");return ;}//client_sd=accept(watcher->fd,(struct sockaddr *)&client_addr,&client_len);client_sd=accept(watcher->fd,NULL,NULL);if(client_sd<0){printf("accept error");return;}total_clients++;printf("successfully connected with client.\n");printf("%d client connected .\n",total_clients);ev_io_init(w_client,read_cb,client_sd,EV_READ);ev_io_start(loop,w_client);}void read_cb(struct ev_loop *loop,struct ev_io *watcher,int revents){char buffer[BUFFER_SIZE];int read;if(EV_ERROR & revents){printf("error event in read");return;}read=recv(watcher->fd,buffer,BUFFER_SIZE,0);if(read==0){ev_io_stop(loop,watcher);free(watcher);perror("peer might closing");total_clients--;printf("%d client connected .\n",total_clients);return;}else{buffer[read]='\0';printf("get the message: %s\n",buffer);}send(watcher->fd,buffer,read,0);bzero(buffer,read);}
客户端代码如下

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<arpa/inet.h>#include<sys/socket.h>#include<netinet/in.h>#define PORT 9999#define BUFFER_SIZE 1024int main(){int sd;struct sockaddr_in addr;//int addr_len=sizeof(addr);char buffer[BUFFER_SIZE]="";if((sd=socket(AF_INET,SOCK_STREAM,0))<0){perror("socket error");return -1;}bzero(&addr,sizeof(addr));addr.sin_family=AF_INET;addr.sin_port=htons(PORT);addr.sin_addr.s_addr=htonl(INADDR_ANY);if(connect(sd,(struct sockaddr *)&addr,sizeof(addr))<0){perror("connect error");return -1;}while(strcmp(buffer,"q")!=0){scanf("%s",buffer);send(sd,buffer,strlen(buffer),0);recv(sd,buffer,BUFFER_SIZE,0);printf("message:%s\n",buffer);}return 0;}
编译的时候用gcc -Wall  -lev参数,-Wall用来显示所有警告和错误,在我的程序里是没有的,因为本人亲测可用,-lev代表编译的时候链接上libev这个库。



0 0