KCPTUN

https://github.com/xtaci/kcptun

option

kcptun 的参数都源自于 kcp 原始协议:https://github.com/skywind3000/kcp

工作模式:

int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc)
参数 作用
nodelay 是否启用 nodelay 模式,0 不启用;1 启用
interval 协议内部工作的 interval 单位 毫秒,比如 10ms 或者 20ms
resend 快速重传模式,默认 0 关闭,可以设置 2 ( 2 次 ACK 跨越将会直接重传 )
nc 是否关闭流控,默认是 0 代表不关闭 1 代表关闭
normal 普通模式:ikcp_nodelay(kcp, 0, 40, 0, 0)
fast 极速模式:ikcp_nodelay(kcp, 1, 10, 2, 1)

最大窗口:

int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);

该调用将会设置协议的最大发送窗口和最大接收窗口大小,默认为 32 这个可以理解为 TCP 的 SND_BUFRCV_BUF 只不过单位不一样 SND/RCV_BUF 单位是 字节,这个单位是

img_kcptun

服务端与客户端 必须一致 的参数 含义
--crypt 加密算法
--key 密钥
--nocomp 压缩数据
--datashard 前向纠错
--parityshard 前向纠错

其余参数两边可独立设定

https://github.com/xtaci/kcptun/issues/342

--nodelay=0 的多等一下,具体是多等多久呢,多等 1/8 RTT

nodelay 是控制超时重传的时候是否再多等一会。 inteval 是超时重传的间隔,和下面的 resend 控制的快速重传没有关系 resend 只是控制快速重传,超过 resend 间隔没收到的包会立刻重传,无需等到 inteval 到了再发送 试下来 0 300 0 1的参数,RepeatSegs明显降低 如果 interval 和 repeat 有关,那么 fastresend 是最大的嫌疑,因为就这个不受时间控制

nodelay 这个参数俺一直都是关闭的,感觉太激进,时常突破窗口设置阈值 interval 估计和设计初衷有关,(流速、流量),另一个帖子网友也提过这个问题,就是过大的interval会造成传输曲线不稳,这在routeros里看的也很清楚,例如20M的阈值,那么可能5M、10M、20M这样跳,而小的interval则一直压着阈值上限走,感觉像是单次的传输VS多次传输

FEC 前向纠错

两端同时设定 -datashard n-parityshard m 参数搭配:

  1. fast + FEC(5,5)
  2. fast2 + FEC(10,3)
  3. fast2 + FEC(0,0)

注意:为了发挥 FEC 最佳效果,设置 parityshard/(parity+datashard) > packet loss 比如 5/(5+5) > 30%

窗口调整

简易窗口自我调优方法:

第一步:同时在两端逐步 增大 CLIENTrcvwndSERVERsndwnd

第二步:尝试下载,观察如果带宽利用率(服务器+客户端两端都要观察)接近物理带宽则停止,否则跳转到第一步

计算带宽

不丢包 的情况下,有最大 --rcvwnd 个数据包从网络上向你传输,以平均数据包大小 avgsize 计算:

network_cap = rcvwnd*avgsize

数据流量,这个值再除以 ping 值 rtt 等于 最大带宽使用量

max_bandwidth = network_cap/rtt = rcvwnd*avgsize/rtt

举例,假设 rcvwnd = 1024, avgsize = 1KB, rtt = 400ms 则:

max_bandwidth = 1024 * 1KB / 400ms = 2.5MB/s ~= 25Mbps

注意:以上计算不包括 前向纠错 的数据量

前向纠错 是最大带宽量的一个固定比例增加:

max_bandwidth_fec = max_bandwidth*(datashard+parityshard)/datashard

举例,设 datashard = 10, partiyshard = 3 则:

max_bandwidth_fec = max_bandwidth * (10 + 3) /10 = 1.3*max_bandwidth = 1.3 * 25Mbps = 32.5Mbps

flow

会消耗 5 倍以上流量

https://github.com/xtaci/kcptun/issues/91

  1. ISP 对 UDP 丢包极高,例如 50%
  2. 服务器,客户端本身的带宽较低,可以适当调低 sndwnd /rcvwnd
  3. 可以尝试 -dscp 46 参数降低丢包率

FEC 前向纠错默认参数就会 多消耗 30%(可以调整) KCP 协议头部平均多消耗 5% (无法调整) 加上丢包 2 倍还算正常

多半是 sndwnd/rcvwnd 太高,你可以在 client 先降低到 128 然后慢慢再加,边加边观察

服务端是 -mtu 1350 -crypt none -nocomp -dscp 46 -sndwnd 1024 -rcvwnd 1024 -mode fast2

客户端是 -crypt none -mtu 1350 -sndwnd 32 -rcvwnd 128 -mode fast2 -dscp 46 -nocomp

100M 肯定是超卖,实际一定达不到,否则不会出现这种程度的重传

为什么要降低客户端的 rcvwnd 呢,我觉得应该降低服务器端的 sndwnd 来调节速度,客户端的 rcvwnd 应该保持一个较大的值,服务器发来的数据照单全收

带宽不够就不要使用大窗口,量力而行

https://github.com/xtaci/kcp-go/blob/master/kcp.go#L657 取二者的小者

我的配置

server --crypt none --mtu 1400 --sndwnd 2048 --rcvwnd 2048 -dscp 46 -mode fast2
client --crypt none --mtu 1400 --sndwnd 128 --rcvwnd 512 -dscp 46 -mode fast2

实际消耗流量不到两倍

两边 -datashard 0 -parityshard 0 还可以更低,不过响应会慢,看你的接受程度

手动参数设定探讨

https://github.com/xtaci/kcptun/issues/137

策略1:通过超时重传+快速重传,响应速度优先(最大化响应时间):

-mode manual -nodelay 1 -resend 2 -nc 1 -interval 20

策略2:仅仅通过超时重传, 带宽效率优先(有效载比优先)

-mode manual -nodelay 1 -resend 0 -nc 1 -interval 40 或
-mode manual -nodelay 0 -resend 0 -nc 1 -interval 20

策略3:尽可能通过 FEC 纠删,最大化传输速度(推荐):

-mode fast -datashard 5 -parityshard 5

响应速度, 传输带宽高载荷比 三者是跷跷板:

比如响应速度,一个数据包发出后,判断对方是否接收到了,是等待一个 RTT 时间没有收到 ACK 就重发,还是说要再等等看。真实的情况始终未知 -nodelay 1 就是不多等了,结果 ACK 晚到了一点点,就多发包了; -nodelay 0 就是已经等了 RTT 后,再等等看,那么如果再等了还等不到,这个时间就浪费了,响应时间就慢了,整体速度也拖慢了。乐观主义还是悲观主义?

根据 香农定理

  1. 信道容量由带宽及信噪比决定,增大带宽、提高信噪比可以增大信道容量
  2. 在要求的信道容量一定的情况下,提高信噪比可以降低带宽的需求,增加带宽可以降低信噪比的需求
  3. 香农公式给出了信道容量的极限,也就是说,实际无线制式中单信道容量不可能超过该极限,只能尽量接近该极限。在卷积编码条件下,实际信道容量离香农极限还差 3dB;在 Turbo 编码的条件下,接近了香农极限
  1. 可以理解为,高丢包率 == 高噪音
  2. 可以理解为,固定丢包率下增大发送带宽 == 更高的传输成功率 (比如通过FEC)
            固定传输带宽下降低丢包率 == 更高的传输成功率 (比如通过DSCP)
    

策略1 可以理解为, 我非常悲观的判断包一旦超过 RTT 大概率丢失了,通过一切手段尽快重新发送 策略3 也可以理解为,我假定我的纠错包能全部把丢失的包还原出来,每 5 个包,2 个纠错包,小于 2/7均匀丢包率 下 (<28%),必定能还原出来,完全不需要重传

策略1 对于网页访问这种 突发性 请求,查询较为友好 策略2 较为中庸 策略3 对于视频流这种较为友好

200Mbps 联通, 日本 vultr, ping 136ms, UDP 丢包 30% 左右:

server -mode manual -nodelay 0 -resend 0 -nc 1 -interval 40 -dscp 46 -nocomp -mtu 1400 -crypt aes-128 -datashard 70 -parityshard 30
client -mode manual -nodelay 0 -resend 0 -nc 1 -interval 40 -nocomp -dscp 46 -mtu 1400 -crypt aes-128 -datashard 70 -parityshard 30

解释一下另一个问题: --datashard 70 --parityshard 30--datashard 7 --parityshard 3 区别

回答:

  1. 如果完全随机丢包(在时域上均匀分布)这两者没有差别,完全等价。
  2. 如果在时域上不均匀,丢包一会儿有一会儿没有,这两者就有差别。70/30 的配置更又可能把数据纠错出来,7/3 的配置很容易全丢无法恢复

选择在 70+30 的这个大区间整体丢包 30% 还是在 7+3 的这个小区间整体丢包 30%? 只有试试 ...

  • FEC 对于 大文件传输,比如用卫星传输 8K 电影到各个影院是非常合适的,对应 fast.com 测速
  • 对于突发性,交互性,试探性的请求,快速重传 更合适,对应网页
  • 对于 y2b 这种,只要过了最初的 70+30 的点,会慢慢快起来

如果有要快又要突发性好,那就只能以浪费带宽为代价了

x86 平台推荐默认的 aes(AES-256) 如果性能不行,考虑 aes192, aes128 其他平台可以考虑 blowfish, cast5, twofish 如果性能还是不行,考虑 tea, salsa20

另外,如果承载的数据本身已经加密 salsa20 足以

https://github.com/xtaci/kcptun/issues/353

默认的 kcp 协议,丢一次包,退让一次。丢的越多等得越长,这个是必须做的退让。假如 30s 超时

rtt* (1+2 + 4 + 8... ) > 30s

就是断线时间

https://github.com/xtaci/kcptun/issues/342

一两句话说不清楚: fastretrans : 快速重传,数据包乱序,中间丢包, fastack ,fastresend, earlyretrans: 没有后续包要发送了,无法触发快速重传阈值,采用的重传,参考Tail loss probe lostseg: 以上条件都不满足,数据包等待超时后也没到。产生的重传。

retranssegs : = fast + early + lost

低丢包网络可以考虑关闭 FEC

https://github.com/xtaci/kcptun/issues/358

关于断流的分析及解决方法

https://github.com/xtaci/kcptun/issues/353

如果对流量不敏感,客户端性能又足够的话建议始终配置 FEC。对于时常会抽风的网络情况来说 FEC 是一贴良药。我现在一般情况下使用 FEC(30,15),如果抽风实在严重就改成 FEC(5,5)。基本上可以非常稳定地使用了。

FEC(0,0) 在网络情况好的时候确实速度很快且更省流量,但几乎每到晚高峰都得调整,太麻烦了。

https://github.com/skywind3000/kcp/wiki

https://github.com/xtaci/kcptun/issues/288

start-stop-daemon -S -q -b -m -p /tmp/var/kcp.pid -x /koolshare/bin/client_linux_arm5 -- -l 127.0.0.1:1091 -r $ss_basic_server:$ss_basic_kcp_port $ss_basic_kcp_parameter

回馈,简易调节法,有需要看过来

https://github.com/xtaci/kcptun/issues/214

下载:客户端以 rcvwnd 也就是 接收 为主,这里设定 256

上传:客户端的 发送 窗口 sndwnd 相对来说就简单点,一般都是一个固定比例比如 上传 是下载的 1/3 等等

其实窗口大小等同于 tcp 的 sndbuf, rcvbuf,决定最大可同时发送的数据:

最大可用带宽 = min(发送窗口大小, 远端接收窗口大小) / rtt

解决了 qos 类型里面要丢大包,默认是丢50,小包是丢10。有同样困扰的朋友可以试试。 一直都按照应该丢小包或者甚至不丢包的思路走,今天瞎填个100,居然把问题解决了。

关于 rcvwnd 和 sndwnd 参数 https://github.com/xtaci/kcptun/issues/409

rtt 延迟通常反映了 拥塞,保持 wnd 不变是比较 利他 的方式。特别是共享宽带

troubleshoot

session scavenged

客户端 session scavenged 服务端 broken pipe : https://github.com/xtaci/kcptun/issues/277

server 端没响应:https://github.com/xtaci/kcptun/issues/210

防火墙开放 UDP

iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
iptables -I INPUT -p udp -m udp --dport 443 -j ACCEPT

把客户端和服务器端的下面这三个参数逐渐减小:

--rcvwnd 1024 --sndwnd 1024 --mtu 500

Kcp用着用着偶尔就会断开{伪解决方案} :https://github.com/xtaci/kcptun/issues/228

UDP 大流量被运营商自动封这个 ip 的 udp 连接

尝试 服务端和客户端 降低:MTU=512

对于 UDP 不能使用 nmap 进行批量测试,因为 UDP 没有三次握手,从而 没有返回包 Server--> Client

只能使用 echo server 对单一端口进行手动测试

随机化端口方案:https://gist.github.com/suikatomoki/89b1221dab19f64ba2b3

不过这个 不是针对 每个 packet 随机化端口,而是针对 每一个connection 随机化端口

client :

iptables -t nat -I OUTPUT -d <vps_ip> -p udp --dport <kcp_server_port> -j DNAT --to-destination <vps_ip>:4000-5000 --random

server :

iptables -t nat -A PREROUTING -p udp -m multiport --dport 4000:5000 -j REDIRECT --to-ports <kcp_server_port>

一直断,所以用了上面的随机端口方案,但发现,封的时候不是封服务器的 UDP 而且是对宽带本身的 UDP 进出进行封堵,重新拨号换 IP 后就能连上了

UDP 上行流量间歇性被封,下行流量是通的

封锁策略又变为封 UDP 下行流量,UDP 上行流量没被封

会不会是路由器本身的问题,比如这个参数太小

net.core.rmem_max = 26214400

启动的时候,有没有出现 setsocket buffer 报错

kcptun-raw 使用发 raw socketlibev 远端通信为 伪 TCP 报文,重新实现了 kcptun 的最基本功能(未实现加密和纠错等,仍在测试),只需一个程序即可,不需要再另外建立UDP over TCP隧道,不容易“卡住”

https://github.com/Chion82/kcptun-raw

为缓解部分 ISP 对 UDP 断流的问题,通过伪造 TCP 报文,实现了简化版的 kcptun。客户端和服务端进程的直接通信方式为带伪 TCP 报头的 IP packet,通过 raw socket 实现,需要通过 iptables 绕过内核协议栈。

https://github.com/xtaci/kcptun/issues/391

那是 ISP 对 UDP 的封锁 有的地区的运营商喜欢封 UDP(流量一大直接切断),很多 issues 已经反映了这个问题

近两日出现断流现象(小的 MTU?):https://github.com/xtaci/kcptun/issues/218