此時,Client A與Client B都可以與Server S通信了。如果Client A此時想直接傳送訊息給Client B,那麼他可以從Server S那兒獲得B的公共外網位址187.34.1.56:40000,是不是Client A向這個位址傳送訊息Client B就能收到了呢?
答案是不行,因為如果這樣傳送訊息,NAT B會將這個訊息丟棄(因為這樣的訊息是不請自來的,為了安全,大多數NAT都會執行丟棄動作)。現在我們需要的是在NAT B上打一個方向為202.187.45.3(即Client A的外網位址)的洞,那麼Client A傳送到187.34.1.56:40000的訊息,Client B就能收到了。這個打洞指令由誰來發呢,哈哈,當然是Server S。
總結一下這個程序:如果Client A想向Client B傳送訊息,那麼Client A傳送指令給Server S,請求Server S指令Client B向Client A方向打洞。
哈哈,是不是很繞口,不過沒關係,想一想就很清楚了,何況還有來源碼呢(侯老師說過:在來源碼面前沒有秘密 8)),然後Client A就可以通過Client B的外網位址與Client B通信了。
注意:以上程序只適合於Cone NAT的情況,如果是Symmetric NAT,那麼當Client B向Client A打洞的連接阜已經重新分配了,Client B將無法知道這個連接阜(如果Symmetric NAT的連接阜是順序分配的,那麼我們或許可以猜測這個連接阜號,可是由於可能導致失敗的因素太多,我們不推薦這種猜測連接阜的方法)。
下面是一個模擬P2P聊天的程序的來源碼,程序很簡單,P2PServer執行在一個擁有公共外網IP的電腦上,P2PClient執行在兩個不同的NAT後(注意,如果兩個客戶端執行在一個NAT後,本程序很可能不能執行正常,這取決於你的NAT是否支持loopback translation,詳見http://midcom-p2p.sourceforge.net/draft-ford-midcom-p2p-01.txt,當然,此問題可以通過雙方先嘗試連接對方的局內網IP來解決,但是這個程式碼只是為了驗證原理,並沒有處理這些問題),後登入的電腦可以獲得先登入電腦的用戶名,後登入的電腦通過send username message的格式來傳送消息。
如果傳送成功,說明你已取得了直接與對方連接的成功。
程序現在支持三個指令:send , getu , exit
send格式:send username message
功能:傳送訊息給username
getu格式:getu
功能:獲得當前伺服器用戶列表
exit格式:exit
功能:註銷與伺服器的連接(伺服器不會自動監測客戶是否吊線)
程式碼很短,相信很容易懂,如果有什麼問題,可以給我發郵件zhouhuis22@sina.com 或者在CSDN上傳送短消息。同時,歡迎轉發此文,但希望保留作者版權8-)。
最後感謝CSDN網友 PiggyXP 和 Seilfer的測試說明
|