用簡單異步
可以輕松改造同步網絡庫從而獲得大幅性能提升,用它改造異步回調網絡庫可以讓我們以同步方式寫代碼,讓代碼更簡潔,可讀性更好,還能避免回調地獄
的問題.
本文通過兩個
例子分別來介紹如何用簡單異步
改造基于asio
的同步網絡庫和異步回調網絡庫.
示例
依賴了獨立版的asio
(commitid:f70f65ae54351c209c3a24704624144bfe8e70a3),它是headeronly
的直接包含頭文件即可.
改造同步網絡庫
以一個同步的echo server/client
為例,先來看看echoserver
:
空 開始服務器(異網::io環境&io環境,正 短 端口){傳控::受者 a(io環境,傳控::端點(傳控::v4(),端口));對(;;){動[錯誤,套接字]=接受(a);會話(標::移動(套接字));}
}
echoserver
啟動時先監聽端口,然后同步accept
,當有新連接到來時在會話
中處理網絡讀寫事件.
元<型名 異網緩沖>
標::雙<異網::錯誤碼,大小型>讀些(異網::ip::傳控::套接字&套接字, 異網緩沖&&緩沖){異網::錯誤碼 錯誤;大小型 長度=套接字.讀些(標::前向<異網緩沖>(緩沖),錯誤);中 標::造雙(錯誤,長度);
}空 會話(傳控::套接字 套接字){對(;;){常 大小型 最大長度=1024;符 數據[最大長度];動[錯誤,長度]=讀些(套接字,數據,最大長度);寫(套接字,數據,長度);}
}
在循環
中先同步讀數據
,再把讀到的數據
發送到對端.
同步的echo client
:
空 開始(異網::io環境&io環境,標::串 主機,標::串 端口){動[ec,套接字]=連接(io環境,主機,端口);常 整 最大長度=1024;符 寫緩沖[最大長度]={"你好 簡單異步"};符 讀緩沖[最大長度];常 整 數=10000;對(整 i=0;i<數;++i){寫(套接字,寫緩沖,最大長度);動[錯誤,回復長度]=讀些(套接字,讀緩沖,最大長度);//處理數據.}
}
同步的echoclient
也是類似的過程,先連接,再循環發送數據和讀數據.整個邏輯都是同步的,代碼簡單易懂.
當然,因為整個過程都是同步等待的,所以無法并發的處理網IO
事件,性能是比較差的.如果用簡單異步
來改造這個同步的網絡庫則可以大幅提升網絡庫的性能(參考benchmark),而且需要修改的代碼很少,這就是簡單異步
的威力.
用簡單異步
改造同步echoserver
簡單異步::協程::懶<空>會話(傳控::套接字 套接字){對(;;){常 大小型 最大長度=1024;符 數據[最大長度];動[錯誤,長度]=協待 異步讀些(套接字,異網::緩沖(數據,最大長度));協待 異步寫(套接字,異網::緩沖(數據,長度));}標::錯誤碼 ec;套接字.關閉(異網::ip::傳控::套接字::都關閉,ec);套接字.關閉(ec);標::輸出<<"完成回聲消息,總:"<<消息索引-1<<".\n";
}簡單異步::協程::懶<空>開始服務器(異網::io環境&io環境, 正 短 端口, 簡單異步::執行器*E){傳控::受者 a(io環境,傳控::端點(傳控::v4(),端口));對(;;){動[錯誤,套接字]=協待 異步接受(a);會話(標::移動(套接字)).通過(E).開始([](動&&){});}
}
可以看到用簡單異步
改造的start_server
和會話
相比,返回類型變成了Lazy
,同步接口變成了co_await async_xxx
,原有的同步邏輯沒有任何變化,這個微小的改動即可讓我們把同步網絡庫改成異步協程網絡庫從而讓性能得到巨大的提升.
更多的代碼細節請看demo_example
中echoserver/client
的例子.
改造異步回調網絡庫
對于一些已有的異步回調網絡庫,也可以用簡單異步
來消除回調,從而讓我們可以用同步方式去使用異步接口,讓代碼變得更簡潔易懂.
以asio
異步httpserver
為例:
空 連接::干讀()
{動 本(從本共享());套接字_.異步讀些(異網::緩沖(緩沖_),[本,本](標::錯誤碼 ec,標::大小型 傳輸字節){如(!ec){請求解析器::結果類型 結果;標::綁定(結果,標::忽略)=請求解析器_.解析(請求_,緩沖_.數據(),緩沖_.數據()+傳輸字節);如(結果==請求解析器::好){請求處理器_.請求處理(請求_,回復_);干寫();}異 如(結果==請求解析器::壞){回復_=回復::回復股票(回復::壞請求);干寫();}異{干讀();}}異 如(ec!=異網::錯誤::中止操作){連接管_.停止(從本共享());}});
}空 連接::干寫()
{動 本(從本共享());異網::異步寫(套接字_,回復_.到緩沖(),[本,本](標::錯誤碼 ec,標::大小型){如(!ec){異網::錯誤碼 忽略誤碼;套接字_.關閉(異網::ip::傳控::套接字::都關閉, 忽略誤碼);}如(ec!=異網::錯誤::中止操作){連接管_.停止(從本共享());}});
}
可以看到基于回調
的異步網絡庫的代碼,比同步
網絡庫復雜很多,如在異步讀的回調中如果沒有讀到完整數據需要遞歸調用do_read
,如果讀到完整數據之后才能在回調中調用異步寫接口.同時,還要注意將shared_from_this()
得到的std::shared_ptr
傳入到異步接口的回調函數中以保證安全的回調.總之,異步回調代碼的編寫難度較大,可讀性也較差,如果用簡單異步
改造則可以較好的解決這些問題,性能也不會降低.
用簡單異步
改造異步httpserver
簡單異步::協程::懶<空>干讀(){動 本(從本共享());對(;;){動[錯誤,傳輸字節]=協待 異步讀些(套接字_,異網::緩沖(讀緩沖_));如(錯誤){斷;}請求解析器::結果類型 結果;標::綁定(結果,標::忽略)=解析器_.解析(請求_,讀緩沖_,讀緩沖_+傳輸字節);如(結果==請求解析器::好){請求處理(請求_,響應_);協待 異步寫(套接字_,響應_.到緩沖());}異 如(結果==請求解析器::壞){響應_=構建響應(狀態類型::壞請求);協待 異步寫(套接字_,響應_.到緩沖());斷;}}
}
用簡單異步
改造之后的httpserver
完全消除了回調函數,完全可按同步
方式寫異步代碼
,簡潔易懂.
測試結果,提高2倍速度!