// 伺服器端#include <iostream>#include <event.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <string.h>#include <fcntl.h>using namespace std;struct event_base* main_base;static const char MESSAGE[] ="Hello, World!\n";void accept_handle(const int sfd, const short event, void *arg){cout<<"accept handle"<<endl;struct sockaddr_in addr;socklen_t addrlen = sizeof(addr);int fd = accept(sfd, (struct sockaddr *) &addr, &addrlen); //處理串連struct bufferevent* buf_ev;buf_ev = bufferevent_new(fd, NULL, NULL, NULL, NULL);buf_ev->wm_read.high = 4096;cout<<"event write"<<endl;bufferevent_write(buf_ev, MESSAGE, strlen(MESSAGE));}int main(){cout<<"hello man!"<<endl;// 1. 初始化EVENTmain_base = event_init();if(main_base)cout<<"init event ok!"<<endl;// 2. 初始化SOCKETint sListen;// Create listening socketsListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// Bindstruct sockaddr_in server_addr;bzero(&server_addr,sizeof(struct sockaddr_in));server_addr.sin_family=AF_INET;server_addr.sin_addr.s_addr=htonl(INADDR_ANY);int portnumber = 8080;server_addr.sin_port = htons(portnumber);/* 捆綁sockfd描述符 */if(bind(sListen,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1){cout<<"error!"<<endl;return -1;}// Listen::listen(sListen, 3);cout<<"Server is listening!\n"<<endl;/*將描述符設定為非阻塞*/int flags = ::fcntl(sListen, F_GETFL);flags |= O_NONBLOCK;fcntl(sListen, F_SETFL, flags);// 3. 建立EVENT 事件struct event ev;event_set(&ev, sListen, EV_READ | EV_PERSIST, accept_handle, (void *)&ev);// 4. 事件添加與刪除event_add(&ev, NULL);// 5. 進入事件迴圈event_base_loop(main_base, 0);cout<<"over!"<<endl;}
/******* 用戶端程式 client.c ************/#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <unistd.h>#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/types.h>#include <arpa/inet.h>int main(int argc, char *argv[]){ int sockfd; char buffer[1024]; struct sockaddr_in server_addr; struct hostent *host; int portnumber,nbytes; if((host=gethostbyname("10.1.39.93"))==NULL) { fprintf(stderr,"Gethostname error\n"); exit(1); } if((portnumber=atoi("8080"))<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); } /* 客戶程式填充服務端的資料 */ bzero(&server_addr,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); } /* 串連成功了 */ if((nbytes=read(sockfd,buffer,1024))==-1) { fprintf(stderr,"Read Error:%s\n",strerror(errno)); exit(1); } buffer[nbytes]='\0'; printf("I have received:%s\n",buffer); /* 結束通訊 */ close(sockfd); exit(0);}
(2)事件設定
socket服務建立後,就可以進行事件設定。我們使用event_set來設定事件對象,其傳入參數包括事件根基(event_base對象),描述符,事件類型,事件發生時的回呼函數,回呼函數傳入參數。其中事件類型包括EV_READ、EV_WRITE、EV_PERSIST,EV_PERSIST和前兩者結合使用,表示該事件為持續事件。
struct event ev;
event_set(&ev, listen_fd, EV_READ | EV_PERSIST, accept_handle, (void *)&ev);
(3)事件添加與刪除
事件設定好後,就可以將其加入事件隊列。event_add用來將事件加入,它接受兩個參數:要添加的事件和時間的逾時值。 如果需要將事件刪除,可以使用event_del來完成。event_del函數會取消所指定的事件。
event_add(&ev, NULL)
3 進入事件迴圈
事件成功添加後就是萬事具備只欠東風了,libevent提供了多種方式來進入事件迴圈,我個人常用的是event_dispatch和event_base_loop,前者最後實際是使用當前事件根基來調用event_base_loop。
event_base_loop(main_base, 0);
4 處理串連
到這裡為止,大家已經完成了事件的設定、事件的添加並進入到了事件迴圈。但是當事件發生時(這裡就是串連建立)如何處理呢? 聰明的使用者會想到前面我們在事件設定時指定的回呼函數accept_handle。沒錯,當串連建立時回呼函數accept_handle會自動的得到調用。
對於緩衝區的讀寫在非阻塞式網路編程中是一個難以處理的問題,幸運的是libevent提供了bufferevent和evbuf來替我們完成該項工作。這裡我們採用bufferevent來處理。
(1)產生bufferevent對象
使用bufferevent_new對象來產生bufferevent對象,並分別指定讀、寫、串連錯誤時的處理函數和函數傳入參數。
(2) 設定讀取量
bufferevent的讀事件啟用以後,即使使用者沒有讀取完bufferevent緩衝區中的資料, bufferevent讀事件也不會再次被啟用。因為bufferevent的讀事件是由其所監控的描述符的讀事件啟用的,只有描述符可讀,讀事件才會被啟用。可通過設定wm_read.high來控制bufferevent從描述符緩衝區中讀取的資料量。
(3) 將事件加入事件隊列
和前面一樣,在事件設定好後,需將事件加入到事件隊列中, 不過bufferevent的有自己專門的加入函數bufferevent_base_set和啟用函數bufferevent_enable。
bufferevent接收兩個參數事件根基個事件對象,前者用來指定事件將加入到哪個事件根基中,後者說明需將那個bufferevnet事件加入。(在多線程的情況下,每個線程可能有自己單獨的事件根基)
在bufferevent初始化完畢後,可以使用bufferevent_enable和bufferevent_disable反覆的啟用與禁止事件,其接收參數為事件對象和事件標誌。其中標誌參數為EV_READ和EV_WRITE。