QQ通信原理
下面有4個基本的問答:
問題一:為什么只要可以連上互聯(lián)網(wǎng)的計算機都可以用QQ相互建立通信,而不需要固定IP?
也就是這個QQ用戶端是怎樣找到另一個QQ用戶的,而用戶在每次使用時他可能用的是不同的計算機,有著不同的IP地址。
服務器端不會以qq用戶端的ip作為唯一標識,服務器端會以qq賬號作為唯一標識,所以這個賬號肯定是唯一的,一個賬號登陸時每次都可以有不同的ip地址,但賬號卻相同,當賬號a登陸服務器,服務器會記錄下賬號a的ip地址,去通知a的好友,告訴他們,a上線了和現(xiàn)在的ip地址,a的好友就可以跟他通信了
問題二:是不是QQ在通信時根本不適用IP,QQ客戶端先是訪問QQ服務器端,然后QQ服務器端再為要建立連接的QQ客戶端建立連接?
只要是網(wǎng)絡層的通信,都會涉及到ip/tcp協(xié)議,就肯定需要ip,qq客戶端登陸qq服務器,服務器只是記錄登陸狀態(tài),不會一直和qq保持通信,只會每隔一段時間發(fā)送心跳數(shù)據(jù)包,來確實qq客戶端是否還在網(wǎng)絡上。當qq客戶端a上線后,服務器會告訴a,目前a的在線好友的最新ip地址,當a需要與任意好友通信時,直接使用ip地址就ok了。qq客戶端a與qq客戶端b通信,可以兩種方式,第一就是qq服務器有轉(zhuǎn)發(fā)的服務器,第二是,a與b直接通信,不會告訴qq服務器
問題三:QQ客戶端可以訪問QQ服務器端,然后服務器端獲取QQ客戶端的IP建立通信,是不是這樣的過程。。。如果是,這個過程是怎么處理的呢?
是這樣的過程,qq客戶端請求一個連接給服務器,服務器接收后,知道qq端a上線,把qq端a的賬號跟目前的ip會記錄下來,放在在線列表里或者其他的地方,然后每隔幾分鐘或者幾秒鐘給qq端a發(fā)送心跳包,問他是否還在線,來確保qq端a的最新狀態(tài)。這里客戶端與服務器的通信方式是udp。而不會時時刻刻都在用tcp連接。
問題四:QQ客戶端雖然IP地址不固定,但是在建立與QQ服務器端的通信時,必須提供自己的IP被服務器獲取,然后才能建立他們之間的通信,進而在建立客戶端之間的通信。也就是,只要能上網(wǎng)就有IP,只不過客戶端的IP,是被QQ客戶端獲取了,然后才建立通信的。(這是自己的猜測,不知對否。。。)
恩,服務器不會以ip作為唯一標識,會以賬號作為唯一標識,但與賬號通信的時候會用到賬號目前所對應的ip,客戶端與客戶端通信也如此
QQ有兩種登陸模式
一種是比較不常用的:直接登陸服務器,所有信息由服務器轉(zhuǎn)發(fā),這種登陸模式有個特點就是你會發(fā)現(xiàn)你使用獲取IP版本的QQ無法獲取對方的IP~?(這個我不清楚有沒有,但是肯定可以)
另一種是普通的:首先連接登陸服務器,在給對發(fā)發(fā)消息的時候,首先嘗試與對方進行打洞連接,如果可以打通消息直接發(fā)送給對方,如果不能打通,則消息轉(zhuǎn)發(fā)服務器,由服務器轉(zhuǎn)發(fā).(傳文件會優(yōu)先P2P,不行再選擇中轉(zhuǎn),不知道聊天是不是優(yōu)先P2P的,還是聊天文字是中轉(zhuǎn)的?圖片呢?會員表情?這個的確要問tx了,技術(shù)上的都是可以實現(xiàn),選擇什么只能問tx了)
如果上面的東西輕松搞定,那么你可以繼續(xù)看了,如果不知道,那么下面就不用看了
?
先貼一點資料
一、登陸。
不管UDP還是TCP,最終登陸成功之后,QQ都會有一個TCP連接來保持在線狀態(tài)。這個TCP連接的遠程端口一般是80,采用UDP方式登陸的時候,端口是8000。因此,假如你所在的網(wǎng)絡開放了80端口(80端口是最常用端口。。就是通常訪問Web的端口,禁掉它的話,你的網(wǎng)絡對你來說價值已經(jīng)不大了),但沒有屏蔽騰訊的服務器IP,恭喜你,你是可以登陸成功QQ的。
二、聊天消息通信。
采用UDP協(xié)議,通過服務器中轉(zhuǎn)方式。因此,現(xiàn)在的IP偵探在你僅僅跟對方發(fā)送聊天消息的時候是無法獲取到IP的。大家都知道,UDP 協(xié)議是不可靠協(xié)議,它只管發(fā)送,不管對方是否收到的,但它的傳輸很高效。但是,作為聊天軟件,怎么可以采用這樣的不可靠方式來傳輸消息呢?于是,騰訊采用了上層協(xié)議來保證可靠傳輸:如果客戶端使用UDP協(xié)議發(fā)出消息后,服務器收到該包,需要使用UDP協(xié)議發(fā)回一個應答包。如此來保證消息可以無遺漏傳輸。之所以會發(fā)生在客戶端明明看到“消息發(fā)送失敗”但對方又收到了這個消息的情況,就是因為客戶端發(fā)出的消息服務器已經(jīng)收到并轉(zhuǎn)發(fā)成功,但客戶端由于網(wǎng)絡原因沒有收到服務器的應答包引起的。
三、文件/自定義表情傳送。
大家都知道,QQ可以傳送文件,可以發(fā)送自定義表情。先說官方表情。官方表情實際發(fā)送的是命令字,而沒有發(fā)送表情??蛻舳耸盏矫钭趾?#xff0c;會自動解釋為對應的表情。因此,QQ2008正式版的客戶端發(fā)出的新版表情,在2007beta4及以前的版本無法找到相對應的表情,就無法解釋,看到的就會是空白信息,但查聊天記錄就會有[表情]字樣。
自定義表情的傳送是以文件傳輸方式進行的。
下面說文件傳輸方式:A要向B發(fā)送一個文件,于是發(fā)出一個文件傳送請求。服務器收到這個文件傳送請求后,轉(zhuǎn)發(fā)給B,同時或者在B應答后,將A的IP地址同時發(fā)送給B。B這個時候就得到了A的真實IP。這里的IP是你的本機IP。也就是說,如果A處在內(nèi)網(wǎng),B得到的地址就是一個內(nèi)網(wǎng)地址。B得到了A的地址之后,就會嘗試去連接A。如果B也處于內(nèi)網(wǎng),那么,顯然A跟B之間的連接是無法建立的。這個時候,客戶端就會請求服務器進行文件中轉(zhuǎn)。因為服務器具有公網(wǎng) IP,處在內(nèi)網(wǎng)的A跟B都是可以連接到服務器的,于是,A跟B的文件傳送就通過服務器中轉(zhuǎn)的方式,順利進行。(注:服務器文件中轉(zhuǎn)使用443端口)
其實紅字部分是不正確的,QQ的文件傳輸采用的是P2P,也就是為什么在相同局域網(wǎng)下,兩個人用QQ傳文件會非???#xff0c;這里用到的是NAT打洞技術(shù),下面我會詳細的說明
無論是傳文件還是聊天文字技術(shù)上都可以使用P2P,P2P 都可以用UDP實現(xiàn),而UDP在NAT打洞上面更加方便和成熟,所以騰訊應該是優(yōu)先UDP,但是使用UDP為了增加可靠性,尤其是傳文件,就要用到UDP模擬TCP ,也就是他所謂的新TCP,看來在UDP安全通信方面,騰訊應該很牛逼了
下面只說的技術(shù),具體QQ是不是這樣的只能問騰訊了
(TCP與UDP的打洞技術(shù)過程基本相同,支持TCP打洞的nat設備不多,洞其實就是socket,udp和tcp的socket api的問題,具體以后寫文章研究一下)
* 注:什么是內(nèi)網(wǎng)、公網(wǎng)
內(nèi)網(wǎng)、公網(wǎng)是兩種Internet的接入方式。
內(nèi)網(wǎng)接入方式:上網(wǎng)的計算機得到的IP地址是Inetnet上的保留地址,保留地址有如下3種形式:
10.x.x.x(學校內(nèi)網(wǎng))
172.16.x.x至172.31.x.x
192.168.x.x(自用路由)
內(nèi)網(wǎng)的計算機以NAT(網(wǎng)絡地址轉(zhuǎn)換)協(xié)議,通過一個公共的網(wǎng)關(guān)訪問Internet。
內(nèi)網(wǎng)的計算機可向Internet上的其他計算機發(fā)送連接請求,但Internet上其他的計算機無法向內(nèi)網(wǎng)的計算機發(fā)送連接請求。
公網(wǎng)接入方式:上網(wǎng)的計算機得到的IP地址是Inetnet上的非保留地址。公網(wǎng)的計算機和Internet上的其他計算機可隨意互相訪問。
*注:Nat技術(shù)基礎
NAT(Network Address?Translators),網(wǎng)絡地址轉(zhuǎn)換:網(wǎng)絡地址轉(zhuǎn)換是在IP地址日益缺乏的情況下產(chǎn)生的,它的主要目的就是為了能夠地址重用。NAT分為兩大類,基本的NAT和NAPT(Network?Address/Port Translator)。
最開始NAT是運行在路由器上的一個功能模塊。
最先提出的是基本的NAT,它的產(chǎn)生基于如下事實:一個私有網(wǎng)絡(域)中的節(jié)點中只有很少的節(jié)點需要與外網(wǎng)連接(呵呵,這是在上世紀90年代中期提出的)。那么這個子網(wǎng)中其實只有少數(shù)的節(jié)點需要全球唯一的IP地址,其他的節(jié)點的IP地址應該是可以重用的。
因此,基本的NAT實現(xiàn)的功能很簡單,在子網(wǎng)內(nèi)使用一個保留的IP子網(wǎng)段,這些IP對外是不可見的。子網(wǎng)內(nèi)只有少數(shù)一些IP地址可以對應到真正全球唯一的IP地址。如果這些節(jié)點需要訪問外部網(wǎng)絡,那么基本NAT就負責將這個節(jié)點的子網(wǎng)內(nèi)IP轉(zhuǎn)化為一個全球唯一的IP然后發(fā)送出去。(基本的NAT會改變IP包中的原IP地址,但是不會改變IP包中的端口)
關(guān)于基本的NAT可以參看RFC 1631
另外一種NAT叫做NAPT,從名稱上我們也可以看得出,NAPT不但會改變經(jīng)過這個NAT設備的IP數(shù)據(jù)報的IP地址,還會改變IP數(shù)據(jù)報的TCP/UDP端口?;綨AT的設備可能我們見的不多(呵呵,我沒有見到過),NAPT才是我們真正討論的主角。
Client A
10.0.0.1:1234
A是其中的一臺計算機,這個網(wǎng)絡的網(wǎng)關(guān)(一個NAT設備)的外網(wǎng)IP是155.99.25.11(應該還有一個內(nèi)網(wǎng)的IP地址,比如10.0.0.10)。
如果Client?A中的某個進程(這個進程創(chuàng)建了一個UDP Socket,這個Socket綁定1234端口)想訪問外網(wǎng)主機18.181.0.31的1235端口,那么當數(shù)據(jù)包通過NAT時會發(fā)生什么事情呢?
首先NAT會改變這個數(shù)據(jù)包的原IP地址,改為155.99.25.11。
接著NAT會為這個傳輸創(chuàng)建一個Session(Session是一個抽象的概念,如果是TCP,也許Session是由一個SYN包開始,以一個FIN包結(jié)束。而UDP呢,以這個IP的這個端口的第一個UDP開始,結(jié)束呢,呵呵,也許是幾分鐘,也許是幾小時,這要看具體的實現(xiàn)了)并且給這個Session分配一個端口,比如62000,然后改變這個數(shù)據(jù)包的源端口為62000。所以本來是(10.0.0.1:1234->18.181.0.31:1235)的數(shù)據(jù)包到了互聯(lián)網(wǎng)上變?yōu)榱?#xff08;155.99.25.11:62000->18.181.0.31:1235)。
一旦NAT創(chuàng)建了一個Session后,NAT會記住62000端口對應的是10.0.0.1的1234端口,以后從18.181.0.31發(fā)送到62000端口的數(shù)據(jù)會被NAT自動的轉(zhuǎn)發(fā)到10.0.0.1上。(注意:這里是說18.181.0.31發(fā)送到62000端口的數(shù)據(jù)會被轉(zhuǎn)發(fā),其他的IP發(fā)送到這個端口的數(shù)據(jù)將被NAT拋棄)這樣Client?A就與Server S1建立以了一個連接。
首先如果兩個機子全部在外網(wǎng),也就是他們可以直接相連,那么P2P一點問題也沒有
第二如果兩個機子一個是內(nèi)網(wǎng)A,一個是外網(wǎng)B
1.如果內(nèi)網(wǎng)的主動想外網(wǎng)的請求連接,那么連接就像上面的解釋一樣
2.但是洞只能有內(nèi)網(wǎng)來打,洞是有方向性的(session保存這個信息),所以當外網(wǎng)的想主動和內(nèi)網(wǎng)的連接時,就需要中介服務器,服務器通知內(nèi)網(wǎng)A向B打洞來建立連接
第三如果兩個機子一個是內(nèi)網(wǎng)A,另一個是另外一個內(nèi)網(wǎng)B
網(wǎng)絡環(huán)境描述:
內(nèi)網(wǎng)1NAT:NAT1/218.7.32.28
內(nèi)網(wǎng)1中一臺主機A:ClientA/192.168.1.128
內(nèi)網(wǎng)2NAT:NAT2/218.7.31.221
內(nèi)網(wǎng)2中一臺主機B:ClientB/192.168.0.5
公網(wǎng)服務器:Server
?
首先讓ClientA和ClientB登錄到服務器Server(假如兩臺主機都采用2347端口),此時NAT1和NAT2會分別為ClientA和 ClientB打開一個指向Server的洞(NAT1上218.7.32.28:26756和NAT2上218.7.31.221:27550)。服務器應改記錄這兩個客戶端的信息(關(guān)鍵是那兩個洞的信息)。當ClientA與ClientB要建立會話時,ClientA首先用2347端口向NAT2的洞發(fā)送一個數(shù)據(jù)包,當然這個數(shù)據(jù)包會被NAT2所丟棄,但是由于這是從NAT1內(nèi)部向外部發(fā)送數(shù)據(jù),所以NAT1為ClientA打開了一個指向NAT2 的洞。而且這個新洞與原來NAT1上指向Server的舊洞的是同一個洞(因為是同一個端口26756),所以這里可以說這個洞具有了兩個方向(關(guān)鍵),同時指向 Server和NAT2。這時ClientA應該通知Server,告訴ClientB,現(xiàn)在可以向NAT1的那個洞 (218.7.32.28:26756)發(fā)送數(shù)據(jù)包了。當ClientB向NAT1的那個洞發(fā)送數(shù)據(jù)以后,NAT2也為ClientB打了一個指向 NAT1的洞,這是可以說ClientA與ClientB的會話就建立完成了,他們可以不依賴Server進行通信了。如果以后ClientA和 ClientB還需要建立其他會話 ,那么這個牽線的“媒人”可以不是Server,而可以是ClientA或ClientB了。
第四如果兩個機子都是同一個內(nèi)網(wǎng)
用上面的方法肯定可以,那么如果NAT支持loopback(就是本地到本地的轉(zhuǎn)換),A,B可以連接,但是比較浪費帶寬和NAT,一般的時候都不會用loopback,會直接內(nèi)網(wǎng)P2P(我覺得QQ客戶端可以做一下判斷以選擇內(nèi)網(wǎng)直接P2P)
注:
NAT對session的處理
以下分析NAPT是依據(jù)什么策略來判斷是否要為一個請求發(fā)出的UDP數(shù)據(jù)包建立Session的.主要有一下幾個策略:
A. 源地址(內(nèi)網(wǎng)IP地址)不同,忽略其它因素, 在NAPT上肯定對應不同的Session
B. 源地址(內(nèi)網(wǎng)IP地址)相同,源端口不同,忽略其它的因素,則在NAPT上也肯定對應不同的Session
C. 源地址(內(nèi)網(wǎng)IP地址)相同,源端口相同,目的地址(公網(wǎng)IP地址)相同,目的端口不同,則在NAPT上肯定對應同一個Session
D. 源地址(內(nèi)網(wǎng)IP地址)相同,源端口相同,目的地址(公網(wǎng)IP地址)不同,忽略目的端口,則在NAPT上是如何處理Session的呢?(這個要根據(jù)下面NAT的種類區(qū)別,Cone相同,Symmetic不同)
NAT分類
根據(jù)Stun協(xié)議(RFC3489),NAT大致分為下面四類
?
?
?
?
?
1)????? Full Cone
?
?
這種NAT內(nèi)部的機器A連接過外網(wǎng)機器C后,NAT會打開一個端口.然后外網(wǎng)的任何發(fā)到這個打開的端口的UDP數(shù)據(jù)報都可以到達A.不管是不是C發(fā)過來的.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
任何發(fā)送到 NAT(202.100.100.100:8000)的數(shù)據(jù)都可以到達A(192.168.8.100:5000)
2)????? Restricted Cone
這種NAT內(nèi)部的機器A連接過外網(wǎng)的機器C后,NAT打開一個端口.然后C可以用任何端口和A通信.其他的外網(wǎng)機器不行.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
任何從C發(fā)送到 NAT(202.100.100.100:8000)的數(shù)據(jù)都可以到達A(192.168.8.100:5000)
3)????? Port Restricted Cone
這種NAT內(nèi)部的機器A連接過外網(wǎng)的機器C后,NAT打開一個端口.然后C可以用原來的端口和A通信.其他的外網(wǎng)機器不行.
例如 A:192.168.8.100 NAT:202.100.100.100 C:292.88.88.88
A(192.168.8.100:5000) -> NAT(202.100.100.100 : 8000) -> C(292.88.88.88:2000)
C(202.88.88.88:2000)發(fā)送到 NAT(202.100.100.100:8000)的數(shù)據(jù)都可以到達A(192.168.8.100:5000)
以上三種NAT通稱Cone NAT.我們只能用這種NAT進行UDP打洞.
4)????? Symmetic
?
?
?
對于這種NAT.連接不同的外部目標.原來NAT打開的端口會變化.而Cone NAT不會.雖然可以用端口猜測.但是成功的概率很小.因此放棄這種NAT的UDP打洞.
?
?
第一種情況, 雙方都是Symmetric NAPT:
此情況應給不存在什么問題,肯定是不支持UDP穿透。
第二種情況, 雙方都是Cone NAPT:
此情況是我們需要的,可以進行UDP穿透。
第三種情況, 一個是Symmetric NAPT, 一個是Cone NAPT:
這個行不行呢,這個問題留給大家吧 ? 來源?http://softpalace.co.de/?p=279
把4種類型分別標為1234,有兩臺主機A:portA和B:portB(port都為外網(wǎng)端口,是與打洞服務器通信的端口),以及打洞服務器S,情景是B拿到了A:portA的信息,要與A通信。
(1)、A為類型1;無論B為哪種類型,都可以直接與A:portA tcp連接;
(2)、A為類型2;無論B為哪種類型,在A知道B之前都無法直接連接,B給S發(fā)一個打洞請求,S轉(zhuǎn)發(fā)該請求到A。若B的類型為1,則A:portA可直接tcp連接到B:portB;若B的類型為2或3,則A:portA和B:portB各自向?qū)Ψ桨l(fā)送一個一字節(jié)的udp包,分別在自己的路由器上打洞,從此A:portA和B:portB可進行udp通信;若B為類型4,則portB在與不同的ip:port通信時會不一樣,所以A:portA先向B發(fā)送一個一字節(jié)的udp包,在路由器上打洞,然后等待B:portB先發(fā)送數(shù)據(jù),A:portA接收到B:portB的數(shù)據(jù)后,即知道portB,也可互通數(shù)據(jù)了;
(3)、A為類型3;無論B為哪種類型,在A知道B之前都無法直接連接,B給S發(fā)一個打洞請求,S轉(zhuǎn)發(fā)該請求到A。若B的類型為1,則A:portA可直接tcp連接到B:portB;若B的類型為2或3,則A:portA和B:portB各自向?qū)Ψ桨l(fā)送一個一字節(jié)的udp包,分別在自己的路由器上打下洞,從此A:portA和B:portB可進行udp通信;若B為類型4,則portB在與不同的ip:port通信時會不一樣,而A又要求知道portB的才可讓B:portB連進來,所以這種情況A只能猜測與A:portA通信的portB,通信概率小;
(4)、A為類型4;無論B為哪種類型,在A知道B之前都無法直接連接,B給S發(fā)一個打洞請求,S轉(zhuǎn)發(fā)該請求到A。若B的類型為1,則A:portA可直接tcp連接到B:portB;若B的類型為2,則B:portB先向A發(fā)送一個一字節(jié)的udp包,在路由器上打洞,然后等待A:portA先發(fā)送數(shù)據(jù),B:portB接收到A:portA的數(shù)據(jù)后,即知道portA,也可互通數(shù)據(jù)了;若B的類型為3,見(3)中B為類型4的描述;若B為4,雙方無法知道對方的端口,無法通信。
Symmetric NAPT穿刺方法————————
僅對Symmetric?NAPT,我想了一個方法,沒條件試驗,大家?guī)臀以囋?#xff1a;1,ClientA告訴Server自己的IP1和偵聽端口;2,ClientB告訴Server自己的IP2和偵聽端口;3,Server告知ClientB,ClientA的IP1(注意,僅僅需要IP,不需說偵聽端口);4,ClientB向ClientA打洞,方法是開啟一個端口M,對IP1的任意端口連發(fā)三個UDP包;6,ClientB在剛才的端口M偵聽;7,ClientB生成一個GUID,告知Server,以便讓Server明白,ClientB已經(jīng)打過洞了,現(xiàn)在正在偵聽;8,Server告訴ClientA,ClientB上線了,打過洞了,現(xiàn)在正在偵聽,并且告訴ClientA,IP2和那個GUID;9,ClientA向IP2:端口N發(fā)送UDP包,N從0~65535增長,一共發(fā)3輪,即3*65536個包,包的格式為“Hello+端口N+GUID”;10,ClientB收到1個含GUID的包,斷定是ClientA發(fā)來的,不是ClientX發(fā)來的,取出端口N,通過Server告知ClientA,(或許可以直接告知ClientA,這得看ClientA是什么情況)。我不經(jīng)常上來,誰試成功了麻煩告訴我一下:icebird@263.net,謝謝。
總結(jié)
- 上一篇: .NET版Web后台快速开发框架 Col
- 下一篇: Linux:编译生成内核模块(来自国嵌的