在网络7层协议中,如果想使用UDP协议达到TCP协议的效果,可以在哪层做文章?(QQ 为什么采用 UDP 协议,而不采用 TCP 协议实现?)
为了解决这题,可以具体看看下面这个讨论。
这个说法应该比较可信的.
那么为什么呢?最本质上UDP的优势还是带宽的利用。这一切要回归到99~03年的网络状况,当时网络的特点就是接入带宽很窄而且抖动特别厉害。所谓抖动可能是多方面的,例如延时突发性地暴增、也有可能是由于路由层面的变化突然导致路由黑洞,还各种等等等等的问题。TCP因为拥塞控制、保证有序等原因,在这种网络状态上对带宽的利用是非常低的。而且因为网络抖动的原因,应用层心跳超时(一般不依靠keepalive)应用层主动断掉socket之后TCP需要三次握手才能重新建立链接,一旦出现频繁的小抖动就会使得带宽利用更低。而等待四次挥手的时间,也会占用服务器上宝贵的资源。
总结来说,当网络差到一定程度了,TCP的优势反而会成为劣势。
这时候我们再看看UDP在这种情况下的表现。使用UDP对抗网络抖动,说到底就是在应用层比TCP更快地探测和重传,一旦超过一定的时间没有收到回复,客户端可以选择马上重试或者换一个IP:PORT重试(假如你的服务像QQ一样有多个接入),在服务器端则可以果断地断掉socket。而可以应用UDP的时候,往往是你的应用层协议本身已经具备了一定的面向连接的特性。如果你应用层的协议已经达到了一定程度的消息幂等,客户端可以几乎无脑地进行重传,这样就可以尽可能地降低网络抖动的影响,同时也可以尽可能地利用整个带宽。而刚好QQ的协议,就具备类似的特点。
简单来说就是我们可以使用UDP实现一个面向连接协议,这个协议可以很好地适应当时的网络状况和QQ本身的业务。但凡事都有成本,成本就是你的应用层协议本身需要去实现抵抗网络异常带来的问题。例如乱序、例如业务数据的分片和重组、例如网络状态探测等等等等。。。
而现在UDP也应用在很多跨运营商、跨地域、跨机房之间的服务调用当中。原因无它,就是网络烂到一定程度了。
即使是UDP协议,完全通过服务器中转、信息传输加密(用户到服务器之间的,= =)、保存聊天内容协助警方等都是钱多用户傻之后的事了。次早期的QQ官方客户端提供了默认不勾选的“通过服务器中转”选项,可见那时信息还是允许客户端和客户端之间直接发送的。
至于UDP穿墙之类的优势,国内最常见的NAT共享网络又不会影响客户端主动发起TCP链接,应该不是使用UDP的主要原因。
[1] http://en.wikipedia.org/wiki/C10k_problem
1.登陆过程,客户端client 采用TCP协议向服务器server发送信息,HTTP协议下载信息。登陆之后,会有一个TCP连接来保持在线状态。
2.和好友发消息,客户端client采用UDP协议,但是需要通过服务器转发。腾讯为了确保传输消息的可靠,采用上层协议来保证可靠传输。如果消息发送失败,客户端会提示消息发送失败,并可重新发送。
3.如果是在内网里面的两个客户端传文件,QQ采用的是P2P技术,不需要服务器中转。
QQ既有UDP也有TCP!
不管UDP还是TCP,最终登陆成功之后,QQ都会有一个TCP连接来保持在线状态。这个TCP连接的远程端口一般是80,采用UDP方式登陆的时候,端口是8000。
UDP协议是无连接方式的协议,它的效率高,速度快,占资源少,但是其传输机制为不可靠传送,必须依靠辅助的算法来完成传输控制。QQ采用的通信协议以UDP为主,辅以TCP协议。由于QQ的服务器设计容量是海量级的应用,一台服务器要同时容纳十几万的并发连接,因此服务器端只有采用UDP协议与客户端进行通讯才能保证这种超大规模的服务。
QQ客户端之间的消息传送也采用了UDP模式,因为国内的网络环境非常复杂,而且很多用户采用的方式是通过代理服务器共享一条线路上网的方式,在这些复杂的情况下,客户端之间能彼此建立起来TCP连接的概率较小,严重影响传送信息的效率。而UDP包能够穿透大部分的代理服务器,因此QQ选择了UDP作为客户之间的主要通信协议。
采用UDP协议,通过服务器中转方式。因此,现在的IP侦探在你仅仅跟对方发送聊天消息的时候是无法获取到IP的。大家都知道,UDP 协议是不可靠协议,它只管发送,不管对方是否收到的,但它的传输很高效。但是,作为聊天软件,怎么可以采用这样的不可靠方式来传输消息呢?于是,腾讯采用了上层协议来保证可靠传输:如果客户端使用UDP协议发出消息后,服务器收到该包,需要使用UDP协议发回一个应答包。如此来保证消息可以无遗漏传输。之所以会发生在客户端明明看到“消息发送失败”但对方又收到了这个消息的情况,就是因为客户端发出的消息服务器已经收到并转发成功,但客户端由于网络原因没有收到服务器的应答包引起的。
尽管UDP协议远没TCP协议那么庞大、复杂,但是,要想将UDP描述清楚,用好UDP却要比TCP难不少,于是文章从下笔写,到最终写成,断断续续拖了好几个月。
说起网络socket,大家自然会想到TCP,用的最多也是TCP,UDP在大家的印象中是作为TCP的补充而存在,是无连接、不可靠、无序、无流量控制的传输层协议。UDP的无连接性已经深入人心,协议上的无连接性指的是一个UDP的Endpoint1(IP,PORT),可以向多个UDP的Endpointi(IP,PORT)发送数据包,也可以接收来自多个UDP的Endpointi(IP,PORT)的数据包。
实现上,需要考虑这样一个特殊情况:UDP Client 在Endpoint_C1只往UDP Server的Endpoint_S1发送数据包,并且只接收来自Endpoint_S1的数据包,把UDP通信双方都固定下来,这样不就形成一条单向的虚”连接”了么?
1. UDP的”连接性”估计很多同学认为UDP的连接性只是将UDP通信双方都固定下来了,一对一只是多对多的一个特例而已,这样UDP连接不连接到无所谓了。果真如此吗?其实不然,UDP的连接性可以带来以下两个好处:
1.1 高效率、低消耗我们知道Linux系统有用户空间(用户态)和内核空间(内核态)之分,对于x86处理器以及大多数其它处理器,用户空间和内核空间之前的切换是比较耗时(涉及到上下文的保存和恢复,一般3种情况下会发生用户态到内核态的切换:发生系统调用时、产生异常时、中断时)。
那么对于一个高性能的服务应该减少频繁不必要的上下文切换,如果切换无法避免,那么尽量减少用户空间和内核空间的数据交换,减少数据拷贝。熟悉socket编程的同学对下面几个系统调用应该比较熟悉了,由于UDP是基于用户数据报的,只要数据包准备好就应该调用一次send或sendto进行发包,当然包的大小完全由应用层逻辑决定的。
细看两个系统调用的参数便知道,sendto比send的参数多2个,这就意味着每次系统调用都要多拷贝一些数据到内核空间。同时,参数到内核空间后,内核还需要初始化一些临时的数据结构来存储这些参数值(主要是对端Endpoint_S的地址信息),在数据包发出去后,内核还需要在合适的时候释放这些临时的数据结构。进行UDP通信的时候,如果首先调用connect绑定对端Endpoint_S的后,那么就可以直接调用send来给对端Endpoint_S发送UDP数据包了。
用户在connect之后,内核会永久维护一个存储对端Endpoint_S的地址信息的数据结构,内核不再需要分配/删除这些数据结构,只需要查找就可以了,从而减少了数据的拷贝。这样对于connect方而言,该UDP通信在内核已经维护这一个“连接”了,那么在通信的整个过程中,内核都能随时追踪到这个“连接”。
1.2 错误提示
相信大家写UDP Socket程序的时候,有时候在第一次调用sendto给一个unconnected UDP socket发送UDP数据包时,接下来调用recvfrom()或继续调sendto的时候会返回一个ECONNREFUSED错误。对于一个无连接的UDP是不会返回这个错误的,之所以会返回这个错误,是因为你明确调用了connect去连接远端的Endpoint_S了。那么这个错误是怎么产生的呢?没有调用connect的UDP Socket为什么无法返回这个错误呢?
点击全文链接(体验更佳哦~):从UDP的”连接性”说起
腾讯优测(http://utest.qq.com)是专业的移动云测试平台,提供【兼容性自动化测试】【云手机】【漏洞检测】等多维度测试服务。
使用UDP进行交互通信的好处在于,延迟较短,对数据丢失的处理比较简单。同时,TCP是一个全双工协议,需要建立连接,所以网络开销也会相对大。如果使用QQ语音和QQ视频的话,UDP的优势就更为突出了,首先延迟较小。最重要的一点是不可靠传输,这意味着如果数据丢失的话,不会有重传。因为用户一般来说可以接受图像稍微模糊一点,声音稍微不清晰一点,但是如果在几秒钟以后再出现之前丢失的画面和声音,这恐怕是很难接受的。
另外,QQ应该是优先采用UDP协议,如果不通的话会自动转为TCP
如果说不用TCP是效率方面使用不方便的话。当年马化腾想4000元把QQ卖掉,难道当初开发的开始的时候还会想到有亿级的用户?
网站HTTP那么多用户,不也基于TCP协议?
说什么两个客户端之间用UDP通信来解决两个局域网内的用户连接的话,那真是。。。。
曾经,或许是,但现在绝对不是,难道QQ如今发送消息是两个客户端直接发的?不需要QQ服务器的中转,监控,屏蔽?
用udp的原因:
1 节省创业成本。udp比tcp开销少,意味着一台服务器能服务的用户更多,并且用udp更容易普遍使用p2p,不用服务器中转,更是大大节省中央服务器的开销。
2 灵活。形象的说,tcp就是在udp基础上实现的一套用起来更简单更傻瓜的协议。用更底层的方式可以构建更高效灵活并符合自己需求的协议,当然是更优的,但提高了开发成本,最基本的,离开局域网后udp丢包概率大,收到包时候的次序可能都是乱的,包括对方还有没有网络,这些全都需要自己去维护,而tcp的数据准确性那都是tcp协议就帮程序员维护好的,对方断线你就知道收到通知了。
3 udp打洞能更容易在不同品牌型号的路由情况下实现p2p,无论聊天文本、语音视频、文件收发都可以实现,对upnp等的支持,很多年了路由器都没有统一,但udp打洞来实现p2p基本上都没问题。
4 后来随着得到投资,以及盈利增加,资金实力强了,服务器成本不再是大问题,才逐步增加了不同功能中tcp的使用比例,这都是后来的事了。
QQ最早为什么使用UDP我不知道,我猜测是开发者偷懒,用UDP发包收包多简单啊,早期也没有丢包重传(据说后面被投诉多了,才在应用层上加了丢包重传)。
至于上面有回答说,用UDP模拟实现了TCP的功能,就现在的情况我可以说,是没有的。
目前都是在应用层实现丢包重传(1秒间隔,最多n次重传),因为这个导致业务服务器都需要做去重与重传,负担很重。
mac os
$ lsof -i -n |grep -i qq
QQPlatfor 555 **** 15u IPv4 0xe36af4996ef57975 0t0 TCP *:49969 (LISTEN)
QQPlatfor 555 **** 18u IPv4 0xe36af4996ef567d5 0t0 TCP 127.0.0.1:49969->127.0.0.1:49242 (ESTABLISHED)
QQPlatfor 555 **** 19u IPv4 0xe36af49973dd9975 0t0 TCP 127.0.0.1:49969->127.0.0.1:49586 (ESTABLISHED)
QQ 5910 **** 21u IPv4 0xe36af49974edb2c5 0t0 UDP 127.0.0.1:5800
QQ 5910 **** 24u IPv4 0xe36af49982299245 0t0 TCP 192.168.0.107:63614->183.60.49.182:https (ESTABLISHED)
QQ 5910 **** 33u IPv4 0xe36af49976641635 0t0 TCP 127.0.0.1:49586->127.0.0.1:49969 (ESTABLISHED)
QQ 5910 **** 44u IPv4 0xe36af49980abf3e5 0t0 TCP 10.20.156.64:60963->59.37.109.170:http-alt (CLOSE_WAIT)
QQ 5910 **** 45u IPv4 0xe36af49982021975 0t0 TCP 192.168.0.107:63989->59.37.108.45:http-alt (CLOSE_WAIT)
QQ 5910 **** 46u IPv4 0xe36af4997202db15 0t0 TCP 10.20.156.64:61282->59.37.108.42:http-alt (CLOSE_WAIT)
QQ 5910 **** 48u IPv4 0xe36af499816e8245 0t0 TCP 10.20.156.64:61283->14.215.154.175:http-alt (CLOSE_WAIT)
TCP或HTTP理论上是可靠连接,但是在网络不好的时候,其实根本不可靠。反而程序员会因为TCP理论上的可靠而不去做底层的重发机制。
那么在网络不好时,用TCP和UDP的区别仅仅是,程序员会收到一个TCP错误,用UDP则会在沉默中丢失数据。
但是如果程序员不手动处理TCP错误,只是“铛”的弹个对话框,把错误报告给用户,那么其实对用户来说一点用都没有。
另一方面,要想在上层手动处理TCP错误,根本做不到嘛。因为应用层重发请求常常导致服务器重复多次处理相同请求的bug。
我们可以看到,网络卡的时候,或者电信网通互相访问的时候,玩个游戏就会频繁掉线。这表现出TCP不适应中国国情。
具体的说,TCP丢包时会退避,然后就会超时。但是,在中国丢包往往是因为ISP的路由器丢你的包,而不是你本地带宽不足。对于ISP这种行为,对用户最有利的解决办法应该是暴力重发,抢占更多带宽,而不是主动退避嘛。
TCP是个烂协议,基本上是人尽皆知的常识。我建议能够选择自定义协议的场合,尽量改用谷歌的QUIC代替TCP协议。
那么,不得不使用TCP怎么办呢?
我在前东家做网页游戏时,Flash Player只支持TCP。我的办法是,每隔一两秒就发一个心跳包,对端只要收不到心跳包就暴力建立新TCP连接,而不是文质彬彬去退避。我把这个做法称为BCP(Brutal Control Protocol,残暴控制协议):
欢迎使用!
2、由于采用P2P的方式进行通信,服务器只是汇总在线信息,而P2P要考虑两个内网穿透的问题,那么TCP就不合适了,所以选用UDP协议的原因是为了内网通信穿透的考虑。
例如:
A在某个内网,B在某个内网,两者进行相连通信的话,只能用UDP实现了。
而MSN之类的非P2P的,就是通过A和B都连接到MSN的服务器,再由MSN的服务器进行中转来实现。