基于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.pswp.cn/news/166790.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/166790.shtml
英文地址,請注明出處:http://en.pswp.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…