首页 > 代码库 > QQ 通信原理分析(转)

QQ 通信原理分析(转)

之前写过一个简单的IM,当时遇到过各种令人崩溃的问题;事后也没有做很好的总结,现在看到这篇文章,感慨良多,特此转摘过来,以做备忘。

出处:http://blog.csdn.net/lxnkobe/article/details/7521331

下面有4个基本的问答:

问题一:为什么只要可以连上互联网的计算机都可以用QQ相互建立通信,而不需要固定IP?
也就是这个QQ用户端是怎样找到另一个QQ用户的,而用户在每次使用时他可能用的是不同的计算机,有着不同的IP地址。
服务器端不会以qq用户端的ip作为唯一标识,服务器端会以qq账号作为唯一标识,所以这个账号肯定是唯一的,一个账号登陆时每次都可以有不同的ip地址,但账号却相同,当账号a登陆服务器,服务器会记录下账号a的ip地址,去通知a的好友,告诉他们,a上线了和现在的ip地址,a的好友就可以跟他通信了
问题二:是不是QQ在通信时根本不适用IP,QQ客户端先是访问QQ服务器端,然后QQ服务器端再为要建立连接的QQ客户端建立连接?
只要是网络层的通信,都会涉及到ip/tcp协议,就肯定需要ip,qq客户端登陆qq服务器,服务器只是记录登陆状态,不会一直和qq保持通信,只会每隔一段时间发送心跳数据包,来确实qq客户端是否还在网络上。当qq客户端a上线后,服务器会告诉a,目前a的在线好友的最新ip地址,当a需要与任意好友通信时,直接使用ip地址就ok了。qq客户端a与qq客户端b通信,可以两种方式,第一就是qq服务器有转发的服务器,第二是,a与b直接通信,不会告诉qq服务器
问题三:QQ客户端可以访问QQ服务器端,然后服务器端获取QQ客户端的IP建立通信,是不是这样的过程。。。如果是,这个过程是怎么处理的呢?
是这样的过程,qq客户端请求一个连接给服务器,服务器接收后,知道qq端a上线,把qq端a的账号跟目前的ip会记录下来,放在在线列表里或者其他的地方,然后每隔几分钟或者几秒钟给qq端a发送心跳包,问他是否还在线,来确保qq端a的最新状态。这里客户端与服务器的通信方式是udp。而不会时时刻刻都在用tcp连接。
问题四:QQ客户端虽然IP地址不固定,但是在建立与QQ服务器端的通信时,必须提供自己的IP被服务器获取,然后才能建立他们之间的通信,进而在建立客户端之间的通信。也就是,只要能上网就有IP,只不过客户端的IP,是被QQ客户端获取了,然后才建立通信的。(这是自己的猜测,不知对否。。。)
恩,服务器不会以ip作为唯一标识,会以账号作为唯一标识,但与账号通信的时候会用到账号目前所对应的ip,客户端与客户端通信也如此

QQ有两种登陆模式
一种是比较不常用的:直接登陆服务器,所有信息由服务器转发,这种登陆模式有个特点就是你会发现你使用获取IP版本的QQ无法获取对方的IP~ (这个我不清楚有没有,但是肯定可以)
另一种是普通的:首先连接登陆服务器,在给对发发消息的时候,首先尝试与对方进行打洞连接,如果可以打通消息直接发送给对方,如果不能打通,则消息转发服务器,由服务器转发.(传文件会优先P2P,不行再选择中转,不知道聊天是不是优先P2P的,还是聊天文字是中转的?图片呢?会员表情?这个的确要问tx了,技术上的都是可以实现,选择什么只能问tx了)

如果上面的东西轻松搞定,那么你可以继续看了,如果不知道,那么下面就不用看了

 

先贴一点资料

一、登陆。

不管UDP还是TCP,最终登陆成功之后,QQ都会有一个TCP连接来保持在线状态。这个TCP连接的远程端口一般是80,采用UDP方式登陆的时候,端口是8000。因此,假如你所在的网络开放了80端口(80端口是最常用端口。。就是通常访问Web的端口,禁掉它的话,你的网络对你来说价值已经不大了),但没有屏蔽腾讯的服务器IP,恭喜你,你是可以登陆成功QQ的。
二、聊天消息通信。
采用UDP协议,通过服务器中转方式。因此,现在的IP侦探在你仅仅跟对方发送聊天消息的时候是无法获取到IP的。大家都知道,UDP 协议是不可靠协议,它只管发送,不管对方是否收到的,但它的传输很高效。但是,作为聊天软件,怎么可以采用这样的不可靠方式来传输消息呢?于是,腾讯采用了上层协议来保证可靠传输:如果客户端使用UDP协议发出消息后,服务器收到该包,需要使用UDP协议发回一个应答包。如此来保证消息可以无遗漏传输。之所以会发生在客户端明明看到“消息发送失败”但对方又收到了这个消息的情况,就是因为客户端发出的消息服务器已经收到并转发成功,但客户端由于网络原因没有收到服务器的应答包引起的。
三、文件/自定义表情传送。
大家都知道,QQ可以传送文件,可以发送自定义表情。先说官方表情。官方表情实际发送的是命令字,而没有发送表情。客户端收到命令字后,会自动解释为对应的表情。因此,QQ2008正式版的客户端发出的新版表情,在2007beta4及以前的版本无法找到相对应的表情,就无法解释,看到的就会是空白信息,但查聊天记录就会有[表情]字样。
自定义表情的传送是以文件传输方式进行的。
下面说文件传输方式:A要向B发送一个文件,于是发出一个文件传送请求。服务器收到这个文件传送请求后,转发给B,同时或者在B应答后,将A的IP地址同时发送给B。B这个时候就得到了A的真实IP。这里的IP是你的本机IP。也就是说,如果A处在内网,B得到的地址就是一个内网地址。B得到了A的地址之后,就会尝试去连接A。如果B也处于内网,那么,显然A跟B之间的连接是无法建立的。这个时候,客户端就会请求服务器进行文件中转。因为服务器具有公网 IP,处在内网的A跟B都是可以连接到服务器的,于是,A跟B的文件传送就通过服务器中转的方式,顺利进行。(注:服务器文件中转使用443端口)

其实红字部分是不正确的,QQ的文件传输采用的是P2P,也就是为什么在相同局域网下,两个人用QQ传文件会非常快,这里用到的是NAT打洞技术,下面我会详细的说明

无论是传文件还是聊天文字技术上都可以使用P2P,P2P 都可以用UDP实现,而UDP在NAT打洞上面更加方便和成熟,所以腾讯应该是优先UDP,但是使用UDP为了增加可靠性,尤其是传文件,就要用到UDP模拟TCP ,也就是他所谓的新TCP,看来在UDP安全通信方面,腾讯应该很牛逼了

下面只说的技术,具体QQ是不是这样的只能问腾讯了

(TCP与UDP的打洞技术过程基本相同,支持TCP打洞的nat设备不多,洞其实就是socket,udp和tcp的socket api的问题,具体以后写文章研究一下)

* 注:什么是内网、公网
内网、公网是两种Internet的接入方式。
内网接入方式:上网的计算机得到的IP地址是Inetnet上的保留地址,保留地址有如下3种形式:
10.x.x.x(学校内网)
172.16.x.x至172.31.x.x
192.168.x.x(自用路由)
内网的计算机以NAT(网络地址转换)协议,通过一个公共的网关访问Internet。
内网的计算机可向Internet上的其他计算机发送连接请求,但Internet上其他的计算机无法向内网的计算机发送连接请求。
公网接入方式:上网的计算机得到的IP地址是Inetnet上的非保留地址。公网的计算机和Internet上的其他计算机可随意互相访问。

*注:Nat技术基础

NAT(Network Address Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的,它的主要目的就是为了能够地址重用。NAT分为两大类,基本的NAT和NAPT(Network Address/Port Translator)。

最开始NAT是运行在路由器上的一个功能模块。

最先提出的是基本的NAT,它的产生基于如下事实:一个私有网络(域)中的节点中只有很少的节点需要与外网连接(呵呵,这是在上世纪90年代中期提出的)。那么这个子网中其实只有少数的节点需要全球唯一的IP地址,其他的节点的IP地址应该是可以重用的。
因此,基本的NAT实现的功能很简单,在子网内使用一个保留的IP子网段,这些IP对外是不可见的。子网内只有少数一些IP地址可以对应到真正全球唯一的IP地址。如果这些节点需要访问外部网络,那么基本NAT就负责将这个节点的子网内IP转化为一个全球唯一的IP然后发送出去。(基本的NAT会改变IP包中的原IP地址,但是不会改变IP包中的端口)
关于基本的NAT可以参看RFC 1631

另外一种NAT叫做NAPT,从名称上我们也可以看得出,NAPT不但会改变经过这个NAT设备的IP数据报的IP地址,还会改变IP数据报的TCP/UDP端口。基本NAT的设备可能我们见的不多(呵呵,我没有见到过),NAPT才是我们真正讨论的主角。

Client A
10.0.0.1:1234

A是其中的一台计算机,这个网络的网关(一个NAT设备)的外网IP是155.99.25.11(应该还有一个内网的IP地址,比如10.0.0.10)。

如果Client A中的某个进程(这个进程创建了一个UDP Socket,这个Socket绑定1234端口)想访问外网主机18.181.0.31的1235端口,那么当数据包通过NAT时会发生什么事情呢?

首先NAT会改变这个数据包的原IP地址,改为155.99.25.11。

接着NAT会为这个传输创建一个Session(Session是一个抽象的概念,如果是TCP,也许Session是由一个SYN包开始,以一个FIN包结束。而UDP呢,以这个IP的这个端口的第一个UDP开始,结束呢,呵呵,也许是几分钟,也许是几小时,这要看具体的实现了)并且给这个Session分配一个端口,比如62000,然后改变这个数据包的源端口为62000。所以本来是(10.0.0.1:1234->18.181.0.31:1235)的数据包到了互联网上变为了(155.99.25.11:62000->18.181.0.31:1235)。

一旦NAT创建了一个Session后,NAT会记住62000端口对应的是10.0.0.1的1234端口,以后从18.181.0.31发送到62000端口的数据会被NAT自动的转发到10.0.0.1上。(注意:这里是说18.181.0.31发送到62000端口的数据会被转发,其他的IP发送到这个端口的数据将被NAT抛弃)这样Client A就与Server S1建立以了一个连接。

首先如果两个机子全部在外网,也就是他们可以直接相连,那么P2P一点问题也没有

第二如果两个机子一个是内网A,一个是外网B

1.如果内网的主动想外网的请求连接,那么连接就像上面的解释一样

2.但是洞只能有内网来打,洞是有方向性的(session保存这个信息),所以当外网的想主动和内网的连接时,就需要中介服务器,服务器通知内网A向B打洞来建立连接

第三如果两个机子一个是内网A,另一个是另外一个内网B

网络环境描述:
内网1NAT:NAT1/218.7.32.28
内网1中一台主机A:ClientA/192.168.1.128
内网2NAT:NAT2/218.7.31.221
内网2中一台主机B:ClientB/192.168.0.5
公网服务器:Server

 

首先让ClientA和ClientB登录到服务器Server(假如两台主机都采用2347端口),此时NAT1和NAT2会分别为ClientA和 ClientB打开一个指向Server的洞(NAT1上218.7.32.28:26756和NAT2上218.7.31.221:27550)。服务器应改记录这两个客户端的信息(关键是那两个洞的信息)。当ClientA与ClientB要建立会话时,ClientA首先用2347端口向NAT2的洞发送一个数据包,当然这个数据包会被NAT2所丢弃,但是由于这是从NAT1内部向外部发送数据,所以NAT1为ClientA打开了一个指向NAT2 的洞。而且这个新洞与原来NAT1上指向Server的旧洞的是同一个洞(因为是同一个端口26756),所以这里可以说这个洞具有了两个方向(关键),同时指向 Server和NAT2。这时ClientA应该通知Server,告诉ClientB,现在可以向NAT1的那个洞 (218.7.32.28:26756)发送数据包了。当ClientB向NAT1的那个洞发送数据以后,NAT2也为ClientB打了一个指向 NAT1的洞,这是可以说ClientA与ClientB的会话就建立完成了,他们可以不依赖Server进行通信了。如果以后ClientA和 ClientB还需要建立其他会话 ,那么这个牵线的“媒人”可以不是Server,而可以是ClientA或ClientB了。

第四如果两个机子都是同一个内网

用上面的方法肯定可以,那么如果NAT支持loopback(就是本地到本地的转换),A,B可以连接,但是比较浪费带宽和NAT,一般的时候都不会用loopback,会直接内网P2P(我觉得QQ客户端可以做一下判断以选择内网直接P2P)

注:

NAT对session的处理

以下分析NAPT是依据什么策略来判断是否要为一个请求发出的UDP数据包建立Session的.主要有一下几个策略:

A. 源地址(内网IP地址)不同,忽略其它因素, 在NAPT上肯定对应不同的Session

B. 源地址(内网IP地址)相同,源端口不同,忽略其它的因素,则在NAPT上也肯定对应不同的Session

C. 源地址(内网IP地址)相同,源端口相同,目的地址(公网IP地址)相同,目的端口不同,则在NAPT上肯定对应同一个Session

D. 源地址(内网IP地址)相同,源端口相同,目的地址(公网IP地址)不同,忽略目的端口,则在NAPT上是如何处理Session的呢?(这个要根据下面NAT的种类区别,Cone相同,Symmetic不同)

NAT分类

根据Stun协议(RFC3489),NAT大致分为下面四类

1)      Full Cone

这种NAT内部的机器A连接过外网机器C后,NAT会打开一个端口.然后外网的任何发到这个打开的端口的UDP数据报都可以到达A.不管是不是C发过来的.

例如 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)

任何发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)

2)      Restricted Cone

这种NAT内部的机器A连接过外网的机器C后,NAT打开一个端口.然后C可以用任何端口和A通信.其他的外网机器不行.

例如 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发送到 NAT(202.100.100.100:8000)的数据都可以到达A(192.168.8.100:5000)

3)      Port Restricted Cone

这种NAT内部的机器A连接过外网的机器C后,NAT打开一个端口.然后C可以用原来的端口和A通信.其他的外网机器不行.

例如 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)发送到 NAT(202.100.100.100:8000)的数据都可以到达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