我的demo地址:
https://github.com/caobo56/KCPDemo
什么是KCP郑藏?
kcp協(xié)議是傳輸層的一個具有可靠性的傳輸層ARQ協(xié)議。它的設(shè)計是為了解決在網(wǎng)絡(luò)擁堵情況下tcp協(xié)議的網(wǎng)絡(luò)速度慢的問題档冬。kcp力求在保證可靠性的情況下提高傳輸速度起趾。kcp協(xié)議的關(guān)注點主要在控制數(shù)據(jù)的可靠性和提高傳輸速度上面班巩,因此kcp沒有規(guī)定下層傳輸協(xié)議查近,一般用udp作為下層傳輸協(xié)議,kcp層協(xié)議的數(shù)據(jù)包在udp數(shù)據(jù)報文的基礎(chǔ)上增加控制頭语卤。當(dāng)用戶數(shù)據(jù)很大追逮,大于一個udp包能承擔(dān)的范圍時(大于mss),kcp會將用戶數(shù)據(jù)分片存儲在多個kcp包中粹舵。因此每個kcp包稱為一個分片钮孵。
為了提供可靠性,kcp采用了重傳機(jī)制眼滤。為實現(xiàn)重傳機(jī)制巴席,kcp為每個分片分配一個唯一標(biāo)識,接收方收到一個包后告知發(fā)送方接到的包的序號诅需,發(fā)送方接到確認(rèn)后再繼續(xù)發(fā)送漾唉。而如果發(fā)送方在一定時間內(nèi)(超時重傳時間)沒有接到確認(rèn),就說明數(shù)據(jù)包丟失了堰塌,發(fā)送方需要重傳丟失的數(shù)據(jù)包赵刑,所以發(fā)送方會把待確認(rèn)的數(shù)據(jù)緩存起來,方便重傳场刑。
個人理解:
個人理解:KCP 只是一種對UDP收發(fā)包的處理機(jī)制般此,并不具有通訊功能。需要額外的配置其底層的傳輸協(xié)議如UDP摇邦。另外恤煞,KCP是一種快速重傳的ARQ協(xié)議屎勘,因此施籍,需要使用KCP的節(jié)點,同時具有收發(fā)數(shù)據(jù)的功能概漱,以便KCP獲取到完整數(shù)據(jù)后發(fā)送接收完整的回調(diào)丑慎。
KCP的好處:就是比TCP浪費30%左右的帶寬,提升UDP的數(shù)據(jù)發(fā)送速度,并提升UDP的可靠性竿裂。
需要注意:
此外玉吁,需要說明的是,由于kcp需要在收到數(shù)據(jù)后腻异,給數(shù)據(jù)的發(fā)送方發(fā)送回執(zhí)消息进副。發(fā)送數(shù)據(jù)時,也要收取一個從服務(wù)端返回的發(fā)送成功回執(zhí)悔常。
所以影斑,一個正常運行的kcp-UDP節(jié)點,必然同時具備收取消息和發(fā)送消息的功能机打,因此矫户,我demo 里不再區(qū)分client 和 server 。
一個kcp節(jié)點残邀,必然既是客戶端皆辽,也是服務(wù)端。
kcp協(xié)議詳解
KCP C源代碼:
https://github.com/skywind3000/kcp
使用方法在KCPgithub 上寫的很清晰:
下面是我用OC 實現(xiàn)的簡單的KCP使用芥挣。
因為kcp協(xié)議的關(guān)注點主要在控制數(shù)據(jù)的可靠性和提高傳輸速度上面驱闷,所以,kcp沒有規(guī)定下層傳輸協(xié)議空免。只需要向KCP提供相應(yīng)的發(fā)送數(shù)據(jù)方法遗嗽,并實現(xiàn)獲取數(shù)據(jù)的回調(diào)方法即可。
因此鼓蜒,我選擇使用GCDAsyncUdpSocket 作為KCP 的下層UDP實現(xiàn):
實現(xiàn)代碼demo地址:
https://github.com/caobo56/KCPDemo
代碼結(jié)構(gòu):
另外需要注意的是痹换,項目中引入了ickp.h 和 ickp.c 兩個C文件,
因此在編譯的時候最好配置一下C的編譯設(shè)置:
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 并設(shè)置啟動時的端口
_netWork.delegate = self;
//設(shè)置代理都弹,接受數(shù)據(jù)
}
#pragma mark 發(fā)送消息
- (IBAction)sendMsgClick:(UIButton *)sender {
//向指定的 ip port 發(fā)送數(shù)據(jù)
[_netWork sendMsg:msgTF.text toHost:ipTF.text toPort:10099];
}
#pragma mark KcpOnUdp delegate
//接收數(shù)據(jù)
-(void)kcpOnUdpDidReciveMsg:(NSString *)str{
NSLog(@"kcpOnUdpDidReciveMsg = %@",str);
}
@end
此外娇豫,我用來跟客戶端/服務(wù)端 鏈接測試的例子是jkcp, https://github.com/beykery/jkcp
在開發(fā)調(diào)試過程中可以以此作為調(diào)試基準(zhǔn)進(jìn)行測試畅厢。
調(diào)試過程中注意客戶端服務(wù)端的kcp參數(shù)要保持一致冯痢,以下是我的配置:
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 端配置:
//設(shè)置KCP參數(shù),同服務(wù)端或者對點端參數(shù)保持一致
//特別是conv框杜,對方客戶端的conv 必須同自身服務(wù)端的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: 數(shù)字浦楣,123 這樣就可以,只需要保證 client.server 的數(shù)字是一樣就可以
user: 指針咪辱,給你自己傳遞參數(shù)用的振劳,你沒有特殊參數(shù)需要傳那就用 NULL