KumoKyaku / kcp

KCP C#版。线程安全,运行时无alloc,对gc无压力。
MIT License
832 stars 136 forks source link

可能出现了数据虚报长度? #23

Closed YYHEggEgg closed 1 year ago

YYHEggEgg commented 1 year ago

首先还是要感谢大佬开发的线程安全版的C# KCP,十分实用!

image

之前在服务器的日志中 (服务器并非C#代码) 找到一些 KCP 数据包报错,于是写了一些代码,截下Output回调发出的包检查数据是否存在问题。

期间一共发了约 4000-5000 个基础 UDP 包(单个KCP实例),服务器在内网虚拟机,程序检查到了 51 次数据损坏。

具体情况是:data 部分的数据长度与 len 不符。可能会长,也可能会短,但是包实际的有效大小与 len 相同。

更具体地,我的所有包在加密前都带有包特征(开头0x4567和结尾0x89AB),如果data的实际长度比len小,可以找到包头但找不到尾部,input触发-2;如果data的实际长度比len大,则有包头并可以在len - 2处找到包尾,但后面的内容(指在下一个分片前的内容)既不是空包也不是外部Send的内容,input会触发-1.

贴几个典型一点的日志,下面是data比len小的:

13:38:38 <Warn:KcpPacketAudit> Detected invalid kcp packet sent at 2023/5/14 13:38:38, bytes_sent=72
13:38:38 <Dbug:KcpPacketAudit> 
----------KcpPacketAudit report----------
Detected following exceptions:
  Segment 0's content length is shorter that reported: 48 bytes, Content (44 bytes): 7BB688DC18DBBB7DDF8823686397EE96A0EC4CC9B26819FA33E24D0E998CCCC134594602637D80301B8417DC

Raw packet (conv=1011, token=4229662954): F3030000EA881BFC51000001A89EC418A70C000014120000300000007BB688DC18DBBB7DDF8823686397EE96A0EC4CC9B26819FA33E24D0E998CCCC134594602637D80301B8417DC

Segment 0: Range 0 +72bytes
  cmd=81, frg=0, wnd=256, ts=415538856, sn=3239, una=4628
  Content (reported: 48bytes, actual: 44bytes): 
    7BB688DC18DBBB7DDF8823686397EE96A0EC4CC9B26819FA33E24D0E998CCCC134594602637D80301B8417DC

-----------------------------------------

下面是data比len大的:

13:38:37 <Warn:KcpPacketAudit> Detected invalid kcp packet sent at 2023/5/14 13:38:37, bytes_sent=76
13:38:37 <Dbug:KcpPacketAudit> 
----------KcpPacketAudit report----------
Detected following exceptions:
  Segment 0's content length is longer that reported, Content: 
Range 28 +36bytes (reported): 7BB68AF318DBBB7DDF9C233B6397EE96A4E04CC9B26829F943CD4D0EB1961514F166F196
Range 64 +12bytes (exceeded): 63B4C7411C84987211EA41A6

Raw packet (conv=1011, token=4229662954): F3030000EA881BFC5100FF008F98C418760C0000F3110000240000007BB68AF318DBBB7DDF9C233B6397EE96A4E04CC9B26829F943CD4D0EB1961514F166F19663B4C7411C84987211EA41A6

Segment 0: Range 0 +76bytes
  cmd=81, frg=0, wnd=255, ts=415537295, sn=3190, una=4595
  Content (reported: 36bytes, actual: 48bytes): 
    7BB68AF318DBBB7DDF9C233B6397EE96A4E04CC9B26829F943CD4D0EB1961514F166F19663B4C7411C84987211EA41A6

-----------------------------------------

即使是程序逻辑上的问题,似乎也不应出现实际data的长度与len不符的情况。

服务器和客户端的代码都不太方便公开(其实是没有),所以排错之类我自己会来做。大佬有什么debug的建议吗?比如:

上文日志使用的 KcpPacketAudit.cs

(注:程序使用的是一种变种的KCP,在conv后面插入了4字节当作token,每次连接时生成的一个随机数,以后input进来的包token不符合则丢弃返回-1)

KumoKyaku commented 1 year ago

我现在一个没有电脑的环境,预计很长一段时间没有电脑。

凭记忆的几点: 库里有三个kcpmegmentmanager,使用不同的内存处理方式。测试下是不是都能复现。

传入send后,数据会立刻复制到库的内置内存块,释放memory是安全的。

查看mtu值,默认好像1400,手动创建这个指定长度的数据包,发送send,查看分片是否符合预期。

测试kcpsegment encode方法,感觉就是因为你的报头加了4个字节的token导致数据错误

KumoKyaku commented 1 year ago

用1400长度,1-255循环的字节数组数据包。应该不会分片。 在用个1410长度的,能稳定分成2片测试。打印raw data直接打印byte比对,不用十六进制。十六进制不容易观察数据错位。

YYHEggEgg commented 1 year ago

感谢建议!等着这周抽出时间来写一点测试代码

YYHEggEgg commented 1 year ago

先写了一点测试的代码,大约就是将一个 kcp 的 output 导出到另一个 kcp 的 input 并 check。测下来没有问题。

后来换上实际使用的协议栈,加了一些测试标记,发现 Flush 疑似有多次调用。
review 了一下代码,发现 client 连接完成后开了两次 Update 线程,导致 buffer 冲突,产生一些十分玄学的 bug。

总结项目代码没有问题,Update() 不能并行调用在文档里也确实写明了。问题已经解决了,很抱歉浪费了您的时间!