在Windows環(huán)境下實(shí)作一個(gè)簡(jiǎn)單的libevent伺服器
Oct 19, 2016 am 10:19 AM最近再學(xué)習(xí)Libevent由於自己使用的是windows系統(tǒng),遺憾的是有關(guān)在vs下可以參考的程序少之又少。在參考了許多的部落格文章後。自己摸摸寫了一個(gè)簡(jiǎn)單的Libevent Server程式。並且在網(wǎng)路上找了一個(gè)簡(jiǎn)單的客戶端程序,測(cè)試該代碼成功。今天在此做一個(gè)記錄。
Libevent的確是一個(gè)非常好用的東西,還在繼續(xù)學(xué)習(xí)中,後續(xù)還要在windows下實(shí)作Libevent的多執(zhí)行緒使用。今天先把自己搞出來的東西貼上來,僅供學(xué)習(xí)參考。在vs2015上編譯通過。
?
預(yù)設(shè)情況下是單線程的(可以配置成多線程,如果有需要的話),每個(gè)線程有且只有一event base,對(duì)應(yīng)一個(gè)struct event_base結(jié)構(gòu)(以及附於其上的事件管理器),用來schedule託管給它的一系列event,可以和作業(yè)系統(tǒng)的進(jìn)程管理類比,當(dāng)然,要更簡(jiǎn)單一點(diǎn)。當(dāng)一個(gè)事件發(fā)生後,event_base會(huì)在適當(dāng)?shù)臅r(shí)間(不一定是立即)去呼叫綁定在這個(gè)事件上的函數(shù)(傳入一些預(yù)先定義的參數(shù),以及在綁定時(shí)指定的一個(gè)參數(shù)),直到這個(gè)函數(shù)執(zhí)行完,再返回schedule其他事件。
?
//建立一個(gè)event_base
struct event_base *base = event_base_new();
assert(base != NULL);
?
/雜,直到有一個(gè)/ 一些事件發(fā)生,然後去處理這些事件。當(dāng)然,這些事件要被綁定在這個(gè)event_base上。每個(gè)事件對(duì)應(yīng)一個(gè)struct event,可以是監(jiān)聽一個(gè)fd或POSIX信號(hào)量之類(這裡只講fd了,其他的看manual吧)。 struct event使用event_new來建立和綁定,使用event_add來啟用:?//建立並綁定一個(gè)eventstruct event *listen_event;//參數(shù):event_base, 監(jiān)聽的屬性與監(jiān)聽的屬性,綁定的回呼函數(shù),給回呼函數(shù)的參數(shù)?listen_event = event_new(base, listener, EV_READ | EV_PERSIST, callback_func, (void*)base);/參數(shù): *類型的,NULL表示無逾時(shí)設(shè)定)event_add(listen_event, NULL);?注:libevent支援的事件及屬性包括(使用bitfield實(shí)現(xiàn),所以要用| 來讓它們合體)(abitfield實(shí)現(xiàn),所以要用| 來讓它們合體)(a ) EV_TIMEOUT: 逾時(shí)(b) EV_READ : 只要網(wǎng)路緩衝中還有數(shù)據(jù),回呼函數(shù)就會(huì)被觸發(fā)(c) EV_WRITE : 只要塞給網(wǎng)路緩衝的資料被寫完,回呼函數(shù)就會(huì)被觸發(fā)(d) EV_SIGNAL : POSIX信號(hào)量,參考manual吧(e) EV_PERSIST : 不指定這個(gè)屬性的話,回呼函數(shù)被觸發(fā)後事件會(huì)被刪除(f) EV_ET : Edge - Trigger邊緣觸發(fā),參考EPOLL_ET 接著需要啟動(dòng)event_base的循環(huán),這樣才能開始處理發(fā)生的事件。迴圈的啟動(dòng)event base dispatch,迴圈將持續(xù),直到不再有需要關(guān)注的事件,或是遇到event_loopbreak() / event_loopexit()函式。 //啟動(dòng)事件循環(huán)event_base_dispatch(base);接下來關(guān)注下綁定到event的回調(diào)函數(shù)callback_func:傳遞給它的是一個(gè)socket fd、一個(gè)event類型及屬性bit_field、以及傳遞給event_new的最後一個(gè)參數(shù)(去上面幾行回顧一下,把event_base傳進(jìn)來了,實(shí)際上更多地是分配一個(gè)結(jié)構(gòu)體,把相關(guān)的資料都撂進(jìn)去,然後丟給event_new,在這裡就能取得到了)。其原型是:typedef void(*event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)?對(duì)於一個(gè)伺服器而言,上面的流程大概是這樣組合的:1 月
list ),bind(),listen(),設(shè)定nonblocking(POSIX系統(tǒng)中可使用fcntl設(shè)置,windows不需要設(shè)置,
??? 實(shí)際上libevent提供了統(tǒng)一的包裝evutil_make_socket_nonblocking)
2. 建立一個(gè)一個(gè)event,將該socket託管給event_base,指定要監(jiān)聽的事件類型,並綁定上對(duì)應(yīng)的回呼函數(shù)(及需要給它的參數(shù))
??? 。對(duì)于listener socket來說,只需要監(jiān)聽EV_READ | EV_PERSIST
4. 啟用該事件
5. 進(jìn)入事件循環(huán)
-------------- -
6. (異步)當(dāng)有client發(fā)起請(qǐng)求的時(shí)候,調(diào)用該回調(diào)函數(shù),進(jìn)行處理。
/*接下來關(guān)注下綁定到event的回調(diào)函數(shù)callback_func:傳遞給它的是一個(gè)socket fd、一個(gè)event類型及屬性bit_field、以及傳遞給event_new的最后一個(gè)參數(shù)(去上面幾行回顧一下,把event_base給傳進(jìn)來了,實(shí)際上更多地是分配一個(gè)結(jié)構(gòu)體,把相關(guān)的數(shù)據(jù)都撂進(jìn)去,然后丟給event_new,在這里就能取得到了)。*/
?
服務(wù)器端代碼:Server.cpp
??1?#include??2?#include??3?#include??4?#include?<string.h>??5?#include??6?#include<event2/event.h>??7?#include??8?#include??9?#include?10?#pragma?comment?(lib,"ws2_32.lib")?11?#include?12?#define?LISTEN_PORT?9999?13?#define?LIATEN_BACKLOG?32?14?using?namespace?std;?15?/*********************************************************************************?16?*??????????????????????????????????????函數(shù)聲明?17?**********************************************************************************/?18?//accept回掉函數(shù)?19?void?do_accept_cb(evutil_socket_t?listener,?short?event,?void?*arg);?20?//read?回調(diào)函數(shù)?21?void?read_cb(struct?bufferevent?*bev,?void?*arg);?22?//error回調(diào)函數(shù)?23?void?error_cb(struct?bufferevent?*bev,?short?event,?void?*arg);?24?//write?回調(diào)函數(shù)?25?void?write_cb(struct?bufferevent?*bev,?void?*arg);?26?/*********************************************************************************?27?*??????????????????????????????????????函數(shù)體?28?**********************************************************************************/?29?//accept回掉函數(shù)?30?void?do_accept_cb(evutil_socket_t?listener,?short?event,?void?*arg)?31?{?32?????//傳入的event_base指針?33?????struct?event_base?*base?=?(struct?event_base*)arg;?34?????//socket描述符?35?????evutil_socket_t?fd;?36?????//聲明地址?37?????struct?sockaddr_in?sin;?38?????//地址長(zhǎng)度聲明?39?????socklen_t?slen?=?sizeof(sin);?40?????//接收客戶端?41?????fd?=?accept(listener,?(struct?sockaddr?*)&sin,?&slen);?42?????if?(fd?< 0) 43 { 44 perror("error accept"); 45 return; 46 } 47 printf("ACCEPT: fd = %u\n", fd); 48 ////注冊(cè)一個(gè)bufferevent_socket_new事件 49 struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); 50 ////設(shè)置回掉函數(shù) 51 bufferevent_setcb(bev, read_cb, NULL, error_cb, arg); 52 ////設(shè)置該事件的屬性 53 bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST); 54 } 55 ////read 回調(diào)函數(shù) 56 void read_cb(struct bufferevent *bev, void *arg) 57 { 58 #define MAX_LINE 256 59 char line[MAX_LINE + 1]; 60 int n; 61 //通過傳入?yún)?shù)bev找到socket fd 62 evutil_socket_t fd = bufferevent_getfd(bev); 63 // 64 while (n = bufferevent_read(bev, line, MAX_LINE)) 65 { 66 line[n] = '\0'; 67 printf("fd=%u, read line: %s\n", fd, line); 68 //將獲取的數(shù)據(jù)返回給客戶端 69 bufferevent_write(bev, line, n); 70 } 71 } 72 ////error回調(diào)函數(shù) 73 void error_cb(struct bufferevent *bev, short event, void *arg) 74 { 75 //通過傳入?yún)?shù)bev找到socket fd 76 evutil_socket_t fd = bufferevent_getfd(bev); 77 //cout << "fd = " << fd << endl; 78 if (event & BEV_EVENT_TIMEOUT) 79 { 80 printf("Timed out\n"); //if bufferevent_set_timeouts() called 81 } 82 else if (event & BEV_EVENT_EOF) 83 { 84 printf("connection closed\n"); 85 } 86 else if (event & BEV_EVENT_ERROR) 87 { 88 printf("some other error\n"); 89 } 90 bufferevent_free(bev); 91 } 92 ////write 回調(diào)函數(shù) 93 void write_cb(struct bufferevent *bev, void *arg) 94 { 95 char str[50]; 96 //通過傳入?yún)?shù)bev找到socket fd 97 evutil_socket_t fd = bufferevent_getfd(bev); 98 //cin >>?str;?99?????printf("輸入數(shù)據(jù)!");100?????scanf_s("%d",?&str);101?????bufferevent_write(bev,?&str,?sizeof(str));102?}103?104?int?main()105?{106?????int?ret;107?????evutil_socket_t?listener;108?????WSADATA??Ws;109?????//Init?Windows?Socket110?????if?(WSAStartup(MAKEWORD(2,?2),?&Ws)?!=?0)111?????{112?????????return?-1;113?????}114?????listener?=?socket(AF_INET,?SOCK_STREAM,?0);115?????assert(listener?>?0);116?????evutil_make_listen_socket_reuseable(listener);117?????struct?sockaddr_in?sin;118?????sin.sin_family?=?AF_INET;119?????sin.sin_addr.s_addr?=?0;120?????sin.sin_port?=?htons(LISTEN_PORT);121?????if?(bind(listener,?(struct?sockaddr?*)&sin,?sizeof(sin))?<?0)?{122?????????perror("bind");123?????????return?1;124?????}125?????if?(listen(listener,?1000)?<?0)?{126?????????perror("listen");127?????????return?1;128?????}129?????printf("Listening...\n");130?????evutil_make_socket_nonblocking(listener);131?????struct?event_base?*base?=?event_base_new();132?????assert(base?!=?NULL);133?????struct?event?*listen_event;134?????listen_event?=?event_new(base,?listener,?EV_READ?|?EV_PERSIST,?do_accept_cb,?(void*)base);135?????event_add(listen_event,?NULL);136?????event_base_dispatch(base);137?????printf("The?End.");138?????return?0;139?}
客戶端代碼:Client.cpp
?1?/*******?客戶端程序??client.c?************/?2?#define?_WINSOCK_DEPRECATED_NO_WARNINGS?3?#define?_CRT_SECURE_NO_WARNINGS?4?#include?5?#include?6?#include?7?#include?<string.h>??????? ?8?#include?9?#include10?#include11?12?#pragma?comment?(lib,"ws2_32.lib")13?int?main(int?argc,?char?*argv[])14?{15?????WSADATA??Ws;16?????//Init?Windows?Socket17?????if?(WSAStartup(MAKEWORD(2,?2),?&Ws)?!=?0)18?????{19?????????return?0;20?????}21?????int?sockfd;22?????char?buffer[1024];23?????struct?sockaddr_in?server_addr;24?????struct?hostent?*host;25?????int?portnumber,?nbytes;26?27?????if?((host?=?gethostbyname("127.0.0.1"))?==?NULL)28?????{29?????????fprintf(stderr,?"Gethostname?error\n");30?????????exit(1);31?????}32?33?????if?((portnumber?=?atoi("9999"))<0)34 {35 fprintf(stderr, "Usage:%s hostname portnumber\a\n", argv[0]);36 exit(1);37 }38 39 /* 客戶程序開始建立 sockfd描述符 */40 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)41 {42 fprintf(stderr, "Socket Error:%s\a\n", strerror(errno));43 exit(1);44 }45 46 /* 客戶程序填充服務(wù)端的資料 */47 memset(&server_addr,0, sizeof(server_addr));48 server_addr.sin_family = AF_INET;49 server_addr.sin_port = htons(portnumber);50 server_addr.sin_addr = *((struct in_addr *)host->h_addr);51?52?????/*?客戶程序發(fā)起連接請(qǐng)求?????????*/53?????if?(connect(sockfd,?(struct?sockaddr?*)(&server_addr),?sizeof(struct?sockaddr))?==?-1)54?????{55?????????fprintf(stderr,?"Connect?Error:%s\a\n",?strerror(errno));56?????????exit(1);57?????}58?59?????while?(true)60?????{61?????????char?MESSAGE[]?=?"hello?server..\n";62?????????//bufferevent_write(buf_ev,MESSAGE,strlen(MESSAGE));?? 63?????????//??64?????????if?(-1?==?(::send(sockfd,?MESSAGE,?strlen(MESSAGE),?0)))65?????????{66?????????????printf("the?net?has?a?error?occured..");67?????????????break;68?????????}69?70?????????if?((nbytes?=?recv(sockfd,?buffer,?1024,0))?==?-1)71?????????{72?????????????fprintf(stderr,?"read?error:%s\n",?strerror(errno));73?????????????exit(1);74?????????}75?76?????????buffer[nbytes]?=?'\0';77?????????printf("I?have?received:%s\n",?buffer);78?????????memset(buffer,?0,?1024);79?80?????????Sleep(2);81?82?????}83?????/*?結(jié)束通訊?????*/84?????closesocket(sockfd);85?????exit(0);86?87?????return?0;88?}
?
回復(fù)內(nèi)容:
[db:回復(fù)內(nèi)容]

熱AI工具

Undress AI Tool
免費(fèi)脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級(jí)程式碼編輯軟體(SublimeText3)