您好,登錄后才能下訂單哦!
這篇文章主要介紹“C++ BoostAsyncSocket如何實現異步反彈通信”,在日常操作中,相信很多人在C++ BoostAsyncSocket如何實現異步反彈通信問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”C++ BoostAsyncSocket如何實現異步反彈通信”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
Boost 利用ASIO框架實現一個跨平臺的反向遠控程序,該遠控支持保存套接字,當有套接字連入時,自動存儲到map容器,當客戶下線時自動從map容器中移除,當我們需要與特定客戶端通信時,只需要指定客戶端ID號即可。
服務端首先定義CEventHandler
類并繼承自CAsyncTcpServer::IEventHandler
接口,該類內需要我們實現三個方法,方法ClientConnected
用于在客戶端連接時觸發,方法ClientDisconnect
則是在登錄客戶端離開時觸發,而當客戶端有數據發送過來時則ReceiveData
方法則會被觸發。
方法ClientConnected
當被觸發時自動將clientId
客戶端Socket套接字放入到tcp_client_id
全局容器內存儲起來,而當ClientDisconnect
客戶端退出時,則直接遍歷這個迭代容器,找到序列號并通過tcp_client_id.erase
將其剔除;
// 客戶端連接時觸發 virtual void ClientConnected(int clientId) { // 將登錄客戶端加入到容器中 tcp_client_id.push_back(clientId); } // 客戶端退出時觸發 virtual void ClientDisconnect(int clientId) { // 將登出的客戶端從容器中移除 vector<int>::iterator item = find(tcp_client_id.begin(), tcp_client_id.end(), clientId); if (item != tcp_client_id.cend()) tcp_client_id.erase(item); }
而ReceiveData
一旦收到數據,則直接將其打印輸出到屏幕,即可實現客戶端參數接收的目的;
// 客戶端獲取數據 virtual void ReceiveData(int clientId, const BYTE* data, size_t length) { std::cout << std::endl; PrintLine(80); std::cout << data << std::endl; PrintLine(80); std::cout << "[Shell] # "; }
相對于接收數據而言,發送數據則是通過同步的方式進行,當我們需要發送數據時,只需要將數據字符串放入到一個BYTE*
字節數組中,并在調用tcpServer.Send
時將所需參數,套接字ID,緩沖區Buf數據,以及長度傳遞即可實現將數據發送給指定的客戶端;
// 同步發送數據到指定的線程中 void send_message(CAsyncTcpServer& tcpServer, int clientId, std::string message, int message_size) { // 獲取長度 BYTE* buf = new BYTE(message_size + 1); memset(buf, 0, message_size + 1); for (int i = 0; i < message_size; i++) { buf[i] = message.at(i); } tcpServer.Send(clientId, buf, message_size); }
客戶端首先我們封裝實現AsyncConnect
類,該類內主要實現兩個功能,其中aysnc_connect
方法用于實現異步連接到服務端,而port_is_open
方法則用于驗證服務器特定端口是否開放,在調用boost::bind
綁定套接字時傳入&AsyncConnect::timer_handle
設置一個超時等待時間。
進入到main
主函數中,通過while
循環讓程序可以一直運行下去,并通過hander.aysnc_connect(ep, 5000)
每隔5秒驗證是否連接成功,如果連接了則進入內循環,通過hander.port_is_open("127.0.0.1", 10000, 5000)
驗證端口是否開放,這主要是為了保證服務端斷開后客戶端依然能夠跳轉到外部循環繼續等待服務端上線。
首先運行服務端程序,接著運行多個客戶端,即可實現自動上線;
當用戶需要通信時,只需要指定id序號到指定的Socket套接字編號即可;
服務端代碼
// 署名權 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #include "AsyncTcpServer.h" #include <string> #include <vector> #include <iostream> #include <boost/tokenizer.hpp> using namespace std; // 存儲當前客戶端的ID號 std::vector<int> tcp_client_id; // 輸出特定長度的行 void PrintLine(int line) { for (int x = 0; x < line; x++) { printf("-"); } printf("\n"); } class CEventHandler : public CAsyncTcpServer::IEventHandler { public: // 客戶端連接時觸發 virtual void ClientConnected(int clientId) { // 將登錄客戶端加入到容器中 tcp_client_id.push_back(clientId); } // 客戶端退出時觸發 virtual void ClientDisconnect(int clientId) { // 將登出的客戶端從容器中移除 vector<int>::iterator item = find(tcp_client_id.begin(), tcp_client_id.end(), clientId); if (item != tcp_client_id.cend()) tcp_client_id.erase(item); } // 客戶端獲取數據 virtual void ReceiveData(int clientId, const BYTE* data, size_t length) { std::cout << std::endl; PrintLine(80); std::cout << data << std::endl; PrintLine(80); std::cout << "[Shell] # "; } }; // 同步發送數據到指定的線程中 void send_message(CAsyncTcpServer& tcpServer, int clientId, std::string message, int message_size) { // 獲取長度 BYTE* buf = new BYTE(message_size + 1); memset(buf, 0, message_size + 1); for (int i = 0; i < message_size; i++) { buf[i] = message.at(i); } tcpServer.Send(clientId, buf, message_size); } int main(int argc, char* argv[]) { CAsyncTcpServer tcpServer(10, 10000); CEventHandler eventHandler; tcpServer.AddEventHandler(&eventHandler); std::string command; while (1) { std::cout << "[Shell] # "; std::getline(std::cin, command); if (command.length() == 0) { continue; } else if (command == "help") { printf(" _ ____ _ _ \n"); printf("| | _ _ / ___| ___ ___| | _____| |_ \n"); printf("| | | | | | \\___ \\ / _ \\ / __| |/ / _ \\ __| \n"); printf("| |__| |_| | ___) | (_) | (__| < __/ |_ \n"); printf("|_____\\__, | |____/ \\___/ \\___|_|\\_\\___|\\__| \n"); printf(" |___/ \n\n"); printf("Usage: LySocket \t PowerBy: LyShark.com \n"); printf("Optional: \n\n"); printf("\t ShowSocket 輸出所有Socket容器 \n"); printf("\t GetCPU 獲取CPU數據 \n"); printf("\t GetMemory 獲取內存數據 \n"); printf("\t Exit 退出客戶端 \n\n"); } else { // 定義分詞器: 定義分割符號為[逗號,空格] boost::char_separator<char> sep(", --"); typedef boost::tokenizer<boost::char_separator<char>> CustonTokenizer; CustonTokenizer tok(command, sep); // 將分詞結果放入vector鏈表 std::vector<std::string> vecSegTag; for (CustonTokenizer::iterator beg = tok.begin(); beg != tok.end(); ++beg) { vecSegTag.push_back(*beg); } // 解析 [shell] # ShowSocket if (vecSegTag.size() == 1 && vecSegTag[0] == "ShowSocket") { PrintLine(80); printf("客戶ID \t 客戶IP地址 \t 客戶端口 \n"); PrintLine(80); for (int x = 0; x < tcp_client_id.size(); x++) { std::cout << tcp_client_id[x] << " \t " << tcpServer.GetRemoteAddress(tcp_client_id[x]) << " \t " << tcpServer.GetRemotePort(tcp_client_id[x]) << std::endl; } PrintLine(80); } // 解析 [shell] # GetCPU --id 100 if (vecSegTag.size() == 3 && vecSegTag[0] == "GetCPU") { char *id = (char *)vecSegTag[2].c_str(); send_message(tcpServer, atoi(id), "GetCPU", strlen("GetCPU")); } // 解析 [shell] # GetMemory --id 100 if (vecSegTag.size() == 3 && vecSegTag[0] == "GetMemory") { char* id = (char*)vecSegTag[2].c_str(); send_message(tcpServer, atoi(id), "GetMEM", strlen("GetMEM")); } // 解析 [shell] # Exit --id 100 if (vecSegTag.size() == 3 && vecSegTag[0] == "Exit") { char* id = (char*)vecSegTag[2].c_str(); send_message(tcpServer, atoi(id), "Exit", strlen("Exit")); } } } return 0; }
客戶端代碼
// 署名權 // right to sign one's name on a piece of work // PowerBy: LyShark // Email: me@lyshark.com #define BOOST_BIND_GLOBAL_PLACEHOLDERS #include <iostream> #include <string> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/array.hpp> #include <boost/date_time/posix_time/posix_time_types.hpp> #include <boost/noncopyable.hpp> using namespace std; using boost::asio::ip::tcp; // 異步連接地址與端口 class AsyncConnect { public: AsyncConnect(boost::asio::io_service& ios, tcp::socket &s) :io_service_(ios), timer_(ios), socket_(s) {} // 異步連接 bool aysnc_connect(const tcp::endpoint &ep, int million_seconds) { bool connect_success = false; // 異步連接,當連接成功后將觸發 connect_handle 函數 socket_.async_connect(ep, boost::bind(&AsyncConnect::connect_handle, this, _1, boost::ref(connect_success))); // 設置一個定時器 million_seconds timer_.expires_from_now(boost::posix_time::milliseconds(million_seconds)); bool timeout = false; // 異步等待 如果超時則執行 timer_handle timer_.async_wait(boost::bind(&AsyncConnect::timer_handle, this, _1, boost::ref(timeout))); do { // 等待異步操作完成 io_service_.run_one(); // 判斷如果timeout沒超時,或者是連接建立了,則不再等待 } while (!timeout && !connect_success); timer_.cancel(); return connect_success; } // 驗證服務器端口是否開放 bool port_is_open(std::string address, int port, int timeout) { try { boost::asio::io_service io; tcp::socket socket(io); AsyncConnect hander(io, socket); tcp::endpoint ep(boost::asio::ip::address::from_string(address), port); if (hander.aysnc_connect(ep, timeout)) { io.run(); io.reset(); return true; } else { return false; } } catch (...) { return false; } } private: // 如果連接成功了,則 connect_success = true void connect_handle(boost::system::error_code ec, bool &connect_success) { if (!ec) { connect_success = true; } } // 定時器超時timeout = true void timer_handle(boost::system::error_code ec, bool &timeout) { if (!ec) { socket_.close(); timeout = true; } } boost::asio::io_service &io_service_; boost::asio::deadline_timer timer_; tcp::socket &socket_; }; int main(int argc, char * argv[]) { try { boost::asio::io_service io; tcp::socket socket(io); AsyncConnect hander(io, socket); boost::system::error_code error; tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"), 10000); // 循環驗證是否在線 go_: while (1) { // 驗證是否連接成功,并定義超時時間為5秒 if (hander.aysnc_connect(ep, 5000)) { io.run(); std::cout << "已連接到服務端." << std::endl; // 循環接收命令 while (1) { // 驗證地址端口是否開放,默認等待5秒 bool is_open = hander.port_is_open("127.0.0.1", 10000, 5000); // 客戶端接收數據包 boost::array<char, 4096> buffer = { 0 }; // 如果在線則繼續執行 if (is_open == true) { socket.read_some(boost::asio::buffer(buffer), error); // 判斷收到的命令是否為GetCPU if (strncmp(buffer.data(), "GetCPU", strlen("GetCPU")) == 0) { std::cout << "獲取CPU參數并返回給服務端." << std::endl; socket.write_some(boost::asio::buffer("CPU: 15 %")); } // 判斷收到的命令是否為GetMEM if (strncmp(buffer.data(), "GetMEM", strlen("GetMEM")) == 0) { std::cout << "獲取MEM參數并返回給服務端." << std::endl; socket.write_some(boost::asio::buffer("MEM: 78 %")); } // 判斷收到的命令是否為終止程序 if (strncmp(buffer.data(), "Exit", strlen("Exit")) == 0) { std::cout << "終止客戶端." << std::endl; return 0; } } else { // 如果連接失敗,則跳轉到等待環節 goto go_; } } } else { std::cout << "連接失敗,正在重新連接." << std::endl; } } } catch (...) { return false; } std::system("pause"); return 0; }
到此,關于“C++ BoostAsyncSocket如何實現異步反彈通信”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。