C# Socket 在高并发的异步发送与接收时,丢包问题。

Jave.Lin 2011-08-29 05:07:17
如题。

因为之前在写项目程序时,没有考虑好高并发的问题。

自己写了个测试用例,经测试,发现,C# 的Socket 在高并发的异步发送、接收时。

丢包问题挺严重,频率很高,如:开启Server后,连接,2000个用户之后。

在用户端,高并发的对Server发送信息。(无论信息长短,都出发丢数据字节的问题)

(如:通讯协议的包头长度:是:8000个字节长度,但最终只接到,7960左右的字节)

请问大侠们都是否遇到过这样的问题。

遇到这问题时,是怎么处理?有没有好的经验?

我目前只想到,既然该次发送的信息,在接收端接收不完整时候,我就丢掉该包了。

但,这样对用户体验不好。如果是一些网络应用程序中,因为用户点击某个按钮后,没反应。可能还有点多一次或更多次。
...全文
1158 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
MikeCheers 2011-12-04
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 yinjiale 的回复:]
mark。。。学习了,再次鄙视下3楼的某位大师...除了会屁话,没见过解决实际问题的...
[/Quote]

呵呵 屁人就只会屁话了 表鸟他
Jave.Lin 2011-08-30
  • 打赏
  • 举报
回复
而且,基础数据类型的运行,本来就是原子操作,也不需要加锁。

成入:

而且,基础数据类型的运算,本来就是原子操作,也不需要加锁。
Jave.Lin 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 sbwwkmyd 的回复:]

C# code
sendBytes += s.EndSend(iar);
receiveBytes += s.EndReceive(iar);

这两是线程共享变量吗?如果是,加锁了吗?
[/Quote]

这是异步操作的,不需要加锁。

如果你了解异步特性就会知道。你上面所说的,你用线程,只是CPU上的一些异步,

而C# 的Socket 的一些Begin系列的开始异步操作,与End系列的结束异步操作。

是真正发挥各硬件的内部芯片,对任务的时间片高效分配,内部已实现排他锁。

而且,基础数据类型的运行,本来就是原子操作,也不需要加锁。
showjim 2011-08-30
  • 打赏
  • 举报
回复
sendBytes += s.EndSend(iar);
receiveBytes += s.EndReceive(iar);

这两是线程共享变量吗?如果是,加锁了吗?
Jave.Lin 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 linux7985 的回复:]

不知道你说的是TCP还是UDP,如果是TCP的话,我建议你还是检测一下,你的代码是否有Bug吧。

或者你百度一下什么 是 TCP以及他有什么特点,如果会丢掉几个字节,那还叫TCP吗?
[/Quote]

代码,不只是我一个人检查过的。

其它同事也看过,我只是对,发送的字节数统计,与接收之后的字节数统计,结果就是我上面所说的情况。

这个没有什么复杂的逻辑,就是一个+=的运行。

1、发送方:

int sendBytes = 0;

在异步发送之后,(详细的代码我就不写了,以下s 是Socket类型变量,iar 是IAsyncResult类型变量)

sendBytes += s.EndSend(iar);//这里是异步发送完之后,如果发送成功了,这些就可以加上发送成功的字节数。

2、接收方:

int receiveBytes = 0;

在异步接收之后:

receiveBytes += s.EndReceive(iar);

这里没有什么复杂的逻辑,只是对sendBytes 与receiveBytes 的最终比较。



我以前在大学也对TCP有过一定的认识。(安全,可靠,完整性,自检,丢了,复发,等等,这些我就不说了)

但没想到,我在内网,测出这样的问题。

我的其它同事,也不知道是这怎么回事。

后来,改成,每个程序,只启动一个客户端,之后,一点事都没有。
烈火蜓蜻 2011-08-30
  • 打赏
  • 举报
回复
不知道你说的是TCP还是UDP,如果是TCP的话,我建议你还是检测一下,你的代码是否有Bug吧。

或者你百度一下什么 是 TCP以及他有什么特点,如果会丢掉几个字节,那还叫TCP吗?
showjim 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 sbwwkmyd 的回复:]
我曾经测试过开两个局域网客户端,每个1000连接死循环发8000大小的包,从来没有出现过LZ说的症状。
LZ最好检查一下程序是否有并发冲突。[/Quote]
两个客户端都是1000个线程都是死循环发包。
测试服务器是E2160双核60%-80%的CPU利用率,由于测试网卡是100Mb的100%利用率,每秒只能验证处理1500左右的请求。
showjim 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 linjf520 的回复:]
我现在测试出来。

要是把所有的客户端连接都在同一个程序域里。就会出现严重丢包。

把我分别启到10个客户端,每个程序就启动一个。

一点丢包现象都没有。

谢谢~~~~

果然要换个思路去偿试才知道。
[/Quote]
我曾经测试过开两个局域网客户端,每个1000连接死循环发8000大小的包,从来没有出现过LZ说的症状。
LZ最好检查一下程序是否有并发冲突。
Jave.Lin 2011-08-30
  • 打赏
  • 举报
回复
好了。。。问题解决了。

结贴了
Jave.Lin 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 yinjiale 的回复:]

mark。。。学习了,再次鄙视下3楼的某位大师...除了会屁话,没见过解决实际问题的...
[/Quote]

而且,渐渐发现,sp1234老师的话,看似批人。

但,自己反正过来想想,他为什么这么说,总有道理。

也不可能无事找茬。

他总是在指导我们。。。
Jave.Lin 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 yinjiale 的回复:]

mark。。。学习了,再次鄙视下3楼的某位大师...除了会屁话,没见过解决实际问题的...
[/Quote]

其实sp1234老师,一眼判断是我的程序问题。才这么直接说的。

结果,果然是我的程序问题,因为没有用Interlocked来原子操作。

所以即便是一些基础运算,还是要严格的加锁同步。

谢谢大家~~~
小小骨头 2011-08-30
  • 打赏
  • 举报
回复
mark。。。学习了,再次鄙视下3楼的某位大师...除了会屁话,没见过解决实际问题的...
Jave.Lin 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 chichenzhe 的回复:]

引用 12 楼 linjf520 的回复:
而且,基础数据类型的运行,本来就是原子操作,也不需要加锁。

成入:

而且,基础数据类型的运算,本来就是原子操作,也不需要加锁。


错了.
正确的应该是:
int i = 0;
i += 1;

实际上是:
先从内存获取i的地址. 得到变量i的值. (这个时候并发会导致未写入)
然后再加1

然后再写回原址.

……
[/Quote]

了解了,谢谢!
Jave.Lin 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 sbwwkmyd 的回复:]

引用 12 楼 linjf520 的回复:
而且,基础数据类型的运行,本来就是原子操作,也不需要加锁。

成入:

而且,基础数据类型的运算,本来就是原子操作,也不需要加锁。

你的CPU是单核的?你确认“sendBytes += ?”编译后是原子指令?
[/Quote]

OK,这回解决了。

InterLocked原子操作就可以了。
chichenzhe 2011-08-30
  • 打赏
  • 举报
回复
这个问题可以说是多线程的经典问题了,随便找本好点的多线程详解 一般都会介绍这个 i+=1的问题.
chichenzhe 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 linjf520 的回复:]
而且,基础数据类型的运行,本来就是原子操作,也不需要加锁。

成入:

而且,基础数据类型的运算,本来就是原子操作,也不需要加锁。
[/Quote]

错了.
正确的应该是:
int i = 0;
i += 1;

实际上是:
先从内存获取i的地址. 得到变量i的值. (这个时候并发会导致未写入)
然后再加1

然后再写回原址.

你想想. 如果你不加锁,那么 产生覆盖的概率应该是多大呢.

因为int+=操作异常简洁,所以在少量并发的时候你看不出来问题. 即便在大量并发的时候你也仅仅能看见微小的区别.

所以,该是什么情况你了解了吗?
chichenzhe 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 sbwwkmyd 的回复:]
引用 12 楼 linjf520 的回复:
而且,基础数据类型的运行,本来就是原子操作,也不需要加锁。

成入:

而且,基础数据类型的运算,本来就是原子操作,也不需要加锁。

你的CPU是单核的?你确认“sendBytes += ?”编译后是原子指令?
[/Quote]

即使在单核CPU下 sendBytes += 也必须是原子操作. 理由我就不说了.

showjim 2011-08-30
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 linjf520 的回复:]
而且,基础数据类型的运行,本来就是原子操作,也不需要加锁。

成入:

而且,基础数据类型的运算,本来就是原子操作,也不需要加锁。
[/Quote]
你的CPU是单核的?你确认“sendBytes += ?”编译后是原子指令?
Jave.Lin 2011-08-29
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 yinjiale 的回复:]

恩,我建议你可以查一下,各种环境下socket接收和发送信息的推荐字节数。
[/Quote]

我现在测试出来。

要是把所有的客户端连接都在同一个程序域里。就会出现严重丢包。

把我分别启到10个客户端,每个程序就启动一个。

一点丢包现象都没有。

谢谢~~~~

果然要换个思路去偿试才知道。
  • 打赏
  • 举报
回复
嘿.net对待这么个小小的socket,随随便便就能丢掉40个字节,然后敢发布到4.0版,微软也太大胆了。
加载更多回复(3)
c# socket 解决粘包,半包

处理原理:

半包:即一条消息底层分几次发送,先有个头包读取整条消息的长度,当不满足长度,将消息临缓存起来,直到满足长度再解码粘包:两条完整/不完整消息粘在一起,一般是解码完上一条消息,然

110,536

社区成员

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

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

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