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);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_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:80server.add_portmap(5000, ep1);// 訪問本機6000端口,將數據包轉發到 8.141.58.64:80server.add_portmap(6000, ep2);io_service.run();}catch (...) {}return 0;
}