boost asio異步通信


異步操作思想
  用戶發起異步事件,asio將這些異步事件投遞到一個隊列中,用戶發起的操作就返回了,io_service::run會處理異步事件隊列中的所有的異步事件,它會將這些事件交給操作系統處理,操作系統處理完成之後會丟到asio的事件完成的隊列中,io_service發現有完成隊列中有完成事件了,就會通知用戶處理完成事件。 所以用戶要發起一個異步操作需要做三件事:
搜索
  調用asio異步操作接口,發起異步操作;如:async_connect、async_read、async_write,這些異步接口需要一個回調函數入參,這個回調函數在事件完成時,由io_service觸發。
  調用io_service::run處理異步事件;發起一個異步操作,必須要保證io_service::run,因爲io_service通過一個循環去處理這些異步操作事件的,如果沒有事件就會退出,所以要保證異步事件發起之後,io_service::run還在運行。要保證一直run的一個簡單辦法就是使用io_service::work,它可以保證io_service一直run。
  處理異步操作完成事件;在調用異步接口時會傳入一個回調函數,這個回調函數就是處理操作完成事件的,比如讀完成了,用戶需要對這些數據進行業務邏輯的處理。

  asio的的核心是io_service, 理解了asio異步接口的機制就容易找出使用asio過程中出現的問題了,在這裏把一些常見的問題列出來,並分析原因和提出解決方法。

  問題1:爲什麼我發起了異步操作,如連接或者寫,對方都沒有反應,好像沒有收到連接請求或者沒有收到數據? 答案:一個很可能的原因是io_service在異步操作發起之後沒有run,解決辦法是保持io_service的run。
  問題2:爲什麼發送數據會報錯? 答案:一個可能的原因是發送的數據失效了,異步發送要求發送的數據在回調完成之前都有效,異步操作只是將異步事件句柄投遞到io_service隊列中就返回了,並不是阻塞的,不注意這一點,如果是臨時變量的數據,除了作用域就失效了,導致異步事件還沒完成時數據就失效了。解決辦法,保證發送數據在事件完成之前一直有效。
  問題3:爲什麼監聽socket時,會報“函數不正確”的異常? 答案:因爲監聽時,也要保證這個socket一直有效,如果是一個臨時變量socket,在調用異步監聽後超出作用域就失效了,解決辦法,將監聽的socket保存起來,使它的生命週期和acceptor一樣長。
  問題4:爲什麼連續調用異步操作時會報錯? 答案:因爲異步操作必須保證當前異步操作完成之後再發起下一次異步操作。解決辦法:在異步完成事件處理完成之後再發起新的異步操作即可。
  問題5:爲什麼對方半天收不到數據,過了半天才一下子收到之前發送的數據? 答案:因爲socket是流數據,一次發送多少數據不是外界能控制的,這也是所謂的粘包問題。解決辦法,可以在接收時指定至少收多少的條件,或者做tcp分包處理。
  說了這麼多,還是來看看例子吧,一個簡單的通信程序:服務端監聽某個端口,允許多個客戶端連接上來,服務器將客戶端發來的數據打印出來。 先看看服務端的需求,需求很簡單,第一,要求能接收多個客戶端;第二,要求把收到的數據打印出來。

  要求能接收多個客戶端是第一個要解決的問題,異步接收需要用到acceptor::async_accept,它接收一個socket和一個完成事件的回調函數。前面的問題3中提到監聽的這個socket不能是臨時變量,我們要把它保存起來,最好是統一管理起來。可以考慮用一個map去管理它們,每次一個新連接過來時,服務器自動分配一個連接號給這個連接,以方便管理。然而,socket是不允許拷貝的,所以不能直接將socket放入容器中,還需要外面包裝一層纔可以。

  第二個問題是打印來自客戶端的數據,既然要打印就需要異步讀數據了。異步讀是有socket完成,這個socket還要完成讀寫功能,爲了簡化用戶操作,我將socket封裝到一個讀寫事件處理器中,這個事件處理器只具備具備讀和寫的功能。服務器每次監聽的時候我都會創建一個新的事件處理器並放到一個map中,客戶端成功連接後就由這個事件處理器去處理各種讀寫事件了。 根據問題1,異步讀寫時要保證數據的有效性,這裏我將一個固定大小的緩衝區作爲讀緩衝區。爲了簡單起見我使用同步發送,異步接收。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章