KCPTUN 参数详解
Posted 小四郎的代码园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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_BUF
和 RCV_BUF
只不过单位不一样 SND/RCV_BUF
单位是 字节,这个单位是 包
服务端与客户端 必须一致 的参数 | 含义 |
---|---|
--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
参数搭配:
fast
+ FEC(5,5)fast2
+ FEC(10,3)fast2
+ FEC(0,0)
注意:为了发挥 FEC 最佳效果,设置 parityshard/(parity+datashard) > packet loss
比如 5/(5+5) > 30%
窗口调整
简易窗口自我调优方法:
第一步:同时在两端逐步 增大 CLIENT 的 rcvwnd
和 SERVER 的 sndwnd
第二步:尝试下载,观察如果带宽利用率(服务器+客户端两端都要观察)接近物理带宽则停止,否则跳转到第一步
计算带宽
在 不丢包 的情况下,有最大 --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
- ISP 对 UDP 丢包极高,例如
50%
- 服务器,客户端本身的带宽较低,可以适当调低
sndwnd /rcvwnd
- 可以尝试
-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 后,再等等看,那么如果再等了还等不到,这个时间就浪费了,响应时间就慢了,整体速度也拖慢了。乐观主义还是悲观主义?
根据 香农定理:
- 信道容量由带宽及信噪比决定,增大带宽、提高信噪比可以增大信道容量
- 在要求的信道容量一定的情况下,提高信噪比可以降低带宽的需求,增加带宽可以降低信噪比的需求
- 香农公式给出了信道容量的极限,也就是说,实际无线制式中单信道容量不可能超过该极限,只能尽量接近该极限。在卷积编码条件下,实际信道容量离香农极限还差 3dB;在 Turbo 编码的条件下,接近了香农极限
- 可以理解为,高丢包率 == 高噪音
- 可以理解为,固定丢包率下增大发送带宽 == 更高的传输成功率 (比如通过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
区别
回答:
- 如果完全随机丢包(在时域上均匀分布)这两者没有差别,完全等价。
- 如果在时域上不均匀,丢包一会儿有一会儿没有,这两者就有差别。
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 socket
和 libev
远端通信为 伪 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
以上是关于KCPTUN 参数详解的主要内容,如果未能解决你的问题,请参考以下文章