1. 程式人生 > >Linux C 開發

Linux C 開發

12 evutil_make_listen_socket_reuseable(server_socketfd);//設定埠重用  vutil_make_socket_nonblocking(server_socketfd);//設定無阻塞

2. 我們首選建立的事件主要用於監聽客戶端的連入。當客戶端有socket連線到伺服器端的時候,回撥函式do_accept就會去執行;當空閒的時候,這個事件就會是一個pending等待狀態,等待有新的連線進來,新的連線進來了之後又會繼續執行。

12 structevent*ev;ev=event_new(base_ev,server_socketfd,EV_TIMEOUT|EV_READ|EV_PERSIST,do_accept,base_ev);

3. 在do_accept事件中我們建立了一個新的事件,這個事件的回撥函式是do_read。主要用來迴圈監聽客戶端上傳的資料。do_read這個方法會一直迴圈執行,接收到客戶端資料就會進行處理。

12345 //建立一個事件,這個事件主要用於監聽和讀取客戶端傳遞過來的資料  
//持久型別,並且將base_ev傳遞到do_read回撥函式中去  structevent*ev;ev=event_new(base_ev,client_socketfd,EV_TIMEOUT|EV_READ|EV_PERSIST,do_read,base_ev);event_add(ev,NULL);

Bufferevent
上面的socket例子估計經過測試估計大家就會有很多疑問:
1. do_read方法作為一個事件會一直被迴圈
2. 當客戶端連線斷開的時候,do_read方法還是在迴圈,根本不知道客戶端已經斷開socket的連線。
3. 需要解決各種粘包和拆包(相關粘包拆包文章)問題

如果要解決這個問題,我們可能要做大量的工作來維護這些socket的連線狀態,讀取狀態。而Libevent的Bufferevent幫我們解決了這些問題。
Bufferevent主要是用來管理和排程IO事件;而Evbuffer(下面一節會講到)主要用來緩衝網路IO資料。
Bufferevent目前支援TCP協議,而不知道UDP協議。我們這邊也只講TCP協議下的Bufferevent的使用。
我們先看下下面的介面(然後結合下面改進socket的例子,自己動手去實驗一下):
1. 建立Bufferevent API

12 //建立一個Bufferevent  structbufferevent*bufferevent_socket_new(structevent_base*base,evutil_socket_t fd,enumbufferevent_options options);

引數:
base:即event_base
fd:檔案描述符。如果是socket的方法,則socket需要設定為非阻塞的模式。
options:行為選項,下面是行為選項內容
1. BEV_OPT_CLOSE_ON_FREE :當 bufferevent 被釋放同時關閉底層(socket 被關閉等) 一般用這個選項
2. BEV_OPT_THREADSAFE :為 bufferevent 自動分配鎖,這樣能夠在多執行緒環境中安全使用
3. BEV_OPT_DEFER_CALLBACKS : 當設定了此標誌,bufferevent 會延遲它的所有回撥(參考前面說的延時回撥)
4. BEV_OPT_UNLOCK_CALLBACKS : 如果 bufferevent 被設定為執行緒安全的,使用者提供的回撥被呼叫時 bufferevent 的鎖會被持有。如果設定了此選項,Libevent 將在呼叫你的回撥時釋放 bufferevent 的鎖

2. 釋放Bufferevent

1 voidbufferevent_free(structbufferevent*bev);

如果設定了延時回撥BEV_OPT_DEFER_CALLBACKS,則釋放會在延時回撥呼叫了回撥函式之後,才會真正釋放。

3. 設定Bufferevent的回撥函式和相關設定
前面我們說過了,使用了Bufferevent之後,Libevent會幫我們託管三種事件:1. 讀取事件  2. 寫入事件  3. 處理事件
我們先看一下回調函式結構:
1. 讀取和寫入的回撥函式結構,其中 ctx為通用傳遞的引數

1 typedefvoid(*bufferevent_data_cb)(structbufferevent*bev,void*ctx);

2. 事件回撥,即連線斷開、錯誤處理等回撥。其中ctx為通用傳遞的引數。
events引數為事件,使用者可以在回撥函式中拿到這個事件來進行事務處理的判斷:
1. BEV_EVENT_READING   在 bufferevent 上進行讀取操作時出現了一個事件
2. BEV_EVENT_WRITING  在 bufferevent 上進行寫入操作時出現了一個事件
3. BEV_EVENT_ERROR  進行 bufferevent 操作時出錯
4. BEV_EVENT_TIMEOUT  在 bufferevent 上出現了超時
5. BEV_EVENT_EOF  在 bufferevent 上遇到了檔案結束符,連線斷開
6. BEV_EVENT_CONNECTED 在 bufferevent 上請求連線完成了

1 typedefvoid(*bufferevent_event_cb)(structbufferevent*bev,shortevents,void*ctx);

3. 在bufferevent上設定回撥函式。
bufev:bufferevent_socket_new建立的bufferevent
readcb:讀取事件的回撥函式,沒有則可以為NULL
writecb:寫入事件的回撥函式,沒有則可以為NULL
eventcb:事件函式的回撥函式,沒有則可以為NULL,一般我們可以在這裡面判斷連線斷開等。
cbarg:公用傳輸的傳遞
通過這個函式,我們就可以設定我們需要的一些回撥函式資訊。

12 voidbufferevent_setcb(structbufferevent*bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void*cbarg);

取回回撥函式:

12 voidbufferevent_getcb(structbufferevent*bufev,bufferevent_data_cb*readcb_ptr,bufferevent_data_cb*writecb_ptr,bufferevent_event_cb*eventcb_ptr,void**cbarg_ptr)

4. 設定Bufferevent事件的型別

1 bufferevent_enable(bev,EV_READ|EV_WRITE|EV_PERSIST);

5. 水位設定。
水位設定可以這麼理解,bufferevent相當於一個水位容器,其中引數:
events:EV_READ 則為設定讀取事件;EV_WRITE 則為寫入事件。EV_READ |  EV_WRITE 為設定兩者的水位。
lowmark:最低水位,預設為0。這個引數非常重要,例如lowmark設定為10,則當bufferevent容器中有10個字元的時候才會去呼叫readcb這個回撥函式。

1 voidbufferevent_setwatermark(structbufferevent*bufev,shortevents,size_t lowmark,size_t highmark);

6. 下面可以看一個設定和回撥函式例子:

12345678910111213141516171819202122232425262728293031323334353637 //建立一個bufferevent  structbufferevent*bev=bufferevent_socket_new(base_ev,client_socketfd,BEV_OPT_CLOSE_ON_FREE);//設定讀取方法和error時候的方法  bufferevent_setcb(bev,read_cb,NULL,error_cb,base_ev);//設定型別  bufferevent_enable(bev,EV_READ|EV_WRITE|EV_PERSIST);//設定水位  bufferevent_setwatermark(bev,EV_READ,0,0);//讀取事件回撥函式  voidread_cb(structbufferevent*bev,void*arg){#define MAX_LINE    256  charline[MAX_LINE+1];intn;evutil_socket_t fd=bufferevent_getfd(bev);while(n=bufferevent_read(bev,line,MAX_LINE),n>0){line[n]='\0';printf("fd=%u, read line: %s\n",fd,line);bufferevent_write(bev,line,n);}puts("haha");}//寫入事件回撥函式  voidwrite_cb(structbufferevent*bev,void*arg){}//事件回撥  voiderror_cb(structbufferevent*bev,shortevent,void*arg){evutil_socket_t fd=bufferevent_getfd(bev);printf("fd = %u, ",fd);if(event&BEV_EVENT_TIMEOUT){printf("Timed out\n");}elseif(event&BEV_EVENT_EOF){printf("connection closed\n");}elseif(event&BEV_EVENT_ERROR){printf("some other error\n");}bufferevent_free(bev);}

4. 輸入輸出相關函式
1. 獲取buffer:

1234 // 獲取到輸入 buffer  structevbuffer*bufferevent_get_input(structbufferevent*bufev);// 獲取到輸出 buffer  structevbuffer*bufferevent_get_output(structbufferevent*bufev);

2. 寫入和輸出函式,成功返回0,失敗返回-1:
bufev:bufferevent
data:寫入的字串資料
size:字元長度

1234 //寫入  intbufferevent_write(structbufferevent*bufev,constvoid*data,size_t size);//輸出  size_t bufferevent_read(structbufferevent*bufev,void*data,size_t size);

3. 寫入輸出函式2:
bufev:bufferevent
buf:buffer塊  下面會講到evbuffer的使用

12 intbufferevent_write_buffer(structbufferevent*bufev,structevbuffer*buf);intbufferevent_read_buffer(structbufferevent*bufev,structevbuffer*buf);

使用Bufferevent後的Socket例子:
上面我們已經介紹完了Bufferevent的相關API,可以看下具體例子。

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061