C++ 基于Boost.Asio实现端口映射器
Boost.Asio 是一个功能强大的 C++ 库,用于异步编程和网络编程,它提供了跨平台的异步 I/O 操作。在这篇文章中,我们将深入分析一个使用 Boost.Asio 实现的简单端口映射服务器,该服务器能够将本地端口的数据包转发到指定的远程服务器上。
端口映射通常用于将一个网络端口上的流量转发到另一个网络端口。这对于实现网络中间人攻击、内网穿透等场景非常有用。我们将使用 Boost.Asio 提供的异步操作来实现这个简单而功能强大的端口映射服务器。
#include <list>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/enable_shared_from_this.hpp>
using boost::asio::ip::tcp;
首先,让我们简要概述代码的主要类:
socket_client类:继承了boost::enable_shared_from_this和tcp::socket,用于表示客户端的套接字。socket_pipe类:表示端口映射的管道,负责在两个客户端之间传递数据。async_listener类:用于异步监听指定端口的连接请求,通过回调函数处理连接。port_map_server类:管理多个监听器,支持添加端口映射规则,并处理连接请求。
1.1 socket_client
socket_client 类继承自 boost::enable_shared_from_this 和 tcp::socket。通过 create 静态方法创建一个 socket_client 实例,提供了共享指针的方式管理对象的生命周期。
如下代码是一个使用 Boost.Asio 库创建的异步 TCP 客户端类。
class socket_client
: public boost::enable_shared_from_this<socket_client>
, public tcp::socket
{
public:
typedef boost::shared_ptr<socket_client> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new socket_client(io_service));
}
public:
socket_client(boost::asio::io_service& io_service)
:tcp::socket(io_service)
{
}
};
以下是对该类的概括:
- 类名:
socket_client - 继承关系:
- 继承自
boost::enable_shared_from_this<socket_client>,这允许在异步操作中安全地使用shared_from_this,以避免悬挂指针的问题。 - 继承自
tcp::socket,表示该类是一个 TCP 套接字。
- 继承自
- 公共成员类型:
pointer:boost::shared_ptr<socket_client>类型的别名,用于管理该类的实例。
- 公共静态函数:
create:工厂方法,用于创建socket_client的实例。通过此方法获取了一个智能指针指向新创建的实例。
- 公共构造函数:
socket_client(boost::asio::io_service& io_service):构造函数,接受一个boost::asio::io_service引用,用于初始化基类tcp::socket。
该类的目的是提供一个异步 TCP 客户端的基本结构,使其能够与 Boost.Asio 库中的异步 I/O 操作协同工作。实际使用时,可以根据具体需求扩展该类,添加成员函数和操作,以实现特定的异步操作逻辑。
1.2 socket_pipe
socket_pipe 类用于处理两个客户端之间的数据传递。通过异步操作实现了从一个客户端读取数据,并将数据写入另一个客户端。出现错误时,会关闭两个客户端的连接。这里使用了递归的方式,实现了数据的循环传递。
如下代码是一个使用是一个 socket_pipe 类的定义,用于在两个 socket_client 实例之间建立数据传输管道。
class socket_pipe
{
public:
socket_pipe(socket_client::pointer read, socket_client::pointer write)
:read_socket_(*read), write_socket_(*write)
{
read_ = read;
write_ = write;
begin_read();
}
private:
void begin_read()
{
read_socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&socket_pipe::end_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void end_read(const boost::system::error_code& error, size_t bytes_transferred)
{
if (error)
handle_error(error);
else
begin_write(bytes_transferred);
}
void begin_write(int bytes_transferred)
{
boost::asio::async_write(write_socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&socket_pipe::end_write, this,
boost::asio::placeholders::error));
}
void end_write(const boost::system::error_code& error)
{
if (error)
handle_error(error);
else
begin_read();
}
void handle_error(const boost::system::error_code& error)
{
read_socket_.close();
write_socket_.close();
delete this;
}
private:
socket_client& read_socket_;
socket_client& write_socket_;
socket_client::pointer read_;
socket_client::pointer write_;
enum { max_length = 1024 };
char data_[max_length];
};
以下是对该类的概括:
- 类名:
socket_pipe - 公共构造函数:
socket_pipe(socket_client::pointer read, socket_client::pointer write):构造函数,接受两个socket_client::pointer实例,一个用于读取数据 (read_socket_),另一个用于写入数据 (write_socket_)。在构造函数中,调用了begin_read函数,启动了异步读取操作。
- 私有成员函数:
begin_read():启动异步读取操作,使用read_socket_.async_read_some异步读取数据。end_read(const boost::system::error_code& error, size_t bytes_transferred):读取操作完成时的回调函数,处理可能的错误,如果没有错误则调用begin_write启动异步写入操作。begin_write(int bytes_transferred):启动异步写入操作,使用boost::asio::async_write异步写入数据。end_write(const boost::system::error_code& error):写入操作完成时的回调函数,处理可能的错误,如果没有错误则调用begin_read启动下一轮异步读取操作。handle_error(const boost::system::error_code& error):处理错误的函数,关闭读取和写入套接字,并释放当前socket_pipe实例。
- 私有成员变量:
socket_client& read_socket_:引用传递的读取套接字。socket_client& write_socket_:引用传递的写入套接字。socket_client::pointer read_:指向读取套接字的智能指针。socket_client::pointer write_:指向写入套接字的智能指针。enum { max_length = 1024 };:定义了最大数据长度。char data_[max_length];:存储数据的缓冲区。
该类的主要目的是在两个 socket_client 之间实现数据的双向传输,通过异步操作实现了循环的读取和写入过程。在错误处理中,如果出现错误,会关闭套接字并释放当前的 socket_pipe 实例。
1.3 async_listener
async_listener 类负责异步监听指定端口,并通过回调函数处理连接。在连接建立时,会调用用户提供的回调函数进行处理。通过 begin_accept 方法开始异步监听。
如下代码是一个使用 async_listener 类的定义,用于异步监听指定端口的连接。
class async_listener
{
public:
typedef boost::function<void(socket_client::pointer client)> accept_handler;
typedef boost::shared_ptr<async_listener> pointer;
public:
async_listener(short port, boost::asio::io_service& io_service)
:io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
begin_accept();
}
void begin_accept()
{
socket_client::pointer client = socket_client::create(io_service_);
acceptor_.async_accept(*client,
boost::bind(&async_listener::end_accept, this, client,
boost::asio::placeholders::error));
}
void end_accept(socket_client::pointer client, const boost::system::error_code& error)
{
if (error)
handle_error(error);
begin_accept();
if (!handle_accept.empty())
handle_accept(client);
}
void handle_error(const boost::system::error_code& error)
{
}
public:
accept_handler handle_accept;
private:
tcp::acceptor acceptor_;
boost::asio::io_service& io_service_;
};
以下是对该类的概括:
- 类名:
async_listener - 公共成员类型:
accept_handler:boost::function<void(socket_client::pointer client)>类型的别名,用于定义连接建立时的回调函数。pointer:boost::shared_ptr<async_listener>类型的别名,用于管理该类的实例。
- 公共构造函数:
async_listener(short port, boost::asio::io_service& io_service):构造函数,接受一个短整型端口号和一个boost::asio::io_service引用。在构造函数中,创建了一个 TCP 接受器 (acceptor_) 并调用begin_accept启动异步接受操作。
- 公共函数:
begin_accept():启动异步接受操作,创建一个新的socket_client实例,并调用acceptor_.async_accept异步等待连接的建立。end_accept(socket_client::pointer client, const boost::system::error_code& error):异步接受操作完成时的回调函数,处理可能的错误,如果没有错误则调用begin_accept启动下一轮异步接受操作。如果定义了handle_accept回调函数,则调用它并传递新连接的socket_client实例。
- 私有成员函数:
handle_error(const boost::system::error_code& error):处理错误的函数,目前仅为空实现。
- 公共成员变量:
accept_handler handle_accept:用于存储用户定义的连接建立时的回调函数。
- 私有成员变量:
tcp::acceptor acceptor_:TCP 接受器,用于监听连接。boost::asio::io_service& io_service_:引用传递的io_service,用于执行异步操作。
该类的主要目的是实现异步监听,一旦有连接建立,就通过回调函数通知用户,并通过 handle_error 处理可能的错误。在连接建立后,会继续监听新的连接。
1.4 port_map_server
port_map_server 类管理多个监听器,支持动态添加端口映射规则。在连接建立时,会调用 handle_accept 处理连接请求。通过 begin_connect 方法开始异步连接远程服务器。
如下代码是一个 port_map_server 类的定义,它通过异步监听多个本地端口,并将连接映射到远程服务器的不同端口。
class port_map_server
{
public:
port_map_server(boost::asio::io_service& io_service) :io_service_(io_service)
{
}
void add_portmap(short port, tcp::endpoint& remote_endpoint)
{
async_listener::pointer listener(new async_listener(port, io_service_));
listeners.push_back(listener);
listener->handle_accept = boost::bind(&port_map_server::handle_accept
, this, remote_endpoint, _1);
}
void handle_accept(tcp::endpoint remote_endpoint, socket_client::pointer client)
{
begin_connect(remote_endpoint, client);
}
void begin_connect(tcp::endpoint& remote_endpoint, socket_client::pointer socket_local)
{
socket_client::pointer socket_remote = socket_client::create(io_service_);
socket_remote->async_connect(remote_endpoint,
boost::bind(&port_map_server::end_connect, this,
boost::asio::placeholders::error, socket_local, socket_remote));
}
void end_connect(const boost::system::error_code& error, socket_client::pointer socket_local, socket_client::pointer socket_remote)
{
if (error)
{
handle_error(error);
}
else
{
new socket_pipe(socket_local, socket_remote);
new socket_pipe(socket_remote, socket_local);
}
}
void handle_error(const boost::system::error_code& error)
{
}
private:
boost::asio::io_service& io_service_;
std::list<async_listener::pointer> listeners;
};
以下是对该类的概括:
- 类名:
port_map_server - 公共构造函数:
port_map_server(boost::asio::io_service& io_service):构造函数,接受一个boost::asio::io_service引用。
- 公共函数:
add_portmap(short port, tcp::endpoint& remote_endpoint):添加端口映射规则的函数。为指定端口创建一个新的async_listener实例,并将其添加到listeners列表中。同时,设置handle_accept回调函数,以便在新连接建立时调用handle_accept函数。
- 私有成员函数:
handle_accept(tcp::endpoint remote_endpoint, socket_client::pointer client):处理新连接建立时的回调函数。在此函数中,调用begin_connect启动异步连接到远程服务器的操作。begin_connect(tcp::endpoint& remote_endpoint, socket_client::pointer socket_local):启动异步连接到远程服务器的操作,创建一个新的远程套接字。end_connect(const boost::system::error_code& error, socket_client::pointer socket_local, socket_client::pointer socket_remote):处理异步连接操作完成时的回调函数。如果连接成功,创建两个socket_pipe实例,分别用于将数据从本地传输到远程和从远程传输回本地。handle_error(const boost::system::error_code& error):处理错误的函数,目前仅为空实现。
- 私有成员变量:
boost::asio::io_service& io_service_:引用传递的io_service,用于执行异步操作。std::list<async_listener::pointer> listeners:存储多个async_listener实例的列表。
该类的主要目的是通过创建多个 async_listener 实例,监听多个本地端口,并在新连接建立时将其映射到远程服务器的不同端口。在连接建立后,会启动异步连接到远程服务器的操作,并创建数据传输的管道。
1.5 port_map_server
这是程序的 main 函数,负责创建一个 boost::asio::io_service 实例,设置两个远程服务器的端点,然后创建一个 port_map_server 实例并添加两个端口映射规则。最后,通过调用 io_service.run() 开始事件循环。
以下是对 main 函数的概括:
- 函数功能:
- 创建一个
boost::asio::io_service实例,用于管理异步操作的事件循环。 - 定义两个远程服务器的端点 (
ep1和ep2),分别是192.168.1.100:80和192.168.1.200:80。 - 创建一个
port_map_server实例,该实例使用上述io_service。 - 通过
add_portmap函数向port_map_server添加两个端口映射规则,将本地端口5000映射到远程服务器192.168.1.100:80,将本地端口6000映射到远程服务器192.168.1.200:80。 - 调用
io_service.run()开始事件循环,等待异步操作的完成。
- 创建一个
- 异常处理:
- 使用了
try和catch块,捕获任何可能抛出的异常,并在catch块中忽略异常。
- 使用了
- 返回值:
- 返回整数
0表示程序正常结束。
- 返回整数
这个 main 函数的作用是启动异步事件循环,使得 port_map_server 开始监听指定端口,接受连接,并将连接映射到远程服务器上。
int main(int argc, char* argv[])
{
try
{
boost::asio::io_service io_service;
tcp::endpoint ep1(boost::asio::ip::address_v4::from_string("192.168.1.100"), 80);
tcp::endpoint ep2(boost::asio::ip::address_v4::from_string("192.168.1.200"), 80);
port_map_server server(io_service);
// 访问本机5000端口,将数据包转发到 8.141.58.64:80
server.add_portmap(5000, ep1);
// 访问本机6000端口,将数据包转发到 8.141.58.64:80
server.add_portmap(6000, ep2);
io_service.run();
}
catch (...) {}
return 0;
}
C++ 基于Boost.Asio实现端口映射器的更多相关文章
- Mybatis基于接口注解配置SQL映射器(一)
上文已经讲解了基于XML配置的SQL映射器,在XML配置的基础上MyBatis提供了简单的Java注解,使得我们可以不配置XML格式的Mapper文件,也能方便的编写简单的数据库操作代码. Mybat ...
- 改进基于Boost.Asio的聊天服务
Boost.Asio是个非常易用的C++异步网络库,官方文档中一个示例是聊天服务,分为chat_message.chat_client.chat_server三个部分.chat_server的启动代码 ...
- Mybatis基于接口注解配置SQL映射器(二)
Mybatis之增强型注解 MyBatis提供了简单的Java注解,使得我们可以不配置XML格式的Mapper文件,也能方便的编写简单的数据库操作代码.但是注解对动态SQL的支持一直差强人意,即使My ...
- BOOST.Asio——Overview
=================================版权声明================================= 版权声明:原创文章 谢绝转载 啥说的,鄙视那些无视版权随 ...
- Boost Asio介绍--之一
原文:http://www.tuicool.com/articles/YbeYR3 Boost Asio介绍--之一 时间 2014-03-26 17:57:39 CSDN博客 原文 http:/ ...
- Boost Asio(一)初探
一.简介 Boost Asio ( asynchronous input and output)关注数据的异步输入输出.Boost Asio 库提供了平台无关性的异步数据处理能力(当然它也支持同步数据 ...
- 使用boost.asio实现网络通讯
#include <boost/asio.hpp> #define USING_SSL //是否加密 #ifdef USING_SSL #include <boost/asio/ss ...
- TTradmin v1.1 - 免端口映射穿透任何内网、基于radmin核心的即时远程协助
TTradmin 是一款免端口映射可直接穿透任何内网,基于radmin核心的即时远程协助软件.在使用的时候只需要保证“协助端”和“被协助端”使用同一个验证码即可实现安全便捷的远程控制,不需要进 ...
- Mybatis基于XML配置SQL映射器(二)
Mybatis之XML注解 之前已经讲到通过 mybatis-generator 生成mapper映射接口和相关的映射配置文件: 下面我们将详细的讲解具体内容 首先我们新建映射接口文档 sysUse ...
- boost::asio译文
Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENS ...
随机推荐
- Educational Codeforces Round 110 (Rated for Div. 2) (AB签到,C题双指针,D题DP好题)
补题链接:Here 1535A. Fair Playoff 四名选手参加了季后赛.比赛按以下方案进行:第一名选手与第二名选手比赛,第三名选手与第四名选手比赛,然后两人中的获胜者进入决赛. 众所周知,在 ...
- 牛客 | 一起来做题~欢乐赛2 (AK 题解)
补题链接:Here A.新比赛,在眼前. 对于每次猜数和裁判的判断,可以确定一个区间内所有的数都有可能,比如对于样例中(8 +)来说,[ -INT_MIN, 7] 中所有的数都有可能,那么对于每次猜数 ...
- Codeforces Round #706 Editorial
1496A. Split it! 类回文判断,只要 k = 0 或者 \(s[1,k] 和 s[n - k + 1,n]\)是回文即可 特判情况 n < 2 * k + 1 为 NO int m ...
- springboot线程池的使用方式1
线程池的创建方法 总共有 7 种,但总体来说可分为 2 类: 一类是通过 ThreadPoolExecutor 创建的线程池: 另一个类是通过 Executors 创建的线程池. 1. Executo ...
- redis命令Incr做计数器 + 切点切面
Redis Incr 命令将 key 中储存的数字值增一. 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作. package com.example.apid ...
- 三、springboot集成金仓数据库
系列导航 一.金仓数据库搭建(单机) 二.金仓数据库搭建(主从安装) 三.springboot集成金仓数据库 1.数据库中创建表 CREATE TABLE TEST_BLOCK_T ( BLOCK_I ...
- vue学习笔记 二、环境搭建+项目创建
系列导航 vue学习笔记 一.环境搭建 vue学习笔记 二.环境搭建+项目创建 vue学习笔记 三.文件和目录结构 vue学习笔记 四.定义组件(组件基本结构) vue学习笔记 五.创建子组件实例 v ...
- mixin混合
多个组件有相同的逻辑,抽离出来 mixin并不是完美的解决方案,会有一些问题 vue3提出composition api旨在解决这些问题
- 0xGame 2023【WEEK1】Crypto全解
What's CBC? 题目信息 from Crypto.Util.number import * from secret import flag,key def bytes_xor(a,b): a, ...
- [转帖]db file sequential read-数据文件顺序读取
https://www.cnblogs.com/xibuhaohao/p/9959593.html 等待事件: "db file sequential read" Referenc ...