基于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_thistcp::socket,用于表示客户端的套接字。
  • socket_pipe 类:表示端口映射的管道,负责在两个客户端之间传递数据。
  • async_listener 类:用于异步监听指定端口的连接请求,通过回调函数处理连接。
  • port_map_server 类:管理多个监听器,支持添加端口映射规则,并处理连接请求。

1.1 socket_client

socket_client 类继承自 boost::enable_shared_from_thistcp::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 套接字。
  • 公共成员类型
    • pointerboost::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);elsebegin_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);elsebegin_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_handlerboost::function<void(socket_client::pointer client)> 类型的别名,用于定义连接建立时的回调函数。
    • pointerboost::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 实例,用于管理异步操作的事件循环。
    • 定义两个远程服务器的端点 (ep1ep2),分别是 192.168.1.100:80192.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() 开始事件循环,等待异步操作的完成。
  • 异常处理
    • 使用了 trycatch 块,捕获任何可能抛出的异常,并在 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:80server.add_portmap(5000, ep1);// 访问本机6000端口,将数据包转发到 8.141.58.64:80server.add_portmap(6000, ep2);io_service.run();}catch (...) {}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/166790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从设计上理解JDK动态代理

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 照理说&#xff0c;动态…

上门预约小程序开发优势

想要放松身心&#xff0c;享受按摩的舒适感&#xff1f;那就需要一个专业的按摩师来上门服务。我们开发的预约按摩小程序app系统&#xff0c;汇聚各类上门按摩服务&#xff0c;包括推拿SPA、小儿推拿、中医等&#xff0c;为您提供高价值、高标准的养生健康体验。24小时随时提供…

GEE土地分类——使用随机森林方法和多源遥感数据进行面向对象的土地分类NAIP数据为例

简介: 数据: 国家农业图像计划 (NAIP) 在美国大陆的农业生长季节获取航空图像。 NAIP 项目每年根据可用资金和图像获取周期签订合同。从 2003 年开始,NAIP 以 5 年为一个周期。2008 年是过渡年,2009 年开始采用 3 年周期。 NAIP 图像以一米的地面采样距离 (GSD) 采集,水…

【前端】让列表像Excel单元格一样编辑

前言 领导说了一堆的话,最后总结一句就是客户很懒,客户的员工更加懒。 本着让别人节省时间的原则,提倡出了让列表和Excal的单元格一样,不仅看数据还可以随时更改数据。 查资料 根据 Jeecg-Vue3 源码介绍,从而知道是基于 Vben Admin 开源项目进行改造的。 因此在 Vben…

Sulfo-CY3 NHS荧光染料的制备和表征

Sulfo-CY3 NHS(源自星戈瑞的花菁染料)荧光染料的制备和表征是确保染料质量和性能的关键步骤。制备Sulfo-CY3 NHS荧光染料&#xff1a; 原材料准备&#xff1a;准备所需的原材料&#xff0c;包括CY3 NHS ester&#xff08;或等效的前体&#xff09;&#xff0c;用于制备Sulfo-C…

沉头孔和埋头孔的区别

埋头空和沉头孔的区别在于螺栓孔上部扩孔&#xff1a;沉头孔是直筒结构&#xff1b;埋头孔是四十五度结构&#xff0c;比沉头孔较为平顺。 螺栓孔上部扩孔能容纳螺栓头部&#xff0c;使螺头部不高于周围表面。埋头空和沉头孔只是两种不同的叫法。 沉头孔是 PCB 上的圆柱形凹槽…

RK3568 支持4x4矩阵键盘

在对应的设备树添加: keypad {compatible = "gpio-matrix-keypad";pinctrl-names = "default";pinctrl-0 = <&GPIO3_A1_pin&GPIO1_D3_pin&GPIO1_D4_pin&GPIO1_C7_pin&GPIO1_D2_pin&GPIO1_D1_pin&GPIO1_D0_pin&GPIO3_A…

将form表单中的省市区的3个el-select下拉框的样式调成统一的间隔距离和长度,vue3项目iot->供应商管理

省市区是用3个el-select组成的 在表单中用el-col&#xff0c;会导致3个下拉的距离不统一&#xff0c;市和区的前面也是不需要文字label的 如何解决:用vue3的:deep()进行样式穿透&#xff0c;由于el-form-item标签都是一样的&#xff0c;为了能准确的找到市的el-form-item&…

解决:前端js下载文件流出现“未知文件格式”错误

第一中情况&#xff1a; 出现的问题&#xff0c;前端已经设置了responseType: blob,下载下来还是格式不对。 最后经过排查&#xff0c;后端缺少charsetutf-8&#xff0c;所以前端可以设置编码&#xff1a; 第二中情况&#xff1a; 后端已经设置了charsetutf-8&#xff0c;前…

机器学习/sklearn 笔记:K-means,kmeans++,MiniBatchKMeans,二分Kmeans

1 K-means介绍 1.0 方法介绍 KMeans算法通过尝试将样本分成n个方差相等的组来聚类&#xff0c;该算法要求指定群集的数量。它适用于大量样本&#xff0c;并已在许多不同领域的广泛应用领域中使用。KMeans算法将一组样本分成不相交的簇&#xff0c;每个簇由簇中样本的平均值描…

JS实现数组去重

数组去重&#xff0c;一般都是在面试的时候才会碰到&#xff0c;一般是要求手写数组去重方法的代码。如果是被提问到&#xff0c;数组去重的方法有哪些&#xff1f;你能答出其中的 10 种&#xff0c;面试官很有可能对你刮目相看。 在真实的项目中碰到的数组去重&#xff0c;一般…

数据结构-树

参考&#xff1a;https://www.hello-algo.com/chapter_tree/binary_tree/#711 1. 介绍 树存储不同于数组和链表的地方在于既可以保证数据检索的速度&#xff0c;又可以保证数据插入删除修改的速度&#xff0c;二者兼顾。 二叉树是一种很重要的数据结构&#xff0c;是非线性的…

【学习篇】Linux中grep、sed、awk

Linux 文本处理三剑客 – awk, sed, grep grep过滤文本 https://zhuanlan.zhihu.com/p/561445240 grep 是 Linux/Unix 系统中的一个命令行工具&#xff0c;用于从文件中搜索文本或字符串。grep 代表全局正则表达式打印。当我们使用指定字符串运行 grep 命令时&#xff0c;如…

Mysql并发时常见的死锁及解决方法

使用数据库时&#xff0c;有时会出现死锁。对于实际应用来说&#xff0c;就是出现系统卡顿。 死锁是指两个或两个以上的事务在执行过程中&#xff0c;因争夺资源而造成的一种互相等待的现象。就是所谓的锁资源请求产生了回路现象&#xff0c;即死循环&#xff0c;此时称系统处于…

星河创新,开拓新纪!2023“星河产业应用创新奖”报名全面开启!

科技的浪潮汹涌而至&#xff0c;人工智能正悄无声息地渗透进我们生活的每一个角落&#xff0c;成为推动社会奔腾向前的强大引擎。 随着大模型时代到来&#xff0c;更多的创新者涌现出来&#xff0c;他们正积极探索AI与实体的深度融合&#xff0c;解决行业难题&#xff0c;开拓…

算法的奥秘:种类、特性及应用详解(算法导论笔记1)

算法&#xff0c;是计算机科学领域的灵魂&#xff0c;是解决问题的重要工具。在算法的世界里&#xff0c;有着各种各样的种类和特性。今天&#xff0c;我将带各位踏上一段探索算法种类的旅程&#xff0c;分享一些常见的算法种类&#xff0c;并给出相应的实践和案例分析。希望通…

c# 微信小程序支付,订单录入发货

微信改动&#xff0c;大家一起改&#xff0c;来吧 private string GetAccessToken(string openid){string AppID "";string AppSecret "";string url "https://api.weixin.qq.com/cgi-bin/token?grant_typeclient_credential&appid"AppI…

华纳云:Linux每天自动备份mysql数据库怎么实现

在 Linux 系统中&#xff0c;你可以使用 cron 任务来定期执行 MySQL 数据库备份。以下是一个简单的步骤&#xff0c;演示如何设置每天自动备份 MySQL 数据库&#xff1a; 创建备份脚本&#xff1a; 创建一个 Shell 脚本&#xff0c;其中包含备份 MySQL 数据库的命令。假设脚本名…

【目标检测】保姆级别教程从零开始实现基于Yolov8的一次性筷子计数

前言 一&#xff0c;环境配置 一&#xff0c;虚拟环境创建 二&#xff0c;安装资源包 前言 最近事情比较少&#xff0c;无意间刷到群聊里分享的基于百度飞浆平台的一次性筷子检测&#xff0c;感觉很有意思&#xff0c;恰巧自己最近在学习Yolov8&#xff0c;于是看看能不能复…

前端JS数据时间排序

一、sort()方法 var data [ { name:‘1’, time:‘2019-04-26 10:53:19’ }, { name:‘2’, time:‘2019-04-26 10:51:19’ },{ name:‘3’, time:‘2019-04-26 11:04:32’ },{ name:‘4’, time:‘2019-04-26 11:05:32’ } ] data.sort(function(a,b){ return a.time < b…