iOS基礎通訊UDP/KCP協(xié)議的使用

我的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 上寫的很清晰:

屏幕快照 2018-02-24 上午10.49.48.png
屏幕快照 2018-02-24 上午10.50.00.png

下面是我用OC 實現的簡單的KCP使用。
因為kcp協(xié)議的關注點主要在控制數據的可靠性和提高傳輸速度上面,所以,kcp沒有規(guī)定下層傳輸協(xié)議。只需要向KCP提供相應的發(fā)送數據方法,并實現獲取數據的回調方法即可。

因此,我選擇使用GCDAsyncUdpSocket 作為KCP 的下層UDP實現:
實現代碼demo地址:
https://github.com/caobo56/KCPDemo

代碼結構:


屏幕快照 2018-02-24 上午11.05.18.png

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

TARGET -> BuildSetting -> C Language Dialect
屏幕快照 2018-02-24 上午11.07.55.png

最終的使用方式:

//
//  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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容