原文地址: http://f543711700.iteye.com/blog/978887
為什么網(wǎng)上講到的P2P打洞基本上都是基于UDP協(xié)議的打洞外遇?難道TCP不可能打洞埃叭?還是TCP打洞難于實(shí)現(xiàn)窄驹?
假設(shè)現(xiàn)在有內(nèi)網(wǎng)客戶端A和內(nèi)網(wǎng)客戶端B沙热,有公網(wǎng)服務(wù)端S初烘。
如果A和B想要進(jìn)行UDP通信,則必須穿透雙方的NAT路由比伏。假設(shè)為NAT-A和NAT-B胜卤。
A發(fā)送數(shù)據(jù)包到公網(wǎng)S,B發(fā)送數(shù)據(jù)包到公網(wǎng)S,則S分別得到了A和B的公網(wǎng)IP,
S也和A B 分別建立了會(huì)話赁项,由S發(fā)到NAT-A的數(shù)據(jù)包會(huì)被NAT-A直接轉(zhuǎn)發(fā)給A葛躏,
由S發(fā)到NAT-B的數(shù)據(jù)包會(huì)被NAT-B直接轉(zhuǎn)發(fā)給B澈段,除了S發(fā)出的數(shù)據(jù)包之外的則會(huì)被丟棄。
所以:現(xiàn)在A B 都能分別和S進(jìn)行全雙工通訊了舰攒,但是A B之間還不能直接通訊败富。
解決辦法是:
A向B的公網(wǎng)IP發(fā)送一個(gè)數(shù)據(jù)包,則NAT-A能接收來自NAT-B的數(shù)據(jù)包
并轉(zhuǎn)發(fā)給A了(即B現(xiàn)在能訪問A了)摩窃;再由S命令B向A的公網(wǎng)IP發(fā)送一個(gè)數(shù)據(jù)包兽叮,則
NAT-B能接收來自NAT-A的數(shù)據(jù)包并轉(zhuǎn)發(fā)給B了(即A現(xiàn)在能訪問B了)。
以上就是“打洞”的原理猾愿。
但是TCP和UDP在打洞上卻有點(diǎn)不同鹦聪。這是因?yàn)椴死鹲ocket(標(biāo)準(zhǔn)socket規(guī)范)的API造成的:
UDP的socket允許多個(gè)socket綁定到同一個(gè)本地端口,而TCP的socket則不允許蒂秘。
這是這樣一個(gè)意思:A B要連接到S泽本,肯定首先A B雙方都會(huì)在本地創(chuàng)建一個(gè)socket,
去連接S上的socket姻僧。創(chuàng)建一個(gè)socket必然會(huì)綁定一個(gè)本地端口(就算應(yīng)用程序里面沒寫
端口规丽,實(shí)際上也是綁定了的,至少java確實(shí)如此)撇贺,假設(shè)為8888赌莺,這樣A和B才分別建立了到
S的通信信道。接下來就需要打洞了松嘶,打洞則需要A和B分別發(fā)送數(shù)據(jù)包到對(duì)方的公網(wǎng)IP艘狭。但是
問題就在這里:因?yàn)镹AT設(shè)備是根據(jù)端口號(hào)來確定session,如果是UDP的socket喘蟆,A B可以
分別再創(chuàng)建socket缓升,然后將socket綁定到8888,這樣打洞就成功了蕴轨。但是如果是TCP的
socket港谊,則不能再創(chuàng)建socket并綁定到8888了,這樣打洞就無法成功橙弱。``