c# socket tcp 接收到的内容乱了

qq_33368845 2018-03-08 07:16:02
实在找不出bug,只好来求助大佬们了。

情况是这样的,我的服务器每天大约能收到好几个内容错了的包,这几个包错得都很有规律。

比如客户端发送过来的内容为AAAABBBBCCCCDDDD 结果如下代码接收到的内容表现为AAAACCCCCCCCDDDD

也就是有4个字符不见了,而且必然多出4个字符,多出来的4个字符是从原本的内容中拷贝的,且紧靠被拷贝字符的位置。

我在本地服务器中进行了测试,然而不知出于什么原因,本地服务器一直接收到完整的包,并没有进入包头不全和包体不全的情况。

客户端发送速度为0.03秒一个包,大约进行了30万次发送,所有的包客户端都能正常收到。

我测试时是使用本地服务器与客户端。在同一台大脑内。

我的包头固定4个字符。


void CheckPackage()
{
if (isClose) return;
int length = 0;
isLost = false;
while (length < recieveLength)
{
//包头不全
if (length + 4 > recieveLength)
{
Tool.Debug.LogWarning("包头不全");
ContinueRecieve(length, recieveLength - length);
return;
}

//获取包头
int packageL = BitConverter.ToInt32(recive, length);

//异常包体
if (packageL > 8192) { Tool.Debug.LogWarning("[异常错误]" + "数据包长度为" + packageL.ToString()); break; }

int need = length + 4 + packageL;

//包体信息不全
if (recieveLength < need)
{
Tool.Debug.LogWarning("包体信息不全");
ContinueRecieve(length, recieveLength - length);
return;
}

length += 4;

//心跳
if (packageL == 0)
{
if (returnHeatBeat) { sendQueue.Enqueue(new byte[] { 0, 0, 0, 0 }); Send(); }
}
else
{
byte[] buff = new byte[packageL];
for (int i = 0; i < buff.Length; i++)
{
buff[i] = recive[i + length];
}
NetMessageManager.Instance.AddBuff(socket.RemoteEndPoint, buff, id);
}
length += packageL;
};

ContinueRecieve(0, 0);
}

void ContinueRecieve(int copyStart, int copyCount)
{
for (int i = 0; i < copyCount; i++)
recive[i] = recive[copyStart + i];
recieveLength = copyCount;
SocketError error3;
socket.BeginReceive(recive, copyCount, recive.Length - copyCount, SocketFlags.None, out error3, EndRecive, this);
}
...全文
797 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
xuzuning 2018-03-11
  • 打赏
  • 举报
回复
正因为错误是偶发性、不确定的,所以才建议你在出现错误时记录下现场 然后你的程序就可投入正式使用,定期查看错误报告是否为空就可以了
qq_33368845 2018-03-11
  • 打赏
  • 举报
回复
引用 20 楼 sp1234 的回复:
所以如果你说是“包头不全”或者“包体不全”,你这块代码很简单,那么你可以在 EndReceive 中向 receice 数组copy 数据的时候打印/记录日志(比如说使用 vs 下运行测试,写
Debug.Print(.....)
打印收到的数据内容,然后在你的“包头不全、包体不全”时写断言,让vs 立刻进入调试,查看最近的日志来判断到底是 EndReceive 收信息不对还是你的 receive 数组中的数据不对。
这种方法我已经测试了,我在客户端同时使用了3种发送方式,完整包发送、(半包头+半包体)、(半包体+半包体),发送了50W次,接收了50次,没有出现任何异常。 无论是EndReceive还是receive数组都是正常的。 在测试中无法引发这个bug出来,这令我十分难办,目前无法猜测出引发这个bug的原因。
  • 打赏
  • 举报
回复
所以如果你说是“包头不全”或者“包体不全”,你这块代码很简单,那么你可以在 EndReceive 中向 receice 数组copy 数据的时候打印/记录日志(比如说使用 vs 下运行测试,写
Debug.Print(.....)
打印收到的数据内容,然后在你的“包头不全、包体不全”时写断言,让vs 立刻进入调试,查看最近的日志来判断到底是 EndReceive 收信息不对还是你的 receive 数组中的数据不对。
  • 打赏
  • 举报
回复
至少可以说我是没有发现 .net 处理 tcp 收发时有系统“丢包”的问题的,出问题都是自己程序问题。你可以在你的 EndReceive 和处理 NetMessageManager.Instance 的地方分别测试,也许是并发处理 BetMessage 的问题而不是往 recieve 数组写数据时的问题。
xuzuning 2018-03-09
  • 打赏
  • 举报
回复
引用 7 楼 qq_33368845 的回复:
引用 1 楼 xuzuning 的回复:
报文加上 CRC 校验,这样你就可以知道什么时候出的错
谢谢大佬,我知道什么时候出错,我是要解决这个错误。。。
既然你知道什么时候出的错,那你就应该能排查出出错的原因,知道了出错的原因,自然就能有解决的办法
cheng2005 2018-03-09
  • 打赏
  • 举报
回复
引用 10 楼 qq_33368845 的回复:
[quote=引用 9 楼 wddw1986 的回复:] 1,首先确定是否丢包,发送的包和接收的包数量是否一致,排除粘包的可能(个人认为你的问题粘包的可能不大,因为粘包不会每次都是一样的症状); 2,检查发包的内容,这个问题发包的时候就出错的可能性不小; 3,检查缓冲区处理的逻辑,有可能缓冲区在多线程操作的情况下被覆盖了(可能较大); 4,检查组包和拆包的逻辑(可能性较小);
1,完全能正常处理粘包,我以0.03秒每次的速度向服务器发送数据包。有大量的包都是粘着的,所以我认为问题不在粘包上,而是在包体不全或者包头不全的情况上。 2,发包都是同一份代码,数据格式都是json, 同一个协议每天会发N次,却只有几次内容错乱。 所以不太可能是客户端发来了错误的数据库。 3,我严格的保护了这个线程,所有方法都是私有的,只有接收结束时会调用这个方法。所以应该和多线程无关。 4 ,我可能陷入了死胡同,私以为这个可能性是最大的,但是找不出问题所在[/quote] 个人建议要重点检查发包的内容,因为你的收发逻辑感觉问题不大,因为出问题的概率小,如果底层有问题的话,数据量大,出错的情况会很大。 从应用层找错误吧,非常有可能不是通讯层的问题。
qq_33368845 2018-03-09
  • 打赏
  • 举报
回复
由于我没有贴好代码,令大佬们阅读不方便,所以重开了一个帖子。
qq_33368845 2018-03-09
  • 打赏
  • 举报
回复
引用 9 楼 wddw1986 的回复:
1,首先确定是否丢包,发送的包和接收的包数量是否一致,排除粘包的可能(个人认为你的问题粘包的可能不大,因为粘包不会每次都是一样的症状); 2,检查发包的内容,这个问题发包的时候就出错的可能性不小; 3,检查缓冲区处理的逻辑,有可能缓冲区在多线程操作的情况下被覆盖了(可能较大); 4,检查组包和拆包的逻辑(可能性较小);
1,完全能正常处理粘包,我以0.03秒每次的速度向服务器发送数据包。有大量的包都是粘着的,所以我认为问题不在粘包上,而是在包体不全或者包头不全的情况上。 2,发包都是同一份代码,数据格式都是json, 同一个协议每天会发N次,却只有几次内容错乱。 所以不太可能是客户端发来了错误的数据库。 3,我严格的保护了这个线程,所有方法都是私有的,只有接收结束时会调用这个方法。所以应该和多线程无关。 4 ,我可能陷入了死胡同,私以为这个可能性是最大的,但是找不出问题所在
cheng2005 2018-03-09
  • 打赏
  • 举报
回复
1,首先确定是否丢包,发送的包和接收的包数量是否一致,排除粘包的可能(个人认为你的问题粘包的可能不大,因为粘包不会每次都是一样的症状); 2,检查发包的内容,这个问题发包的时候就出错的可能性不小; 3,检查缓冲区处理的逻辑,有可能缓冲区在多线程操作的情况下被覆盖了(可能较大); 4,检查组包和拆包的逻辑(可能性较小);
qq_33368845 2018-03-09
  • 打赏
  • 举报
回复
引用 3 楼 sp1234 的回复:
当你的 recieveLength 远大于 need 的时候,怎么能不丢数据的?说明一下。 另外就是你的代码看上去应该精简掉 90%,感觉写得很大很臃肿,无用的代码太多了。应该精简。
上面的代码就是处理粘包和分包的AddBuff那里是一个完全的包的内容。 CheckPackage()是将所接收到的内容处理掉, 这个while循环是将多个包逐个拿出来。
qq_33368845 2018-03-09
  • 打赏
  • 举报
回复
引用 1 楼 xuzuning 的回复:
报文加上 CRC 校验,这样你就可以知道什么时候出的错
谢谢大佬,我知道什么时候出错,我是要解决这个错误。。。
qq_33368845 2018-03-09
  • 打赏
  • 举报
回复
这个就是我处理粘包和分包的代码呀。
xian_wwq 2018-03-09
  • 打赏
  • 举报
回复
本地测试没有问题不代表代码没有问题 举个最简单的例子 在局域网测试通过的程序,在Internet不一定能够正常工作 因为tcp是基于流的,一旦网络不稳定,那么“粘包”的问题就很严重 为了避免socket通信和数据业务解析相互影响,就得把数据接收和解析分离; 建议lz把EndReceive的处理逻辑贴出来
xuzuning 2018-03-09
  • 打赏
  • 举报
回复
发现错误的时机很重要,自然是越早越好(crc校验可在第一时间发现) 仅仅保存数据时没用的,还要保存相关的变量及值
qq_33368845 2018-03-09
  • 打赏
  • 举报
回复
引用 15 楼 xuzuning 的回复:
其实你并不知道哪儿错了! 你应该在出现错误时保存现场,这样才能分析出原因,依据保存的现场数据,你就有可能再现错误
我保存了出错时接收到的内容。
xuzuning 2018-03-09
  • 打赏
  • 举报
回复
其实你并不知道哪儿错了! 你应该在出现错误时保存现场,这样才能分析出原因,依据保存的现场数据,你就有可能再现错误
qq_33368845 2018-03-09
  • 打赏
  • 举报
回复
引用 13 楼 xuzuning 的回复:
引用 7 楼 qq_33368845 的回复:
[quote=引用 1 楼 xuzuning 的回复:] 报文加上 CRC 校验,这样你就可以知道什么时候出的错
谢谢大佬,我知道什么时候出错,我是要解决这个错误。。。
既然你知道什么时候出的错,那你就应该能排查出出错的原因,知道了出错的原因,自然就能有解决的办法[/quote] 知道在哪出错了,但是并不清楚出错的原因。无论我如何测试都没有任何问题,粘包、断包各种情况都能正确的拆解出来。 我发送的数据量已经大大的超过的正常服务器所接收的数据量。测试的时候就是不出错。
threenewbee 2018-03-08
  • 打赏
  • 举报
回复
你的接收代码有问题,调试下看看。
  • 打赏
  • 举报
回复
当你的 recieveLength 远大于 need 的时候,怎么能不丢数据的?说明一下。 另外就是你的代码看上去应该精简掉 90%,感觉写得很大很臃肿,无用的代码太多了。应该精简。
  • 打赏
  • 举报
回复
把你处理分包(以及沾包)的代码贴出来。
加载更多回复(1)

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧