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。 void accept_handle(const int sfd, const short event, void *arg){ struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); int fd = accept(sfd, (struct sockaddr *) &addr, &addrlen); //處理串連 buf_ev = bufferevent_new(fd, buffered_on_read, NULL, NULL, fd) buf_ev->wm_read.high = 4096 bufferevent_base_set(main_base, buf_ev); bufferevent_enable(buf_ev, EV_READ); } 5 讀取緩衝區 當緩衝區讀就緒時會自動啟用前面註冊的緩衝區讀函數,我們可以使用bufferevent_read函數來讀取緩衝區 bufferevent_read函數參數分別為:所需讀取的事件緩衝區,讀入資料的存放地,希望讀取的位元組數。函數返回實際讀取的位元組數。 注意:及時緩衝區未讀完,事件也不會再次被啟用(除非再次有資料)。因此此處需反覆讀取直到全部讀取完畢。 6 寫回用戶端 bufferevent系列函數不但支援讀取緩衝區,而且支援寫緩衝區(即將結果返回給用戶端)。 void buffered_on_read(struct bufferevent *bev, void * arg){ char buffer[4096] ret = bufferevent_read(bev, &buffer, 4096); bufferevent_write(bef, (void *)&buffer, 4096); } 三 結束語 至此我們已經可以使用libevent編寫非阻塞的事件驅動伺服器,它支援串連建立、socket可讀等事件的處理。 但在實際的使用事件驅動的伺服器中,通常是使用一個線程處理串連,然後使用多個線程來處理請求。後面我將繼續介紹如何使用libevent來編寫多線程的伺服器。 |