我的demo地址:
https://github.com/caobo56/KCPDemo
什么是KCP?
kcp協(xié)議是傳輸層的一個具有可靠性的傳輸層ARQ協(xié)議。它的設計是為了解決在網絡擁堵情況下tcp協(xié)議的網絡速度慢的問題。kcp力求在保證可靠性的情況下提高傳輸速度。kcp協(xié)議的關注點主要在控制數據的可靠性和提高傳輸速度上面,因此kcp沒有規(guī)定下層傳輸協(xié)議,一般用udp作為下層傳輸協(xié)議,kcp層協(xié)議的數據包在udp數據報文的基礎上增加控制頭。當用戶數據很大,大于一個udp包能承擔的范圍時(大于mss),kcp會將用戶數據分片存儲在多個kcp包中。因此每個kcp包稱為一個分片。
為了提供可靠性,kcp采用了重傳機制。為實現重傳機制,kcp為每個分片分配一個唯一標識,接收方收到一個包后告知發(fā)送方接到的包的序號,發(fā)送方接到確認后再繼續(xù)發(fā)送。而如果發(fā)送方在一定時間內(超時重傳時間)沒有接到確認,就說明數據包丟失了,發(fā)送方需要重傳丟失的數據包,所以發(fā)送方會把待確認的數據緩存起來,方便重傳。
個人理解:
個人理解:KCP 只是一種對UDP收發(fā)包的處理機制,并不具有通訊功能。需要額外的配置其底層的傳輸協(xié)議如UDP。另外,KCP是一種快速重傳的ARQ協(xié)議,因此,需要使用KCP的節(jié)點,同時具有收發(fā)數據的功能,以便KCP獲取到完整數據后發(fā)送接收完整的回調。
KCP的好處:就是比TCP浪費30%左右的帶寬,提升UDP的數據發(fā)送速度,并提升UDP的可靠性。
需要注意:
此外,需要說明的是,由于kcp需要在收到數據后,給數據的發(fā)送方發(fā)送回執(zhí)消息。發(fā)送數據時,也要收取一個從服務端返回的發(fā)送成功回執(zhí)。
所以,一個正常運行的kcp-UDP節(jié)點,必然同時具備收取消息和發(fā)送消息的功能,因此,我demo 里不再區(qū)分client 和 server 。
一個kcp節(jié)點,必然既是客戶端,也是服務端。
kcp協(xié)議詳解
KCP C源代碼:
https://github.com/skywind3000/kcp
使用方法在KCPgithub 上寫的很清晰:


下面是我用OC 實現的簡單的KCP使用。
因為kcp協(xié)議的關注點主要在控制數據的可靠性和提高傳輸速度上面,所以,kcp沒有規(guī)定下層傳輸協(xié)議。只需要向KCP提供相應的發(fā)送數據方法,并實現獲取數據的回調方法即可。
因此,我選擇使用GCDAsyncUdpSocket 作為KCP 的下層UDP實現:
實現代碼demo地址:
https://github.com/caobo56/KCPDemo
代碼結構:

另外需要注意的是,項目中引入了ickp.h 和 ickp.c 兩個C文件,
因此在編譯的時候最好配置一下C的編譯設置:
TARGET -> BuildSetting -> C Language Dialect

最終的使用方式:
//
// SendViewController.m
// textDemo
//
// Created by caobo56 on 18/2/22.
// Copyright ? 2018年 caobo56. All rights reserved.
//
#import "SendViewController.h"
#import "KcpOnUdp.h"
/**
* 客戶端
*/
@interface SendViewController ()<KcpOnUdpDelegate>
{
__weak IBOutlet UITextField *msgTF;
__weak IBOutlet UITextField *ipTF;
__weak IBOutlet UILabel *receiveLab;
}
@property (nonatomic,strong)KcpOnUdp * netWork;
@end
@implementation SendViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = @"KcpOnUdp節(jié)點";
_netWork = [KcpOnUdp creatKcpOnUdpWithPort:10099];
//啟動KcpOnUdp 并設置啟動時的端口
_netWork.delegate = self;
//設置代理,接受數據
}
#pragma mark 發(fā)送消息
- (IBAction)sendMsgClick:(UIButton *)sender {
//向指定的 ip port 發(fā)送數據
[_netWork sendMsg:msgTF.text toHost:ipTF.text toPort:10099];
}
#pragma mark KcpOnUdp delegate
//接收數據
-(void)kcpOnUdpDidReciveMsg:(NSString *)str{
NSLog(@"kcpOnUdpDidReciveMsg = %@",str);
}
@end
此外,我用來跟客戶端/服務端 鏈接測試的例子是jkcp, https://github.com/beykery/jkcp
在開發(fā)調試過程中可以以此作為調試基準進行測試。
調試過程中注意客戶端服務端的kcp參數要保持一致,以下是我的配置:
server 配置:
TestServer s = new TestServer(10099, 1);
s.noDelay(1, 10, 2, 1);
s.setMinRto(10);
s.wndSize(128, 128);
s.setTimeout(10 * 1000);
s.setMtu(512);
s.start();
clinet 配置:
TestClient tc = new TestClient();
tc.noDelay(1, 10, 2, 1);
tc.setMinRto(10);
tc.wndSize(128, 128);
tc.setTimeout(10 * 1000);
tc.setMtu(512);
tc.setConv(121106);
iOS 端配置:
//設置KCP參數,同服務端或者對點端參數保持一致
//特別是conv,對方客戶端的conv 必須同自身服務端的conv一致
int32_t conv = 121106;
c_kcp = ikcp_create(conv, NULL);
ikcp_nodelay(c_kcp, 1, 10, 2, 1);
c_kcp->rx_minrto = 10;
ikcp_wndsize(c_kcp, 128, 128);
ikcp_setmtu(c_kcp, 512);
配置說明:
conv: 數字,123 這樣就可以,只需要保證 client.server 的數字是一樣就可以
user: 指針,給你自己傳遞參數用的,你沒有特殊參數需要傳那就用 NULL