文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C++ BoostAsyncSocket实现异步反弹通信的案例详解

2023-03-19 17:37

关注

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

到此这篇关于C++ BoostAsyncSocket实现异步反弹通信的案例详解的文章就介绍到这了,更多相关C++ BoostAsyncSocket异步反弹通信内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯