QQ 為什麼以 UDP 協議為主,以 TCP 協議為輔?


某次架構師大會上那個58同城做即時通信的人說:原因是因為當時沒有epoll這種可以支持成千上萬tcp並發連接的技術,所以他們使用了udp,然後在udp上面封裝了一下,模擬了一下tcp,解決了大並發的問題,之後因為做的很nb了,雖然epoll這種技術出現了,還是沒有改回使用tcp了.現在再做類似的東西就不需要使用udp了.
這個說法應該比較可信的.


很多人提到keepalive,TCP無法感知網路中斷這些問題。。。這個算是TCP一個容易踩的坑,但這並不能說明UDP就比TCP好(或者說解釋為何要使用UDP)。因為在UDP上面一樣需要面對這些問題,而解決這類問題的方法和在TCP上面進行應用層心跳的方法其實沒有本質上的區別。而這就是為什麼沒有接觸過這類問題的人會有題主提出的疑惑。

那麼為什麼呢?最本質上UDP的優勢還是帶寬的利用。這一切要回歸到99~03年的網路狀況,當時網路的特點就是接入帶寬很窄而且抖動特別厲害。所謂抖動可能是多方面的,例如延時突發性地暴增、也有可能是由於路由層面的變化突然導致路由黑洞,還各種等等等等的問題。TCP因為擁塞控制、保證有序等原因,在這種網路狀態上對帶寬的利用是非常低的。而且因為網路抖動的原因,應用層心跳超時(一般不依靠keepalive)應用層主動斷掉socket之後TCP需要三次握手才能重新建立鏈接,一旦出現頻繁的小抖動就會使得帶寬利用更低。而等待四次揮手的時間,也會佔用伺服器上寶貴的資源。

總結來說,當網路差到一定程度了,TCP的優勢反而會成為劣勢。

這時候我們再看看UDP在這種情況下的表現。使用UDP對抗網路抖動,說到底就是在應用層比TCP更快地探測和重傳,一旦超過一定的時間沒有收到回復,客戶端可以選擇馬上重試或者換一個IP:PORT重試(假如你的服務像QQ一樣有多個接入),在伺服器端則可以果斷地斷掉socket。而可以應用UDP的時候,往往是你的應用層協議本身已經具備了一定的面向連接的特性。如果你應用層的協議已經達到了一定程度的消息冪等,客戶端可以幾乎無腦地進行重傳,這樣就可以儘可能地降低網路抖動的影響,同時也可以儘可能地利用整個帶寬。而剛好QQ的協議,就具備類似的特點。

簡單來說就是我們可以使用UDP實現一個面向連接協議,這個協議可以很好地適應當時的網路狀況和QQ本身的業務。但凡事都有成本,成本就是你的應用層協議本身需要去實現抵抗網路異常帶來的問題。例如亂序、例如業務數據的分片和重組、例如網路狀態探測等等等等。。。

而現在UDP也應用在很多跨運營商、跨地域、跨機房之間的服務調用當中。原因無它,就是網路爛到一定程度了。


即時通訊時效性要求高,用TCP維持多人同時在線是個問題[1], 涉及到伺服器數量,系統調優,編程手段等很多方面。記得QQ伺服器最初只有一台,還是Windows。我不會Windows開發,可以等哪位Window開發老手講下在Win 98的winsock介面下用什麼手段能開發出支持高並發的伺服器出來,難度多大。

即使是UDP協議,完全通過伺服器中轉、信息傳輸加密(用戶到伺服器之間的,= =)、保存聊天內容協助警方等都是錢多用戶傻之後的事了。次早期的QQ官方客戶端提供了默認不勾選的「通過伺服器中轉」選項,可見那時信息還是允許客戶端和客戶端之間直接發送的。

至於UDP穿牆之類的優勢,國內最常見的NAT共享網路又不會影響客戶端主動發起TCP鏈接,應該不是使用UDP的主要原因。

[1] http://en.wikipedia.org/wiki/C10k_problem


先問是不是,再問為什麼?
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協議發回一個應答包。如此來保證消息可以無遺漏傳輸。之所以會發生在客戶端明明看到「消息發送失敗」但對方又收到了這個消息的情況,就是因為客戶端發出的消息伺服器已經收到並轉發成功,但客戶端由於網路原因沒有收到伺服器的應答包引起的。


登陸採用TCP協議和HTTP協議,你和好友之間發送消息,主要採用UDP協議,內網傳文件採用了P2P技術。總來的說:
1.登陸過程,客戶端client 採用TCP協議向伺服器server發送信息,HTTP協議下載信息。登陸之後,會有一個TCP連接來保持在線狀態。
2.和好友發消息,客戶端client採用UDP協議,但是需要通過伺服器轉發。騰訊為了確保傳輸消息的可靠,採用上層協議來保證可靠傳輸。如果消息發送失敗,客戶端會提示消息發送失敗,並可重新發送。
3.如果是在內網裡面的兩個客戶端傳文件,QQ採用的是P2P技術,不需要伺服器中轉。


如果單就QQ用戶消息的傳遞而言:

最早年是UDP,而且是P2P的。
原因前面很多同學提了。
1)P2P的UDP穿透容易。這個應該是主要原因。
2)UDP成本低。
用過老QQ應該還記得很多時候有消息無法傳遞到對端的事情。
有人還記得當年的QQ可以查看對方的地點嗎。就是通過IP轉換查詢得到的。
當然使用UDP並不完美呀。安全問題就很麻煩。

現在,如果沒記錯後面都是TCP的了。而且已經都是C/S架構的了。
主要改進的原因還是安全吧。
改進的時間應該在06年左右。

當然其他很多東西的傳輸也還有走UDP的。

記錯勿怪。


首先,QQ並不是完全基於UDP實現。比如在使用QQ進行文件傳輸等活動的時候,就會使用TCP作為可靠傳輸的保證。
使用UDP進行交互通信的好處在於,延遲較短,對數據丟失的處理比較簡單。同時,TCP是一個全雙工協議,需要建立連接,所以網路開銷也會相對大。如果使用QQ語音和QQ視頻的話,UDP的優勢就更為突出了,首先延遲較小。最重要的一點是不可靠傳輸,這意味著如果數據丟失的話,不會有重傳。因為用戶一般來說可以接受圖像稍微模糊一點,聲音稍微不清晰一點,但是如果在幾秒鐘以後再出現之前丟失的畫面和聲音,這恐怕是很難接受的。


既然問題是騰訊QQ為什麼使用UDP而不使用TCP,給你分享一篇騰訊內部的關於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較易實現。


還有個原因,QQ 最初有幾種消息類型沒有數據長度這個 field,後來為了兼容性也沒法改了。


網路特別差的時候,tcp是不可靠的,這是我做過多年IM總結出來的經驗,為什麼會不可靠呢,因為你檢測網路掉線是需要時間的,剛好你把消息發出去時,網路其它是已經掉線了,而你卻還不知道,所以為了不掉消息,在應用層還要加一層消息生傳機制。這樣TCP就沒有什麼優勢了,底層有重傳機制,應用層還要自己做重傳。QQ當年選用UDP,是因為哪時電信和網通跨網互聯的網路挺別差,經常掉消息。剛開始做IM之類的,用TCP還是最簡單的方法,因為你要自己實現一個高效的重傳,排序機制很難,不是一般人能做到了,用Tcp就省事多了。


UDP才是最基礎的協議,對於QQ這樣級別的應用來說,TCP有諸多不便和限制,你可以認為QQ用的UDP其實是符合QQ的要求的一個升級版協議,根據自己的產品邏輯增加了很多TCP的特性比如丟包重發機制。
另外,QQ應該是優先採用UDP協議,如果不通的話會自動轉為TCP


UDP能穿大部分NAT啊,只需一個外網伺服器引導一下之後就不用管了,對於早期的騰訊來說,省下不少伺服器還是很划得來的。
當然現在鵝廠不差錢,像微信就不用UDP了,但QQ協議估計也沒人能大改了,畢竟那麼大的裝機量你也沒法全部升級到一個全新的不兼容協議去。


1、QQ前期資金不多,採用P2P的方式單台伺服器(聽說一開始用的是電信的小型機,後來自己買的也是小型機)能支持的用戶量比較大,聽說是單機支持了幾十萬的用戶量。
2、由於採用P2P的方式進行通信,伺服器只是匯總在線信息,而P2P要考慮兩個內網穿透的問題,那麼TCP就不合適了,所以選用UDP協議的原因是為了內網通信穿透的考慮。

例如:
A在某個內網,B在某個內網,兩者進行相連通信的話,只能用UDP實現了。
而MSN之類的非P2P的,就是通過A和B都連接到MSN的伺服器,再由MSN的伺服器進行中轉來實現。


真正的原因其實很殘酷。

那個時代的國內程序通信大部分是udp。原因是那個時代的vc的udp編程非常簡單,但tcp則需要理論支持。當時的程序員連基本的編程語言都掌握不好,更別提通信原理了。

-----------

呵呵,評論里的裝逼犯居然開答案來辱罵我:

那麼,你能解釋下,為什麼你會刪掉這條評論?

還【coe】,呵呵...我一開始還納悶了,好歹我也是柬埔寨MIT電腦技術專修學院畢業的,這辭彙怎麼沒見過?難道是近兩年的新出辭彙?然而查了谷歌、維基百科、計算機專業詞典都查不到,原來是【自造辭彙】啊,難怪我質疑幾句,就嚇得趕緊刪除評論,裝逼裝到連辭彙都敢自造了,臉皮可真是深不見底。


TCP是個爛協議。

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,殘暴控制協議):

Bcp - scala-bcp 0.1.0


歡迎使用!


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要做的事情,重傳、保證順序、保證唯一性,在應用層都有(且都不可避免地要有)另外的機制來解決。

相比之下,握手揮手、超時控制、連接的維持,卻是多餘的負擔。

直到今天,IM用TCP也不過是網路發達了,用得起而已。


我們Java老師說,QQ對於普通用戶是採用UDP協議,對於會員用TCP協議。


首先要搞明白是不是,然後問為什麼
現在的QQ後面開了很多埠,你可以在Mac上用lsof -i -n 查看,既有TCP也有UDP(win下不是有個360可以查看嘛)。
&<基於音頻、視頻、文件的通信基本上是UDP&>


推薦閱讀:

tcp協議可靠嗎? 怎麼知道自己發出的消息已經被是否被成功接收?
tcp上傳文件時的ack變化?
怎樣生動描述 TCP 的「三次握手」?
tcp協議握手為什要各隨機一個數字並加一?
在TCP里可以讓數個Application共享一個Port么?

TAG:騰訊QQ | UDP | Windows開發 | TCPIP | TCP |