參考資料:《Boost Asio C++網絡編程》第五章
學會網絡編程之後,理所當然會有這樣的想法:在單位電腦上部署自己寫的服務段軟件,在家裏電腦上部署客戶端,從而遠程辦公。但是實際操作下來可以發現,兩臺電腦上的ip地址,根本不能建立socket直連,這是爲什麼呢。
內外網連接規則
對於普通網絡用戶,電腦入網都分配的是內網IP。儘管IPv6已經出來多年,目前互聯網公網IP普遍還在IPv4階段,可利用的IP地址相當有限。寬帶商本身僅僅能拿到少量IPv4地址的使用權,不會給每個個人用戶配置單獨的公網IP,爲了讓每個用戶上網,把用戶都納入局域網中分配局域網IP;建立網關設備作爲門戶,收集每個用戶的socket請求然後變爲門戶IP的數據包,轉發到公網上去,建立長期連接;用戶連接單獨開啓一個端口用於標識數據轉發地址;但是不允許外網主動申請連接,如圖1
內網穿透
既然內網之間無法連接,內網的網絡用戶,需要其他方式才能進行互聯,實現聊天、傳文件、遠程控制等功能。上述可知,內網能主動連接公網服務器建立長期連接,但不能反向,那麼不同內網用戶可以預先向同一公網服務器建立長期連接,然後公網服務器進行轉發,間接實現內網電腦之間的互聯,如圖2:
資源利用策略
一般情況下,一臺公網服務器就會佔用一個公網IP,如果服務量巨大,那這臺服務器會崩潰,需要將壓力分散到多個服務器中,一種可用策略如圖3:
ASIO代理服務
代理服務器很簡單,需要配備兩個端口,一個接收客戶端sock連接,一個接收服務端socket連接。端口可以設置reuse屬性,進行多進程監聽,但是最好一個端口僅對應一個服務。
class proxy : public boost::enable_shared_from_this<proxy> {
proxy(ip::tcp::endpoint ep_client, ip::tcp::endpoint ep_serv
er) : ... {}
void on_start() {
do_read(client_, buff_client_);
do_read(server_, buff_server_);
}
這裏是異步調用,直接啓動client和server的監聽即可,後續回調函數需要將sock作爲參數,好識別究竟是客戶端還是服務端發出了響應:
void do_read(ip::tcp::socket & sock, char* buff) {
async_read(sock, buffer(buff, max_msg), MEM_FN3(read_com
plete,ref(sock),_1,_2), MEM_FN3(on_read,ref(sock),_1,_2));
}
注意這裏使用std/boost::ref(),顯示調用std/boost::bind執行引用參數傳遞,因爲bind默認參數列表都是值傳遞。
void on_read(ip::tcp::socket & sock, const error_code& err,
size_t bytes) {
char * buff = &sock == &client_ ? buff_client_ : buff_se
rver_;
do_write(&sock == &client_ ? server_ : client_, buff, by
tes);
}
實際操作很簡單,如果是client的sock讀取進來,那麼就向server的sock寫入同樣的數據;反之亦然。代理的一大作用在這裏,就是可以把client/server傳入的數據進行修改,再用不同的sock傳遞出去。