簡介
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有正常模式和快速模式兩種,通過以下策略達到提高流速的結果:
RTO翻倍vs不翻倍:
TCP超時計算是RTOx2,這樣連續丟三次包就變成RTOx8了,十分恐怖,而KCP啟動快速模式后不x2,只是x1.5(實驗證明1.5這個值相對比較好),提高了傳輸速度,
選擇性重傳 vs 全部重傳:
TCP丟包時會全部重傳從丟的那個包開始以后的資料,KCP是選擇性重傳,只重傳真正丟失的資料包,
快速重傳:
發送端發送了1,2,3,4,5幾個包,然后收到遠端的ACK: 1, 3, 4, 5,當收到ACK3時,KCP知道2被跳過1次,收到ACK4時,知道2被跳過了2次,此時可以認為2號丟失,不用等超時,直接重傳2號包,大大改善了丟包時的傳輸速度,
延遲ACK vs 非延遲ACK:
TCP為了充分利用帶寬,延遲發送ACK(NODELAY都沒用),這樣超時計算會算出較大 RTT時間,延長了丟包時的判斷程序,KCP的ACK是否延遲發送可以調節,
UNA vs ACK+UNA:
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)
- nodelay :是否啟用 nodelay模式,0不啟用;1啟用,
- interval :協議內部作業的 interval,單位毫秒,比如 10ms或者 20ms
- resend :快速重傳模式,默認0關閉,可以設定2(2次ACK跨越將會直接重傳)
- nc :是否關閉流控,默認是0代表不關閉,1代表關閉,
- 普通模式: ikcp_nodelay(kcp, 0, 40, 0, 0);
- 極速模式: ikcp_nodelay(kcp, 1, 10, 2, 1);
- 最大視窗:
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結合,那么可以繼續延伸閱讀:
- Wiki Home
- KCP 最佳實踐
- 同現有TCP服務器集成
- 傳輸資料加密
- 應用層流量控制
- 性能評測
開源案例
- kcptun: 基于 kcp-go做的高速遠程埠轉發(隧道) ,配合ssh -D,可以比 shadow.socks 更流暢的看在線視頻,
- dog-tunnel: GO開發的網路隧道,使用 KCP極大的改進了傳輸速度,并移植了一份 GO版本 KCP
- v2.ray: 著名代理軟體,Shadow.socks 代替者,1.17后集成了 kcp協議,使用UDP傳輸,無資料包特征,
- HP-Socket: 高性能網路通信框架 HP-Socket,
- frp: 高性能內網穿透的反向代理軟體,可將將內網服務暴露映射到外網服務器,
- asio-kcp: 使用 KCP的完整 UDP網路庫,完整實作了基于 UDP的鏈接狀態管理,會話控制,KCP協議調度等
- kcp-java: Java版本 KCP協議實作,
- kcp-netty: kcp的Java語言實作,基于netty,
- java-kcp: JAVA版本KCP,基于netty實作(包含fec功能)
- csharp-kcp: csharp版本KCP,基于dotNetty實作(包含fec功能)
- kcp-cpp: KCP 的多平臺(Windows、MacOS、Linux)C++ 實作作為應用程式中的簡單庫,包含適用于所有平臺的套接字處理和輔助函式,
- kcp-go: 高安全性的kcp的 GO語言實作,包含 UDP會話管理的簡單實作,可以作為后續開發的基礎庫,
- kcp-csharp: kcp的 csharp移植,同時包含一份回話管理,可以連接上面kcp-go的服務端,
- kcp-csharp: 新版本 Kcp的 csharp移植,執行緒安全,運行時無alloc,對gc無壓力,
- kcp2k: Line-by-line translation to C#, with optional Server/Client on top.
- kcp-rs: KCP的 rust移植
- kcp-rust:新版本 KCP的 rust 移植
- tokio-kcp:rust tokio 的 kcp 集成
- lua-kcp: KCP的 Lua擴展,用于 Lua服務器
- node-kcp: node-js 的 KCP 介面
- nysocks: 基于libuv實作的node-addon,提供nodejs版本的代理服務,客戶端接入支持SOCKS5和ss兩種協議
- shadow.socks-android: Shadow.socks for android 集成了 kcptun 使用 kcp協議加速 shadow.socks,效果不錯
- kcpuv: 使用 libuv開發的kcpuv庫,目前還在 Demo階段
- Lantern:更好的 VPN,Github 50000 星,使用 kcpgo 加速
- rpcx :RPC 框架,1000+ 星,使用 kcpgo 加速 RPC
- xkcptun: c語言實作的kcptun,主要用于OpenWrt, LEDE開發的路由器專案上
- et-frame: C#前后端框架(前端unity3d),統一用C#開發游戲,實作了前后端kcp協議
- yasio: 一個跨平臺專注于任意客戶端程式的異步socket庫, 易于使用,相同的API操作KCP/TCP/UDP, 性能測驗結果: benchmark-pump.
- gouxp: 用Go實作基于回呼方式的KCP開發包,包含加解密和FEC支持,簡單易用,
商業案例
- 明日帝國:Game K17 的 《明日帝國》 (Google Play),使用 KCP 加速游戲訊息,讓全球玩家流暢聯網
- 仙靈大作戰:4399 的 MOBA游戲,使用 KCP 優化游戲同步
- CC:網易 CC 使用 kcp 加速視頻推流,有效提高流暢性
- BOBO:網易 BOBO 使用 kcp 加速主播推流
- 云帆加速:使用 KCP 加速檔案傳輸和視頻推流,優化了臺灣主播推流的流暢度
- SpatialOS: 大型多人分布式游戲服務端引擎,BigWorld 的后繼者,使用 KCP 加速資料傳輸,
歡迎告知更多案例
協議比較
如果網路永遠不卡,那 KCP/TCP 表現類似,但是網路本身就是不可靠的,丟包和抖動無法避免(否則還要各種可靠協議干嘛),在內網這種幾乎理想的環境里直接比較,大家都差不多,但是放到公網上,放到3G/4G網路情況下,或者使用內網丟包模擬,差距就很明顯了,公網在高峰期有平均接近10%的丟包,wifi/3g/4g下更糟糕,這些都會讓傳輸變卡,
感謝 asio-kcp 的作者 zhangyuan 對 KCP 與 enet, udt做過的一次橫向評測,結論如下:
- ASIO-KCP has good performace in wifi and phone network(3G, 4G).
- The kcp is the first choice for realtime pvp game.
- The lag is less than 1 second when network lag happen. 3 times better than enet when lag happen.
- The enet is a good choice if your game allow 2 second lag.
- UDT is a bad idea. It always sink into badly situation of more than serval seconds lag. And the recovery is not expected.
- enet has the problem of lack of doc. And it has lots of functions that you may intrest.
- kcp's doc is chinese. Good thing is the function detail which is writen in code is english. And you can use asio_kcp which is a good wrap.
- The kcp is a simple thing. You will write more code if you want more feature.
- UDT has a perfect doc. UDT may has more bug than others as I feeling.
具體見:橫向比較 和 評測資料,為猶豫選擇的人提供了更多指引,
大型多人游戲服務端引擎 SpatialOS 在集成 KCP 協議后做了同 TCP/RakNet 的評測:
對比了在服務端重繪率為 60 Hz 同時維護 50 個角色時的回應時間,詳細對比報告見:
- Kcp a new low latency secure network stack
關于協議
近年來,網路游戲和各類社交網路都在成幾何倍數的增長,不管網路游戲還是各類互動社交網路,互動性和復雜度都在迅速提高,都需要在極短的時間內將資料同時投遞給大量用戶,因此傳輸技術自然變為未來制約發展的一個重要因素,而開源界里各種著名的傳輸協議,如 raknet/enet 之類,一發布都是整套協議堆疊一起發布,這種形式是不利于多樣化的,我的專案只能選擇用或者不用你,很難選擇 “部分用你”,然而你一套協議堆疊設計的再好,是非常難以滿足不同角度的各種需求的,
因此 KCP 的方式是把協議堆疊 “拆開”,讓大家可以根據專案需求進行靈活的調整和組裝,你可以下面加一層 reed solomon 的糾刪碼做 FEC,上面加一層類 RC4/Salsa20 做流加密,握手處再設計一套非對稱密鑰交換,底層 UDP 傳輸層再做一套動態路由系統,同時探測多條路徑,選最好路徑進行傳輸,這些不同的 “協議單元” 可以像搭建積木一般根據需要自由組合,保證 “簡單性” 和 “可拆分性”,這樣才能靈活適配多變的業務需求,哪個模塊不好,換了就是,
未來傳輸方面的解決方案必然是根據使用場景深度定制的,因此給大家一個可以自由組合的 “協議單元” ,方便大家集成在自己的協議堆疊中,
For more information, please see the Success Stories.
關于作者
作者:林偉 (skywind3000)
歡迎關注我的:twitter 和 zhihu,
我在多年的開發經歷中,一直都喜歡研究解決程式中的一些瓶頸問題,早年喜歡游戲開發,照著《VGA編程》來做游戲圖形,讀 Michael Abrash 的《圖形程式開發人員指南》做軟渲染器,愛好擺弄一些能夠榨干 CPU 能夠運行更快的代碼,參加作業后,興趣轉移到服務端和網路相關的技術,
2007 年時做了幾個傳統游戲后開始研究快速動作游戲的同步問題,期間寫過不少文章,算是國內比較早研究同步問題的人,然而發現不管怎么解決同步都需要在網路傳輸方面有所突破,后來離開游戲轉行互聯網后也發現不少領域有這方面的需求,于是開始花時間在網路傳輸這個領域上,嘗試基于 UDP 實作一些保守的可靠協議,仿照 BSD Lite 4.4 的代碼實作一些類 TCP 協議,覺得比較有意思,又接著實作一些 P2P 和動態路由網相關的玩具,KCP 協議誕生于 2011 年,基本算是自己傳輸方面做的幾個玩具中的一個,
Kcptun 的作者 xtaci 是我的大學同學,我倆都是學通信的,經常在一起研究如何進行傳輸優化,
-
本文來自博客園,作者:古道輕風,轉載請注明原文鏈接:https://www.cnblogs.com/88223100/p/kcp.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/404271.html
標籤:其他
