Android上kcp协议使用初探

4,923 阅读6分钟

最近公司要求针对上传下载提速,打算采用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开发环境,因此不再赘述。

kcptun原理

第一步 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,如图:

enter description here

这是因为gomobile编译的go文件不允许使用package main。这三个文件修改完成后还需对main.go这最后一点修改:

  • main方法改成名成:Runmain方法是启动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.aarkcpclient-sources.jar一直到安卓工程中的libs目录下,修改模块下的build.gradle文件以保证kcpclient.aarkcpclient-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为例。

charles socks代理设置

经过以上准备,kcptun就可以正常使用了。以前的请求方式不用做修改,只需保证三点即可:

  • 启动kcp client&server成功,并且保证两端配置对应,有问题请参考默认配置以及kcptun github的参数说明,记得kcp client的remoteaddr需要修改成你server端所在的ip。
  • 设置本地代理
  • 电脑需要设置socks代理,保证测试成功。

kcp client启动成功如图

通过kcptun代理,每次请求都会有如下日志显示,并且在chales可以看到请求的执行情况。

enter description here

总结

在经过上传下载测试发现,在网络良好的条件下,使用kcp和不使用kcp,速度相差无几,但是在网络不稳定时,通过kcp协议请求比不使用kcp会有较大的速度提升。但是使用kcp协议耗费流量增加非常明显,流量增加可能会比不使用kcp增加30%~50%,这也是kcp协议本身决定的,这个无法避免,所以是否使用kcp这本身也是个平衡选择,想要更快且适应不稳定的网络环境,kcp就是个比较好的选择,例如游戏,直播。 说了这么多,对于kcp我自己的理解也十分有限,所以如有不足之处还请谅解指点。