README in English
KCP是快速可靠協議,能以比TCP 浪費10%-20% 的頻寬的代價,換取平均延遲降低30%-40%,且最大延遲降低三倍的傳輸效果。純演算法實現,並不負責底層協定(如UDP)的收發,需要使用者自己定義下層資料包的發送方式,以callback的方式提供給KCP。 連時鐘都需要外部傳遞進來,內部不會有任何一次系統呼叫。
整個協定只有ikcp.h, ikcp.c兩個來源文件,可以方便的整合到使用者自己的協定棧中。也許你實現了一個P2P,或者某個基於UDP的協議,而缺乏一套完善的ARQ可靠協議實現,那麼簡單的拷貝這兩個文件到現有項目中,稍微編寫兩行代碼,即可使用。
TCP是為流量設計的(每秒內可以傳輸多少KB的資料),講究的是充分利用頻寬。而KCP是為流速設計的(單一資料包從一端發送到一端需要多少時間),以10%-20%頻寬浪費的代價換取了比TCP快30%-40%的傳輸速度。 TCP頻道是一條流速很慢,但每秒流量很大的大運河,而KCP是水流湍急的小激流。 KCP有正常模式和快速模式兩種,透過以下策略達到提高流速的結果:
TCP超時運算是RTOx2,這樣連續丟三次包就變成RTOx8了,十分恐怖,而KCP啟動快速模式後不x2,只是x1.5(實驗證明1.5這個值相對比較好),提高了傳輸速度。
TCP丟包時會全部重傳從丟的那個包開始以後的數據,KCP是選擇性重傳,只重傳真正遺失的資料包。
發送端發送了1,2,3,4,5幾個包,然後收到遠端的ACK: 1, 3, 4, 5,當收到ACK3時,KCP知道2被跳過1次,收到ACK4時,知道2被跳過了2次,此時可以認為2號遺失,不用等超時,直接重傳2號包,大大改善了丟包時的傳輸速度。
TCP為了充分利用頻寬,延遲發送ACK(NODELAY都沒用),這樣逾時運算會算出較大RTT時間,延長了丟包時的判斷流程。 KCP的ACK是否延遲發送可以調整。
ARQ模型反應有兩種,UNA(此編號前所有包已收到,如TCP)和ACK(此編號包已收到),光用UNA將導致全部重傳,光用ACK則丟失成本太高,以往協定都是二選其一,而KCP協定中,除去單獨的ACK包外,所有包都有UNA資訊。
KCP正常模式同TCP一樣使用公平退讓法則,即發送視窗大小由:發送快取大小、接收端剩餘接收快取大小、丟包退讓及慢啟動這四要素決定。但傳送及時性要求很高的小數據時,可選擇透過設定跳過後兩步,僅用前兩項來控制發送頻率。以犧牲部分公平性及頻寬利用率之代價,換取了開啟BT都能流暢傳輸的效果。
您可以使用vcpkg庫管理器下載並安裝kcp:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install kcp
vcpkg中的kcp函式庫由Microsoft團隊成員和社群貢獻者保持最新狀態。如果版本過時,請在vcpkg儲存庫上建立issue或提出PR。
建立KCP物件:
// 初始化 kcp对象,conv为一个表示会话编号的整数,和tcp的 conv一样,通信双
// 方需保证 conv相同,相互的数据包才能够被认可,user是一个给回调函数的指针
ikcpcb *kcp = ikcp_create(conv, user);
設定回調函數:
// KCP的下层协议输出函数,KCP需要发送数据时会调用它
// buf/len 表示缓存和长度
// user指针为 kcp对象创建时传入的值,用于区别多个 KCP对象
int udp_output ( const char *buf, int len, ikcpcb *kcp, void *user)
{
....
}
// 设置回调函数
kcp->output = udp_output;
循環調用update:
// 以一定频率调用 ikcp_update来更新 kcp状态,并且传入当前时钟(毫秒单位)
// 如 10ms调用一次,或用 ikcp_check确定下次调用 update的时间不必每次调用
ikcp_update (kcp, millisec);
輸入一個下層資料包:
// 收到一个下层数据包(比如UDP包)时需要调用:
ikcp_input (kcp, received_udp_packet, received_udp_size);
處理了下層協定的輸出/輸入後KCP協定就可以正常運作了,使用ikcp_send 來向遠端發送資料。而另一端則使用ikcp_recv(kcp, ptr, size)來接收資料。
協定預設模式是一個標準的ARQ,需要透過配置開啟各項加速開關:
工作模式:
int ikcp_nodelay (ikcpcb *kcp, int nodelay, int interval, int resend, int nc)
最大視窗:
int ikcp_wndsize (ikcpcb *kcp, int sndwnd, int rcvwnd);
這個呼叫將會設定協定的最大發送視窗和最大接收視窗大小,預設為32. 這個可以理解為TCP的SND_BUF 和RCV_BUF,只不過單位不一樣SND/RCV_BUF 單位是位元組,這個單位是包。
最大傳輸單元:
純演算法協定並不負責探測MTU,預設mtu是1400字節,可以使用ikcp_setmtu來設定該值。該值將會影響資料包歸併及分片時候的最大傳輸單元。
最小RTO:
不管是TCP還是KCP計算RTO時都有最小RTO的限制,即便計算出來RTO為40ms,由於預設的RTO是100ms,協定只有在100ms後才能偵測到丟包,快速模式下為30ms,可以手動更改該值:
kcp->rx_minrto = 10 ;
協定的使用和配置都是很簡單的,大部分情況看完上面的內容基本上都可以使用了。如果你需要進一步進行精細的控制,例如改變KCP的記憶體分配器,或者你需要更有效的大規模調度KCP連結(例如3500個以上),或者如何更好的同TCP結合,那麼可以繼續延伸閱讀:
相關閱讀:《原神》也正在使用KCP 加速遊戲訊息
KCP 成功的運作在多個用戶規模上億的專案上,為他們提供了更靈敏和絲滑網路體驗。
歡迎告知更多案例
如果網路永遠不卡,那麼KCP/TCP 表現類似,但是網路本身就是不可靠的,丟包和抖動無法避免(否則還要各種可靠協定幹嘛)。在內網這種幾乎理想的環境裡直接比較,大家都差不多,但是放到公網上,放到3G/4G網絡情況下,或者使用內網丟包模擬,差距就很明顯了。公網在高峰期有平均接近10%的丟包,wifi/3g/4g下更糟糕,這些都會讓傳輸變卡。
感謝asio-kcp 的作者zhangyuan 對KCP 與enet, udt做過的一次橫向評測,結論如下:
具體見:橫向比較和評測數據,為猶豫選擇的人提供了更多指引。
大型多人遊戲服務端引擎SpatialOS 在整合KCP 協定後做了同TCP/RakNet 的評測:
比較了在服務端刷新率為60 Hz 同時維護50 個角色時的回應時間,詳細比較報告請見:
近年來,網路遊戲和各類社交網絡都在成幾何倍數的增長,不管網絡遊戲還是各類互動社交網絡,交互性和複雜度都在迅速提高,都需要在極短的時間內將數據同時投遞給大量用戶,因此傳輸技術自然變成未來限制發展的重要因素,而開源界裡各種著名的傳輸協議,如raknet/enet之類,一發布都是整套協議棧一起發布,這種形式是不利於多樣化的,我的專案只能選擇用或不用你,很難選擇“部分用你”,然而你一套協議棧設計的再好,是非常難以滿足不同角度的各種需求的。
因此KCP 的方式是把協議棧“拆開”,讓大家可以根據項目需求進行靈活的調整和組裝,你可以下面加一層reed solomon 的糾刪碼做FEC,上面加一層類RC4/Salsa20 做流加密,握手處再設計一套非對稱金鑰交換,底層UDP 傳輸層再做一套動態路由系統,同時探測多條路徑,選最好路徑進行傳輸。這些不同的“協議單元” 可以像搭建積木一般根據需要自由組合,保證“簡單性” 和“可拆分性”,這樣才能靈活適配多變的業務需求,哪個模組不好,換了就是。
未來傳輸方面的解決方案必然是根據使用場景深度定制的,因此給大家一個可以自由組合的“協議單元” ,方便大家集成在自己的協議棧中。
For more information, please see the Success Stories.
作者:林偉(skywind3000)
歡迎關注我的:個人部落格和推特。
我在多年的開發經驗中,一直都喜歡研究解決程式中的一些瓶頸問題,早年喜歡遊戲開發,照著《VGA程式設計》來做遊戲圖形,讀Michael Abrash 的《圖形程式開發人員指南》做軟渲染器,愛好擺弄一些能夠榨乾CPU 能夠運行更快的程式碼,參加工作後,興趣轉移到服務端和網路相關的技術。
2007 年時做了幾個傳統遊戲後開始研究快速動作遊戲的同步問題,期間寫過不少文章,算是國內比較早研究同步問題的人,然而發現不管怎麼解決同步都需要在網絡傳輸方面有所突破,後來離開遊戲轉行互聯網後也發現不少領域有這方面的需求,於是開始花時間在網絡傳輸這個領域上,嘗試基於UDP 實現一些保守的可靠協議,仿照BSD Lite 4.4 的代碼實現一些類TCP協議,覺得比較有趣,又接著實現一些P2P 和動態路由網相關的玩具。 KCP 協議誕生於2011 年,基本上算是自己傳輸方面做的幾個玩具中的一個。
Kcptun 的作者xtaci 是我的大學同學,我兩個都是學通信的,經常在一起研究如何進行傳輸優化。
歡迎使用支付寶手掃描上面的二維碼,對該項目進行捐贈。捐贈款項將用於持續優化KCP協議以及完善文件。
感謝:明明、星仔、進、帆、頒釵、斌銨、曉丹、餘爭、虎、晟敢、徐瑋、王川、趙剛強、胡知鋒、萬新朝、何新超、劉暘、侯憲輝、吳佩儀、華斌、如濤、胡堅。 。 。 (早先的名單實在不好意思沒記錄下來)等同學的捐款與支持。
歡迎關注
KCP交流群:364933586(QQ群號),KCP集成,調優,網路傳輸以及相關技術討論
Gitter 群:https://gitter.im/skywind3000/KCP
blog: http://www.skywind.me
This project exists thanks to all the people who contribute.