標籤:
如果你是偶然瀏覽到這裡,請先看
原始碼及常式:
命令列: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_server.h,就可實現一個簡單的服務端了,如下(還示範了一個echo伺服器,所以繼承st_server寫了個echo_server類):
//configuration #define SERVER_PORT 9527 #define REUSE_OBJECT //use objects pool //#define FORCE_TO_USE_MSG_RECV_BUFFER //force to use the msg recv buffer #define ENHANCED_STABILITY //configuration #include "st_asio_wrapper_server.h" using namespace st_asio_wrapper; #define QUIT_COMMAND "quit" #define RESTART_COMMAND "restart" #define LIST_ALL_CLIENT "list_all_client" #define LIST_STATUS "status" #define SUSPEND_COMMAND "suspend" #define RESUME_COMMAND "resume" //demonstrate how to use custom packer //in the default behavior, every st_tcp_socket has their own packer, and cause memory waste //at here, we make every echo_socket use the same global packer for memory saving //notice: do not do this for unpacker, because unpacker has member variables and can‘t share each other auto global_packer(boost::make_shared<packer>()); //demonstrates how to control the type of st_server_socket_base::server from template parameters class i_echo_server : public i_server { public: virtual void test() = 0; }; class echo_socket : public st_server_socket_base<boost::asio::ip::tcp::socket, i_echo_server> { public: echo_socket(i_server& server_) : st_server_socket_base(server_) {inner_packer(global_packer);} public: //because we use objects pool(REUSE_OBJECT been defined), so, strictly speaking, this virtual //function must be rewrote, but we don‘t have member variables to initialize but invoke father‘s //reset() directly, so, it can be omitted, but we keep it for possibly future using virtual void reset() {st_server_socket_base::reset();} protected: virtual void on_recv_error(const error_code& ec) { //the type of st_server_base::server now can be controled by derived class(echo_socket), //which is actually i_echo_server, so, we can invoke i_echo_server::test virtual function. server.test(); st_server_socket_base::on_recv_error(ec); } //msg handling: send the original msg back(echo server) #ifndef FORCE_TO_USE_MSG_RECV_BUFFER //this virtual function doesn‘t exists if FORCE_TO_USE_MSG_RECV_BUFFER been defined virtual bool on_msg(msg_ctype& msg) {post_msg(msg); return false;} #endif //we should handle the msg in on_msg_handle for time-consuming task like this: virtual void on_msg_handle(msg_ctype& msg) {post_msg(msg);} //please remember that we have defined FORCE_TO_USE_MSG_RECV_BUFFER, so, st_tcp_socket will directly //use the msg recv buffer, and we need not rewrite on_msg(), which doesn‘t exist any more //msg handling end }; class echo_server : public st_server_base<echo_socket, st_object_pool<echo_socket>, i_echo_server> { public: echo_server(st_service_pump& service_pump_) : st_server_base(service_pump_) {} //from i_echo_server, pure virtual function, we must implement it. virtual void test() {/*puts("in echo_server::test()");*/} }; int main() { puts("type quit to end these two servers."); std::string str; st_service_pump service_pump; st_server server_(service_pump); //only need a simple server? you can directly use st_server server_.set_server_addr(SERVER_PORT + 100); echo_server echo_server_(service_pump); //echo server service_pump.start_service(1); 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 == LIST_STATUS) { printf("normal server:\nvalid links: " size_t_format ", closed links: " size_t_format "\n", server_.size(), server_.closed_object_size()); printf("echo server:\nvalid links: " size_t_format ", closed links: " size_t_format "\n", echo_server_.size(), echo_server_.closed_object_size()); } //the following two commands demonstrate how to suspend msg dispatching, no matter recv buffer been used or not else if (str == SUSPEND_COMMAND) echo_server_.do_something_to_all(boost::bind(&echo_socket::suspend_dispatch_msg, _1, true)); else if (str == RESUME_COMMAND) echo_server_.do_something_to_all(boost::bind(&echo_socket::suspend_dispatch_msg, _1, false)); else if (str == LIST_ALL_CLIENT) { puts("clients from normal server:"); server_.list_all_object(); puts("clients from echo server:"); echo_server_.list_all_object(); } else server_.broadcast_msg(str); } return 0; } //restore configuration #undef SERVER_PORT #undef REUSE_OBJECT //use objects pool //#undef FORCE_TO_USE_MSG_RECV_BUFFER //force to use the msg recv buffer #undef ENHANCED_STABILITY //restore configuration
以上例子中,服務端從控制台接收資料,調用broadcast_msg廣播資料;當收到資料時,會輸出到控制台(st_tcp_socket實現);
其中st_server server_;這行申請了一個普通的服務端,它的功能僅僅是發送接收訊息,接受串連。一般來說,就像教程一裡面的用戶端一樣,需要從st_server_socket繼承一個自己的通訊端類,從st_server繼承一個服務類。為此,服務端demo還示範了一個echo伺服器,它會把收到的任何資料發送回去(大家可以學著做一個echo用戶端,但不要echo服務端與echo用戶端一同工作,否則就死迴圈了。
start_service開啟服務,stop_service結束服務(退出時必須明確調用),is_running判斷服務的運行狀態;如果想修改服務端地址,則在調用start_service之前調用set_server_addr函數;
stop_service之後,可再次調用start_service開啟服務;
注意:st_server的del_client一般用於服務端被動刪除某個client(即在錯誤發生的時候,比如在st_tcp_socket的on_recv_error和on_send_error裡面調用,參看st_server_socket);服務端如果想主動關閉某個client,建議調用這個client的force_close或者graceful_close(st_tcp_socket實現)函數,它們的調用最終會促使on_recv_error的調用;
st_server的close_all_client主動關閉所有client,比如服務端退出的時候(stop_service會自動調用);
重寫st_server的on_accept函數,根據你自己的策略確定是否接受用戶端的串連,接受返回true;
st_server的維護了一個鏈表(st_object_pool實現)用於儲存所有的client(這樣帶幾個好處:一、在廣播訊息的時候,很方便;二、可開啟類似記憶體回收機制的自動清理已經關閉的串連的功能;三、可開啟對象池功能),如果你想自己管理這些client,可以在on_accept裡面返回false,然後把它儲存在自己的容器裡面,並調用start(st_server_socket實現)以便開始接受資料(只調用一次即可);
當然,你還可以在返回true的同時,自己也儲存一份client(此時就不要再調用start了),這樣做不會帶來多少記憶體消耗,因為它是用智能指標封裝的,複製一份只是增加一個引用計數。至於這樣做有什麼好處,如果你想不到,說明你不需要,當你有需求的時候,你自然而然就會知道有什麼用了,我在這裡只是告訴大家可以這樣做,有個印象即可;
關於是重寫on_msg還是on_msg_handle,請參看教程第四篇。
boost.asio封裝類st_asio_wrapper開發教程(2013.12.8更新)(二)