最近公司要求針對上傳下載提速,打算採用kcp協議進行底層改造,於是對kcp進行了一些調研。
KCP是一個快速可靠協議,能以比 TCP浪費10%-20%的頻寬的代價,換取平均延遲降低 30%-40%,且最大延遲降低三倍的傳輸效果。純演算法實現,並不負責底層協議(如UDP)的收發,需要使用者自己定義下層資料包的傳送方式,以 callback的方式提供給 KCP。 連時鐘都需要外部傳遞進來,內部不會有任何一次系統呼叫。
以上片段來自skywind3000/kcp 對kcp的介紹。而這裡對kcp也不再展開,kcp的具體介紹還是大家自行搜尋理解比較好。
而目前比較成熟完備的解決方案是kcptun,目前在Github上有9.8K star。 kcptun在kcp協議的基礎上,做了很多優化,包括向前就錯(FEC),差異化服務或(DSCP),加解密,壓縮。 接下來,我將會以Android開發者的角度講述kcptun移植到Android端的過程,預設讀者已經配置好Android開發環境,因此不再贅述。
第一步 Go環境準備
kcptun本身是個Go專案,而Go專案本身是支援移植到Android或iOS客戶端作為第三方庫使用的。像我一樣的Go小白可以先看一下這篇文章:使用 Go 進行 iOS 和 Android 程式設計 已翻譯 100%
1.Go語言環境安裝
請參考:菜鳥教程,記得將go配置到環境變數中方便使用。
2.GoMobile工具安裝
GoMobile工具用於編譯和執行 Android 和 iOS 的應用。 注意⚠️ 該工具下載可能需要科學上網,包括編譯kcptun過程一些依賴庫也是需要科學上網的。如果沒有梯子建議使用國內映象。請參考:國內的go get問題的解決
第二步 Go專案編譯
在真正編譯kcptun之前,可以使用命令列先編譯一下最簡單的 hello
專案來確認環境已經全部安裝好。
筆者go的安裝目錄為 /usr/local/go
,在安裝好gomobile之後,在/usr/local/go/src/golang.org/x/mobile/example/
目錄下會有幾個例子工程,現在對example下的bind專案開始編譯。進入到/usr/local/go/src/golang.org/x/mobile/example/bind/hello
目錄下編譯hello.go
檔案。
使用gomobile bind -target=android
即可,編譯成功會在hello.go所在目錄生成兩個檔案。這兩個檔案我們就可以匯入到Android工程中使用
下面開始真正編譯kcptun。
我們客戶端所需要的只是kcptun的client部分。現在kcptun,將kcptun下的client資料夾挪到/usr/local/go/src/golang.org/x/mobile/example/
目錄下,我這裡將其重新命名為了kcp-client。在開始編譯之前我們需要對go原始碼進行一點小修改才能順利進行。
修改kcp-client下的三個檔案,將程式碼中的package main
改成別的包名,我這裡是改成了`package kcpclient,如圖:
這是因為gomobile編譯的go檔案不允許使用package main
。這三個檔案修改完成後還需對main.go
這最後一點修改:
- 將main方法改成名成:Run,main方法是啟動kcp client的唯一入口,將其改名成大寫字母開頭的方法才會暴露出來給Android端使用,否則是無法在Android端啟動kcp client的。
- 在main.go預定義程式碼中加入一個配置路徑屬性CONFIGPATH,修改Run方法中程式碼片段,將其改為如下:
if c.String("c") != "" {
err := parseJSONConfig(&config, c.String("c"))
checkError(err)
}else if CONFIGPATH != "" {
err := parseJSONConfig(&config, CONFIGPATH)
checkError(err)
}
複製程式碼
這裡加入了CONFIGPATH屬性,並且暴露給客戶端,可以在啟動kcp client前通過setCONFIGPATH
方法,傳入配置的json檔案路徑,對kcp client進行自定義引數配置。具體完整的main.go見Github。
上面都準備好後開始真正編譯kcp client。
進入到kcp-client 所在目錄,執行gomobile bind -target=android
命令,如果一切順利會在kcp-client 目錄下生成兩個編譯產物,如下圖,kcpclient.aar以及kcpclient-sources.jar。
ps.如果編譯過程中報錯缺少庫,99%都是因為牆的原因,自行翻牆或者使用國內映象下載。
移植Android端使用
加入kcp依賴
將kcpclient.aar和kcpclient-sources.jar一直到安卓工程中的libs目錄下,修改模組下的build.gradle檔案以保證kcpclient.aar和kcpclient-sources.jar兩個庫能真正引用。
android {
...
repositories {
flatDir {
dirs 'libs', '../libs'
}
}
}
dependencies {
implementation fileTree(include: '*.jar', dir: 'libs')
implementation(name: 'kcpclient', ext: 'aar')
}
複製程式碼
啟動kcp client
啟動kcp client是個網路操作,所以要放到子執行緒進行操作。最簡單的做法是如下:
new Thread() {
@Override
public void run() {
super.run();
//這裡設定自己的kcp client配置,也可不呼叫,使用預設配置
Kcpclient.setCONFIGPATH("xxxx");
Kcpclient.run();
}
}.start();
複製程式碼
設定應用層代理
從上面的kcptun原理可以看出,應用層資料先是使用TCP協議封裝,經過kcp client則轉換成了kcp協議。這裡面的轉換是通過本地代理完成的。這也是kcptun的優勢之一,無需改動原有的應用層實現,只需通過本地代理即可實現tcp->kcp的轉換。這裡以OkhttpClient為例,設定socks代理。
final String hostName = "127.0.0.1";
//kcp client config中設定的localaddr
final int port = 12948;
OkHttpClient.Builder builder = new OkHttpClient.Builder();
Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(hostName, port));
builder.proxy(proxy);
mOkHttpClient = builder.build();
複製程式碼
通過該方式建立OkHttpClient之後,請求會使用kcp協議進行請求了。
測試
為了測試kcptun移植後的可用性,需要先搭建一個kcptun server。kcptun server可以直接下載Github 上面的release版,根據對應的系統下載對應的版本。
我這裡使用的是mac,下載了kcptun-darwin-amd64。
下載完成後解壓,直接將server_darwin_amd64拖動命令後即可啟動kcp伺服器。如果需要自定義配置,使用命令server_darwin_amd64 -c 配置路徑
即可。
其次,不論kcp client是自定義配置還是預設配置,kcp client的remoteaddr需要改成你電腦本機的IP,方便測試。
啟動伺服器後,還需要給伺服器設定socks 代理,保證請求能通。這裡以Charles為例。
經過以上準備,kcptun就可以正常使用了。以前的請求方式不用做修改,只需保證三點即可:
- 啟動kcp client&server成功,並且保證兩端配置對應,有問題請參考預設配置以及kcptun github的引數說明,記得kcp client的remoteaddr需要修改成你server端所在的ip。
- 設定本地代理
- 電腦需要設定socks代理,保證測試成功。
通過kcptun代理,每次請求都會有如下日誌顯示,並且在chales可以看到請求的執行情況。
總結
在經過上傳下載測試發現,在網路良好的條件下,使用kcp和不使用kcp,速度相差無幾,但是在網路不穩定時,通過kcp協議請求比不使用kcp會有較大的速度提升。但是使用kcp協議耗費流量增加非常明顯,流量增加可能會比不使用kcp增加30%~50%,這也是kcp協議本身決定的,這個無法避免,所以是否使用kcp這本身也是個平衡選擇,想要更快且適應不穩定的網路環境,kcp就是個比較好的選擇,例如遊戲,直播。 說了這麼多,對於kcp我自己的理解也十分有限,所以如有不足之處還請諒解指點。