BoostAsyncSocket 異步反彈通信案例
Boost 利用ASIO框架實(shí)現(xiàn)一個(gè)跨平臺的反向遠(yuǎn)控程序,該遠(yuǎn)控支持保存套接字,當(dāng)有套接字連入時(shí),自動存儲到map容器,當(dāng)客戶下線時(shí)自動從map容器中移除,當(dāng)我們需要與特定客戶端通信時(shí),只需要指定客戶端ID號即可。
回到頂部
AsyncTcpServer
服務(wù)端首先定義CEventHandler
類并繼承自CAsyncTcpServer::IEventHandler
接口,該類內(nèi)需要我們實(shí)現(xiàn)三個(gè)方法,方法ClientConnected
用于在客戶端連接時(shí)觸發(fā),方法ClientDisconnect
則是在登錄客戶端離開時(shí)觸發(fā),而當(dāng)客戶端有數(shù)據(jù)發(fā)送過來時(shí)則ReceiveData
方法則會被觸發(fā)。
方法ClientConnected
當(dāng)被觸發(fā)時(shí)自動將clientId
客戶端Socket套接字放入到tcp_client_id
全局容器內(nèi)存儲起來,而當(dāng)ClientDisconnect
客戶端退出時(shí),則直接遍歷這個(gè)迭代容器,找到序列號并通過tcp_client_id.erase
將其剔除;
// 客戶端連接時(shí)觸發(fā)virtual void ClientConnected(int clientId){
// 將登錄客戶端加入到容器中
tcp_client_id.push_back(clientId);}
?// 客戶端退出時(shí)觸發(fā)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
一旦收到數(shù)據(jù),則直接將其打印輸出到屏幕,即可實(shí)現(xiàn)客戶端參數(shù)接收的目的;
// 客戶端獲取數(shù)據(jù)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] # ";}
相對于接收數(shù)據(jù)而言,發(fā)送數(shù)據(jù)則是通過同步的方式進(jìn)行,當(dāng)我們需要發(fā)送數(shù)據(jù)時(shí),只需要將數(shù)據(jù)字符串放入到一個(gè)BYTE*
字節(jié)數(shù)組中,并在調(diào)用tcpServer.Send
時(shí)將所需參數(shù),套接字ID,緩沖區(qū)Buf數(shù)據(jù),以及長度傳遞即可實(shí)現(xiàn)將數(shù)據(jù)發(fā)送給指定的客戶端;
// 同步發(fā)送數(shù)據(jù)到指定的線程中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);}
回到頂部
AsyncTcpClient
客戶端首先我們封裝實(shí)現(xiàn)AsyncConnect
類,該類內(nèi)主要實(shí)現(xiàn)兩個(gè)功能,其中aysnc_connect
方法用于實(shí)現(xiàn)異步連接到服務(wù)端,而port_is_open
方法則用于驗(yàn)證服務(wù)器特定端口是否開放,在調(diào)用boost::bind
綁定套接字時(shí)傳入&AsyncConnect::timer_handle
設(shè)置一個(gè)超時(shí)等待時(shí)間。
進(jìn)入到main
主函數(shù)中,通過while
循環(huán)讓程序可以一直運(yùn)行下去,并通過hander.aysnc_connect(ep, 5000)
?每隔5秒驗(yàn)證是否連接成功,如果連接了則進(jìn)入內(nèi)循環(huán),通過hander.port_is_open("127.0.0.1", 10000, 5000)
驗(yàn)證端口是否開放,這主要是為了保證服務(wù)端斷開后客戶端依然能夠跳轉(zhuǎn)到外部循環(huán)繼續(xù)等待服務(wù)端上線。
回到頂部
案例演示
首先運(yùn)行服務(wù)端程序,接著運(yùn)行多個(gè)客戶端,即可實(shí)現(xiàn)自動上線;

當(dāng)用戶需要通信時(shí),只需要指定id序號到指定的Socket套接字編號即可;

回到頂部
源代碼
服務(wù)端代碼
// 署名權(quán)// 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;// 存儲當(dāng)前客戶端的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:
// 客戶端連接時(shí)觸發(fā)
virtual void ClientConnected(int clientId)
{
// 將登錄客戶端加入到容器中
tcp_client_id.push_back(clientId);
}
// 客戶端退出時(shí)觸發(fā)
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);
}
// 客戶端獲取數(shù)據(jù)
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] # ";
}};// 同步發(fā)送數(shù)據(jù)到指定的線程中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數(shù)據(jù) \n");
printf("\t GetMemory ? ? ? ? 獲取內(nèi)存數(shù)據(jù) \n");
printf("\t Exit ? ? ? ? ? ? ?退出客戶端 \n\n");
}
else
{
// 定義分詞器: 定義分割符號為[逗號,空格]
boost::char_separator<char> sep(", --");
typedef boost::tokenizer<boost::char_separator<char>> CustonTokenizer;
CustonTokenizer tok(command, sep);
// 將分詞結(jié)果放入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;}
客戶端代碼
// 署名權(quán)// 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;
// 異步連接,當(dāng)連接成功后將觸發(fā) connect_handle 函數(shù)
socket_.async_connect(ep, boost::bind(&AsyncConnect::connect_handle, this, _1, boost::ref(connect_success)));
// 設(shè)置一個(gè)定時(shí)器 ?million_seconds
timer_.expires_from_now(boost::posix_time::milliseconds(million_seconds));
bool timeout = false;
// 異步等待 如果超時(shí)則執(zhí)行 timer_handle
timer_.async_wait(boost::bind(&AsyncConnect::timer_handle, this, _1, boost::ref(timeout)));
do
{
// 等待異步操作完成
io_service_.run_one();
// 判斷如果timeout沒超時(shí),或者是連接建立了,則不再等待
} while (!timeout && !connect_success);
timer_.cancel();
return connect_success;
}
// 驗(yàn)證服務(wù)器端口是否開放
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;
}
}
// 定時(shí)器超時(shí)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);
// 循環(huán)驗(yàn)證是否在線
go_: ?while (1)
{
// 驗(yàn)證是否連接成功,并定義超時(shí)時(shí)間為5秒
if (hander.aysnc_connect(ep, 5000))
{
io.run();
std::cout << "已連接到服務(wù)端." << std::endl;
// 循環(huán)接收命令
while (1)
{
// 驗(yàn)證地址端口是否開放,默認(rèn)等待5秒
bool is_open = hander.port_is_open("127.0.0.1", 10000, 5000);
// 客戶端接收數(shù)據(jù)包
boost::array<char, 4096> buffer = { 0 };
// 如果在線則繼續(xù)執(zhí)行
if (is_open == true)
{
socket.read_some(boost::asio::buffer(buffer), error);
// 判斷收到的命令是否為GetCPU
if (strncmp(buffer.data(), "GetCPU", strlen("GetCPU")) == 0)
{
std::cout << "獲取CPU參數(shù)并返回給服務(wù)端." << std::endl;
socket.write_some(boost::asio::buffer("CPU: 15 %"));
}
// 判斷收到的命令是否為GetMEM
if (strncmp(buffer.data(), "GetMEM", strlen("GetMEM")) == 0)
{
std::cout << "獲取MEM參數(shù)并返回給服務(wù)端." << 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
{
// 如果連接失敗,則跳轉(zhuǎn)到等待環(huán)節(jié)
goto go_;
}
}
}
else
{
std::cout << "連接失敗,正在重新連接." << std::endl;
}
}
}
catch (...)
{
return false;
}
std::system("pause");
return 0;}
回到頂部
項(xiàng)目地址
https://github.com/lyshark/BoostAsyncSocket