boost.asio包裝類st_asio_wrapper開發教程(一)

一:什麼是st_asio_wrapper
它是一個c/s網絡編程框架,基於對boost.asio的包裝(最低在boost-1.49.0上調試過),目的是快速的構建一個c/s系統;

二:st_asio_wrapper的特色
效率高、跨平臺、徹底異步,固然這是從boost.asio繼承而來;
自動重連,數據透明傳輸,自動解決分包粘包問題(必須使用默認的打包解包器,這一特性表現得與udp同樣);
只支持tcp和udp協議;

三:st_asio_wrapper的大致結構
st_asio_wrapper.h:
編譯器版本測試,更新日誌等;

st_asio_wrapper_base.h:
存放一些全局類、函數、宏以及日誌輸出等;

st_asio_wrapper_timer.h(class st_timer):
定時器類,如下類均須要,除了打包解包器;

st_asio_wrapper_socket.h(class st_socket):
st_tcp_socket和st_udp_socket的基類,主要負責消息派發相關功能;

st_asio_wrapper_tcp_socket.h(class st_tcp_socket):
tcp套接字類,用於tcp數據的收發,從st_socket繼承;

st_asio_wrapper_udp_socket.h(class st_udp_socket):
udp套接字類,用於udp數據的收發,從st_socket繼承;

st_asio_wrapper_packer.h(interface i_packer, class packer):
i_packer是打包器的接口,packer是st_asio_wrapper自帶的打包器;

st_asio_wrapper_unpacker.h(interface i_unpacker, class unpacker):
i_unpacker是解包器的接口,unpacker是st_asio_wrapper自帶的解包器;

st_asio_wrapper_service_pump.h(interface i_service, class st_service_pump):
asio的io_servie包裝類,用於運行st_asio_wrapper的全部service對象(st_server_base, st_sclient, st_tcp_client_base, st_udp_client_base);

st_asio_wrapper_object_pool.h(class st_object_pool):
對象池,從原來的st_server_base抽象出來,供st_server_base和st_client繼承,因而乎,服務端和客戶端(支持多條鏈接的)都有了對象池功能;

st_asio_wrapper_server.h(class st_server_base):
st_server是服務端的服務對象類,用於服務端的監聽、接受客戶端請求等,須要實現i_server接口;

st_asio_wrapper_server_socket.h(interface i_server, class st_server_socket):
前者用於從st_server獲取必要的信息,和調用必要的接口;後者從st_tcp_socket繼承,用於服務端的數據收發;

st_asio_wrapper_connector.h(class st_connector):
從st_tcp_socket繼承,實現鏈接服務器(包括重連)邏輯;

st_asio_wrapper_client.h(class st_sclient_base, class st_client_base):
client相關的公共邏輯,好比遍歷等,被st_tcp_client_base和st_udp_client_base繼承;

st_asio_wrapper_tcp_client.h(class st_tcp_sclient, class st_tcp_client_base):
前者從st_connector繼承,只支持一條鏈接,後者支持任意多條鏈接,他們實現了一些邏輯,以便被st_service_pump調用;

st_asio_wrapper_udp_client.h(class st_udp_sclient, class st_udp_client_base):
基於udp套接字的service對象;

st_asio_wrapper_ssl.h(class st_ssl_connector_base, class st_ssl_object_pool, class st_ssl_server_socket_base, class st_ssl_server_base):
全部st_asio_wrapper庫支持的ssl相關的類庫;

源代碼及例程下載地址:
命令行:svn checkout http://st-asio-wrapper.googlecode.com/svn/trunk/ st-asio-wrapper-read-only
若是從svn客戶端界面上打開,則只輸入http://st-asio-wrapper.googlecode.com/svn/trunk/到地址欄便可
git:https://github.com/youngwolf-project/st_asio_wrapper/,另外,個人資源裏面也有下載,但不是最新的。
QQ交流羣:198941541
其中include文件夾裏面是st_asio_wrapper的全部類、asio_client和asio_server配合用於演示最簡單的數據收發、file_server和file_client用於演示文件傳送、test_client和asio_server配合用於性能測試、udp_client演示udp通訊,他們都基於st_asio_wrapper,能夠當作是sample;

        類庫(包括demo)在vc下有幾個警告,都可安全忽略;
        類庫裏面大量使用了c++0x特性,主要有:range-based for loop、lambda表達式、nullptr、auto、右值引用、泛型begin()和end()等;所以至少須要vc2010及其以上版本的編譯器,或者gcc4.6以上;
        在vc2010和gcc4.6以前的編譯器版本中,請使用兼容版本,在compatiable_edition文件夾裏面(注意兼容版本的效率並不比普通版本低,甚至略高);
        推薦在能用的狀況下,仍是用普通版本(或者說標準版本,這是相對於兼容版本而言的),雖然效率沒有提升,但你用在一個複雜的工程中時,可能普通版本效率會高,由於相同的代碼下,c++0x的效率要廣泛的高一些,在st_asio_wrapper裏面沒有表現出來,是由於我特別的對兼容版本作過優化(並且個人使用也有限,好比有些沒法優化的地方,我恰好不須要使用,就躲過去了);
        須要開發者本身編譯boost庫,大概須要boost.system和boost.thread兩個庫。

五:開發教程(客戶端)
客戶端直接#include st_asio_wrapper_client.h,就可實現一個簡單的客戶端了,以下:c++

 
//configuration  

#define SERVER_PORT     9527  

#define FORCE_TO_USE_MSG_RECV_BUFFER //force to use the msg recv buffer  

//configuration  

#include "../include/st_asio_wrapper_tcp_client.h"  

using namespace st_asio_wrapper;  

#define QUIT_COMMAND    "quit"  

#define RESTART_COMMAND "restart"  

#define RECONNECT_COMMAND "reconnect"  

#define SUSPEND_COMMAND "suspend"  

#define RESUME_COMMAND  "resume"  

int main() {  

    std::string str;  

    st_service_pump service_pump;  

    st_tcp_sclient client(service_pump);  

//there is no corresponding echo client demo as server endpoint  

//because echo server with echo client made dead loop, and occupy almost all the network resource  

    client.set_server_addr(SERVER_PORT + 100, SERVER_IP);  

//  client.set_server_addr(SERVER_PORT, "::1"); //ipv6  

//  client.set_server_addr(SERVER_PORT, "127.0.0.1"); //ipv4  

    service_pump.start_service();  

while(service_pump.is_running())  

    {  

        std::cin >> str;  

if (str == QUIT_COMMAND)  

            service_pump.stop_service();  

else if (str == RESTART_COMMAND)  

        {  

            service_pump.stop_service();  

            service_pump.start_service(1);  

        }  

else if (str == RECONNECT_COMMAND)  

            client.graceful_close(true);  

//the following two commands demonstrate how to suspend msg sending, no matter recv buffer been used or not  

else if (str == SUSPEND_COMMAND)  

            client.suspend_send_msg(true);  

else if (str == RESUME_COMMAND)  

            client.suspend_send_msg(false);  

else  

            client.safe_send_msg(str);  

    }  

}  

//restore configuration  

#undef SERVER_PORT  

#undef FORCE_TO_USE_MSG_RECV_BUFFER //force to use the msg recv buffer  

//restore configuration  

 

 

以上例子中,客戶端從控制檯接收數據,調用safe_send_msg發送數據;當收到數據時,會輸出到控制檯(st_tcp_socket或者st_udp_socket實現);

        其中,start_service開啓服務,stop_service結束服務(退出時必須明確調用),is_running判斷服務的運行狀態;若是想修改服務端地址,則在調用start_service以前調用set_server_addr函數;
        stop_service以後,可再次調用start_service開啓服務;
        不stop_service而直接想重鏈接,則以true調用st_connector的force_close或者graceful_close;
        固然,通常來講,客戶端不會只把收到的數據顯示到控制檯這麼簡單,此時須要從st_tcp_sclient或者st_udp_sclient派生一個類(若是你有多條鏈接,則從st_connector或者st_udp_socket派生一個類,並用st_tcp_client或者st_udp_client來管理它,這個請參考test_client這個demo,它支持多條鏈接),而後有兩種方法供你選擇,一:重寫on_msg並在裏面直接返回true(或者定義FORCE_TO_USE_MSG_RECV_BUFFER宏,至於爲何要這樣,你須要看完全部st_asio_wrapper的一系列教程共四篇),而後再重寫on_msg_handle並在裏面作消息處理;二:重寫on_msg,並在裏面作消息處理,而後返回false。這兩種方式的區別是:前者不會阻塞消息的接收,後者會,好比你的消息處理須要1秒,那麼在這1秒鐘以內,前者與接收消息並行,後者則不能接受消息,直到消息處理結束。另外一個區別是:前者須要接收緩存,後者不須要。固然,這裏說的阻塞,是指對當前套接字的阻塞,與其它套接字無關,好比在服務端,有兩個套接字,當你處理套接字1的消息時,不管你用前面的哪種方法,都不可能阻塞套接字2的消息接收。關於這個話題,請參看教程第四篇
        在消息發送成功以後,你有一次機會獲得通知,那就是重寫on_msg_send函數,它的參數就是這個消息(注意,是打包後的);
        每調用一次send_msg或者safe_send_msg,對方一定收到一次如出一轍的數據,二次開發者不用再考慮分包粘包問題(必須使用默認的打包解包器),這一特色你徹底能夠把它當成udp的行爲;
        st_socket裏面有發送消息緩存,因此當二次開發者有數據須要發送的時候,能夠隨時調用send_msg(注意緩存滿的問題,safe_send_msg保證數據發送成功,當緩存滿時會等待)而不用關心當前鏈接是否已經創建,它會在條件適當的時候,爲你發送數據。

你可能須要修改的宏有如下幾類:
1.全局宏,服務端客戶端均須要:
        UNIFIED_OUT_BUF_NUM:unified_out類使用的緩存空間大小,默認2048;
        MAX_MSG_LEN:消息最大長度(打包後的,拿默認的packer來講,它最大僅支持3998消息長度,由於還有一個2字節的包頭),默認4000。對於默認打包器解包器,這個長度範圍只能是1至(65535-2),由於包頭只用了兩個字節的緣故,若是想要超過這個限制,可定義HUGE_MSG宏;
        HUGE_MSG:開啓大消息支持,默認關閉。注意,大消息會佔用大內存,請看asio_client這個demo,裏面有演示用法(註釋狀態),以及對於佔用大內存的解釋;
        MAX_MSG_NUM:每一個st_socket消息緩存最大消息條數(接收和發送緩存共用,因此總的最大消息條數是2倍的MAX_MSG_NUM),默認1024;
        ENHANCED_STABILITY:加強的健壯性,若是開啓這個宏,全部service對象都會在最外層(io_service.run)包一層try catch,以增長健壯性,固然,增長try是會有效率損失的,本宏默認不開啓;
        FORCE_TO_USE_MSG_RECV_BUFFER:始終使用消息接收緩存,這個是編譯期優化,前面說了,on_msg()返回true表明使用消息接收緩存,若是你的on_msg()永遠返回true,則能夠開啓這個宏,那麼st_tcp_socket或者st_udp_socket將再也不調用on_msg()(根本不存在這個虛函數了)而是直接將消息放入消息接收緩存,這樣能夠提升一些效率,默認不開啓本宏;
        NO_UNIFIED_OUT:讓st_asio_wrapper裏面的全部輸出失效;
        CUSTOM_LOG:自定義輸出,此時你須要提供5個日誌函數,函數名和簽名參看unified_out類,且要麼是靜態的,要麼是全局的。
        DEFAULT_PACKER:自定義打包器,它是一個類名,必須提供默認構造函數(即沒有參數的構造函數)。
        DEFAULT_UNPACKER:自定義解包器,它是一個類名,必須提供默認構造函數(即沒有參數的構造函數)。
        ST_SERVICE_THREAD_NUM:IO線程數量,用於運行io_service.run函數。
        MAX_OBJECT_NUM:對象池最多支持的對象數量,默認4096;
        REUSE_OBJECT:是否開啓對象池,若是開啓,當建立新對象時,將嘗試使用已經被關閉的對象,此時將不會自動定時的釋放被關閉的對象鏈表,請參看SOCKET_FREE_INTERVAL宏,默認不開啓本宏;
        SOCKET_FREE_INTERVAL:說這個宏以前,要說一下st_object_pool的內部工做原理:當調用del_object的時候(st_server_socket在on_recv_error裏面的默認行爲),st_object_pool的做法並非刪除這個對象,而是把這個對象移動到另一個鏈表裏面,這個鏈表裏面的對象會被每SOCKET_FREE_INTERVAL秒遍歷一次,以找到已經關閉超過CLOSED_SOCKET_MAX_DURATION秒鐘的對象,而後真正的從內存中釋放它,緣由是當del_object的時候,可能還有其它的異步操做還沒完成,或者完成了,但還在隊列中沒有被分發,若是此時從內存中釋放對象,那麼後面的異步回調的時候,可能會出現內存訪問越界。這個問題能夠經過爲每個異步調用都綁定一個指向本對象this指針的shared_ptr作爲參數,但過分使用shared_ptr會影響到效率。單位爲秒,默認10秒。若是開啓了REUSE_OBJECT,則本宏根本不使用(也就不存在定時遍歷了),由於被關閉的對象已經放入對象池,等着被重用,不能釋放;
        AUTO_CLEAR_CLOSED_SOCKET:服務端是否認時的循環的調用clear_all_closed_object()以清除已經被關閉的套接字,若是你不方便調用del_object,則對象池裏面將會累積愈來愈多的被關閉了的對象(若是有鏈接出錯,或者退出的話),你能夠打開這個宏,讓st_object_pool爲你定時的作這些清除工做;注意,若是對象鏈表很是大,遍歷鏈表是會影響效率的;遍歷的時間間隔由CLEAR_CLOSED_SOCKET_INTERVAL定義,默認不開啓本宏;
        CLEAR_CLOSED_SOCKET_INTERVAL:定時調用clear_all_closed_object()間隔,單位爲秒,默認60秒,若是未開啓AUTO_CLEAR_CLOSED_SOCKET,則本宏根本不使用(也就不存在定時調用clear_all_closed_object()了);
        CLOSED_SOCKET_MAX_DURATION:關閉鏈接多少秒鐘以後,能夠安全釋放對象或者重用對象(取決於REUSE_OBJECT是否被定義,定義了就是重用,不然釋放),默認爲5秒。

2.tcp客戶端專用宏:
        GRACEFUL_CLOSE_MAX_DURATION:優雅關閉時,最長等待時間,單位爲秒,默認5。
        SERVER_IP:服務器IP地址,用字符串形式表示,默認"127.0.0.1";
        SERVER_PORT:服務器端口,默認5050;
        RE_CONNECT_INTERVAL:當鏈接服務器失敗時,延時多長時間重連,單位是毫秒,默認500毫秒;
        RE_CONNECT_CONTROL:打開此宏以後,能夠控制重鏈接次數,運行時也可修改。

3.tcp服務端專用宏:
        SERVER_PORT:服務器端口(服務器IP若是要設置的話,只能調用set_server_addr接口,不能經過宏來實現),默認5050;
        TCP_DEFAULT_IP_VERSION:在不指定服務端IP時,經過這個宏指定IP協議的版本(v4仍是v6,取值分別是boost::asio::ip::tcp::v4()和boost::asio::ip::tcp::v6()),若是指定了IP,則版本從ip地址中分析得來;
        ASYNC_ACCEPT_NUM:最多同時投遞多少個異步accept調用,默認1;
        NOT_REUSE_ADDRESS:關閉端口重用,udp也用使用此宏。

4.udp客戶端專用宏:
        UDP_DEFAULT_IP_VERSION:在不指定IP時,經過這個宏指定IP協議的版本(v4仍是v6,取值分別是boost::asio::ip::udp::v4()和boost::asio::ip::udp::v6()),若是指定了IP,則版本從ip地址中分析得來;

以上宏均可以按工程爲單位來修改,你只須要在include相應st_asio_wrapper相關頭文件以前,定義這些宏便可,具體例子demo裏面都有。git

 

 

boost.asio包裝類st_asio_wrapper開發教程(2015.6.5更新)(一)github

 

文檔和demo編程

https://github.com/youngwolf-project/st_asio_wrapper/緩存

相關文章
相關標籤/搜索