接收UDP丢包

易企加_天马行空 CEO  2010-04-24 11:08:12
最近在写一个接收UDP丢包的程序,碰到一个奇怪的现象,在每秒发送3000个包的情况下,一共发送30w个,如果把bygte[]转成对象并存储到list里,就会丢掉几百个,如果不做存储到list的动作,就不会掉,而且每秒发12000个都不会丢。谁能帮我解释为什么呢?谢谢了

说明:我的处理数据包的代码只花费了0.055毫秒,但udpclient本身endreceive和beginreceive,加起来却花费0.022+0.026毫秒。代码如下
new:
Client = new UdpClient(IpEp);
Client.Client.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.NoChecksum, true);
//Client.Client.ExclusiveAddressUse = true;
if (bufferSize > 0)
{
Client.Client.ReceiveBufferSize = bufferSize;(13070)
}
// Receives a datagram from a remote host asynchronously
Client.BeginReceive(_callback, null);


callback:
IPEndPoint ep = IpEp;
byte[] receiveBytes = Client.EndReceive(iar, ref ep);
_OnReceiveBytes(receiveBytes);
success = true;
// Continues to receives a datagram from a remote host asynchronously.
Client.BeginReceive(_callback, null);
...全文
479 点赞 收藏 33
写回复
33 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
首先谢谢sp1234,问题基本解决了,我把socket的receivebuffersize设置足够大就不丢了。还是处理速度没有发送速度快的问题,因为我是看的平均时间,感觉非常快,其实在其中的某一次可能会花费几毫秒甚至十几毫秒,所以会丢包,但我跟踪了所有代码,Endreceive和beginreceive基本上占掉所有花费时间的一半以上,而且很不稳定,长的可能花费30几毫秒。
回复
leafold 2010-04-26
            U u = new U(){I=99};

for (int ui = 0; ui < 10; ui++ ){ UList.Add(u);}

foreach (var au in UList)
{
if (au.Equals(u)) Console.WriteLine("Equals");
}

U为一个struct,UList是个List<U>。明显Ulist添加的引用。而程序中传入的是引用。如果接收数据的时候用的同一个对象,没有new新的对象,那么List中存储的都是同一个对象。
回复
leafold 2010-04-26
是我搞错了。
struct的equals确实不是引用比较。
回复
据说QQ聊天时经常丢包。如果你的接受股票信息有QQ聊天那种容忍度,就可以采取udp。
回复
那就没有办法了。顶多你可以尝试把 Client.BeginReceive(_callback, null); 这一句提前5行写。

你说采取udp通信是为了效率,但是如果需要可靠性时,就需要自己设计协议来保证发送端重发丢失的包,这是必须的。如果你不实现相关的提高可靠性的协议,那么遇到这种“丢包”的担心就必须自己承担,因为udp本来就是这样设计的,就是经常丢包的!
回复
[Quote=引用 19 楼 leafold 的回复:]
C# code
U u = new U(){I=99};

for (int ui = 0; ui < 10; ui++ ){ UList.Add(u);}

foreach (var au in UList)
{
if (au.Equals(u)) Con……
[/Quote]

这位仁兄,本来这个帖子不是讨论这个问题的,但你的说法是错误的,所以我必须给出纠正。

首先请看下面的代码:

QuoteLevel1 lev = new QuoteLevel1() { askPrice = 1 };
QuoteLevel1 lev1 = new QuoteLevel1() { askPrice = 1 };
Console.WriteLine(lev.Equals(lev1));
Console.WriteLine(object.ReferenceEquals(lev,lev1));

Console.ReadLine();

QuoteLeve1 是struct 这段代码输出为true false,我想这足以解释为什么你的代码输出为true了,
再看下面代码:

List<QuoteLevel1> list = new List<QuoteLevel1>();
list.Add(lev);
list.Add(lev);
foreach (QuoteLevel1 l in list)
{
Console.WriteLine(lev.Equals(l));
Console.WriteLine(object.ReferenceEquals(lev, l));
}

输出为True False True False.
这就说明添加到list的对象是重新copy的了。
回复
yuanhuiqiao 2010-04-26
udp协议是一种无连接的协议,和tcp协议比较传输速度快,占用资源少,但容易出现丢包,可以在包头稍微加个校验,丢包重发。
回复
chensun1229 2010-04-26
路过,学习了~`
回复
cqsfd 2010-04-26
行 我测试下
回复
另外你coppy到自己的buffer里,这个buffer必须是一开始就创建了的,不能每次去new,否则对速度有影响。
回复
我认为我的程序已经优化到我能优化的顶端了,现在通过修改buffer确实能解决问题。

client.Client.ReceiveBufferSize = bufferSize;

client是UdpClient
回复
cqsfd 2010-04-26
我在做的,是用专门线程,将系统接收缓冲buffer里的东西拷贝到自己创建的buffer
想请教楼主,你说的receivebuffersize 是自己创建的BUFFER区,而不是系统的接收缓冲buffer区吧??
刚开始我也想直接扩大系统的接受缓冲buffer,但发现被系统定死了,除非改内核,而我显然做不来...
你的解决思路也是提高接收速度,使用自己的buffer?
回复
[Quote=引用 25 楼 cqsfd 的回复:]
引用 24 楼 tmxk2002 的回复:

首先谢谢sp1234,问题基本解决了,我把socket的receivebuffersize设置足够大就不丢了。还是处理速度没有发送速度快的问题,因为我是看的平均时间,感觉非常快,其实在其中的某一次可能会花费几毫秒甚至十几毫秒,所以会丢包,但我跟踪了所有代码,Endreceive和beginreceive基本上占掉所有花费时间的一半以上,而且很不稳定……
[/Quote]

我一直在测试这个东西,现在我已经改成自己创建buffer区,收到二进制丢到自己的buffer就立即返回,所以基本上从收到byte[]到beginreceive已不耗时间了,但中间偶尔会出现几毫秒至几十毫秒的情况。这个肯怕没办法解决了,现在关键是beginreceive和endreceive本身耗掉的时间没办法优化了,从现象看,通过设置buffersize能减少这种情况,

我现在设置的buffersize,已经到7m了,现在的情况是,如果buffersize稍微大于网络上占用的带宽,就不会丢包。
回复
guoyichao 2010-04-26
最简单的解决办法是调用_OnReceiveBytes(receiveBytes)时也采用异步委托方式调用。
回复
cqsfd 2010-04-26
[Quote=引用 24 楼 tmxk2002 的回复:]

首先谢谢sp1234,问题基本解决了,我把socket的receivebuffersize设置足够大就不丢了。还是处理速度没有发送速度快的问题,因为我是看的平均时间,感觉非常快,其实在其中的某一次可能会花费几毫秒甚至十几毫秒,所以会丢包,但我跟踪了所有代码,Endreceive和beginreceive基本上占掉所有花费时间的一半以上,而且很不稳定,长的可能花费30几毫秒。
[/Quote]
这个问题我之前在网络版发帖讨论过
UDP在接受端是会造成丢包的
因为UDP协议的操作过程在操作系统层面看,就是UDP协议栈接收到网络数据,通知操作系统,操作系统把这些网络数据保存到该端口对应的接收缓冲区,再通知应用程序来提取。如果应用程序因为某些耗时操作,提取速度慢了,接收缓冲区一直满的,而新的数据不停到来,新到的数据就会被丢弃。楼主所谓的“把socket的receivebuffersize设置足够大就不丢了”真的可行?windows操作系统,receivebuffersize默认值就是8K吧,最大值也只能调到8k。linux系统receivebuffersize默认值8k,倒是可以调到64k。
要解决,个人认为只有两种方法,
1,调大receivebuffersize值,但由于最大值限制,除非你从内核里面改
2,加快接受线程处理速度,用专门的线程把数据从接受缓冲区拷贝出来到自己设定的缓存区域
本人也在做这方面的工作,希望和楼主深入探讨。之前也从楼主的其他回帖里学到很多,非常感谢
回复
[Quote=引用 1 楼 tmxk2002 的回复:]
我也改成了收到包就立马保存到队列,然后有一个线程异步从队列里拿数据包处理,这个时候从收到数据包程序处理的时间就只有0.008毫秒了
[/Quote]这不等于说线程就不另外花费cpu时间了。
回复
很正常,就算丢失几万个包也很正常。udp本来就是如此,它从来不在c/s链路上保证发送或者读取的可靠性的,如果你发的太快,或者读得太慢,或者任何网络上任何压力和干扰.....没有人能够说得清的无数原因,随随便便就丢包了。

如果要可靠地通信,使用tcp。tcp基本上可以保证在网络不特别拥塞时有99.99%的可靠性。
回复
leafold 2010-04-25
是丢还是数量准确但数据会重复?
如果是后者,把ref去掉。
前者,定义一个缓存队列最好。
回复
sp1234,谢谢你的回复,我也知道udp不可靠,但更关键是,我不把对象存入到list里,就不会丢,就一个非常简单的list.Add操作,这是我疑惑的地方
至于你认为由于线程另外花费cpu时间了,我一直监控着cpu和network资源,cpu最高跳到25%,也只很少的跳那么一下两下,大部分情况下cpu为0,至于network,我是在千兆网里测试的,大约为3.5%的样子,所以按里也不会因为这个拥堵导致,随便说下服务器的配置,4个双内核cpu,内存16G,安装windows2008的64bit

至于为什么不用tcp,第一个是因为源头是udp广播的,第二个应用是股市的报价接收,用tcp会来不及接收的。虽然不会丢包,有序了,但就会导致接收报价延迟厉害。

至于上面还有一位兄弟说计数重复,我是在一收到包就用inerlocked.increment计数的,按理不会冲掉数据,另外用ref,是在计数之后了,就算因为ref导致数据冲掉,计数仍然应该是对的,因为我这里的对象都是struct,为了防止它copy,我才用的ref,但最终存储的时候是直接传递参数的,所以应该不会冲掉数据。

我再强调一下,其他什么都一样,就是在最后调用list.add操作后丢包,否则不丢。所以我怀疑是不是频繁对内存操作会不会导致udp接收,但这个似乎不可能吧。。。
回复
TimeAndSale 等是struct
回复
加载更多回复
相关推荐
发帖
C#
创建于2007-09-28

10.5w+

社区成员

.NET技术 C#
申请成为版主
帖子事件
创建了帖子
2010-04-24 11:08
社区公告

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