本文转自:http://www.csharpwin.com/csharpspace/2244.shtml
在我做的项目中,经常遇到一种如下形式的网络结构。
出于安全的考虑,server处于防火墙之后,client无法直接访问,只能通过telnet登陆到proxy server上访问。在这种方式下,就无法利用cient上的各种强大的桌面工具(如数据库客户端等),只能通过telnet的命令行形式交互,确实有些不便。
我们可以通过端口映射解决这个问题,实现client到server的"直接访问"。当client想访问server时,只需要与proxy的某个端口建立连接,proxy监听到这个连接后,建立一个与server的连接(client的目标),同时提供这两个连接的消息传输管道。这样,所有client发到proxy的消息都发送到了server上,server的消息也发送到了proxy上,从而实现了client到server的访问。
由于连接的数目可能较多,并且proxy程序起着一个消息中转的作用,因此程序本身需要较高的socket通信效率,所有的操作都不能阻塞,否则严重影响其它的进程通信,因此程序中的socket的连接,通信方式都必须采用异步操作。(多线程的方式如果连接的进程较多时则开销太大)。
这种方式十分简单有效,并且不需要对客户端和服务器端做任何修改。不足的地方有如下几处:
-
proxy需要中转所有的消息,负荷较大。(不过处理目前的几十个客户端应用是绰绰有余,并且目前的通信瓶颈一般在internet上)
-
需要在proxy上建立端口监听,并且所监听的端口需要能被client直接访问,这种情况的网络很多时候得不到满足。(大多时候proxy只开放了几个有限的端口,并无多余的端口让端口映射程序绑定)
本来我用C#写了一个,程序非常简单,这里就不拿出来了。
后来由于要把这个程序放到Unix服务器上长期运行,我就用C++重写了一下,最初我是用socket api写的,可程序的可读性总是不尽人意,后来就改用了asio库(asio 0.3.8 rc3,与早期的asio库不兼容),通过boost的asio,function,smart_ptr这几个库的运用,一个C++版的端口映射程序便诞生了,精简、高效、安全、跨平台,原来c++下的异步socket也可以如此优雅。^_^
代码如下:
#include #include #include #include #include using boost::asio::ip::tcp; using namespace std; class socket_client : public boost::enable_shared_from_this ,public tcp::socket { public: typedef boost::shared_ptr 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) { } }; 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]; }; class async_listener { public: typedef boost::function accept_handler; typedef boost::shared_ptr 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_; }; 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_; list listeners; }; int main() { try { boost::asio::io_service io_service; tcp::endpoint ep(boost::asio::ip::address_v4::from_string("192.168.1.193"),23); tcp::endpoint ep2(boost::asio::ip::address_v4::from_string("192.168.1.175"),23); port_map_server server(io_service); server.add_portmap(3000,ep); server.add_portmap(4000,ep2); io_service.run(); } catch (std::exception& e) { std::cerr << e.what() << std::endl; } return 0; }