Boost 利用ASIO框架实现一个跨平台的反向远控程序,该远控反对保留套接字,当有套接字连入时,主动存储到map容器,当客户下线时主动从map容器中移除,当咱们须要与特定客户端通信时,只须要指定客户端ID号即可。
AsyncTcpServer
服务端首先定义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);}
AsyncTcpClient
客户端首先咱们封装实现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;}
我的项目地址
https://github.com/lyshark/BoostAsyncSocket