最近在写一个接收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);
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);
程序监听了10来个端口,但只有一个端口会接收数据。我也改成了收到包就立马保存到队列,然后有一个线程异步从队列里拿数据包处理,这个时候从收到数据包程序处理的时间就只有0.008毫秒了,但只要把这些包转换成对象,并且存储到list就会丢,转换成对象但不存list就不会丢,(现象是这样的) 我实在无法解释为什么,只好请教各位高手了
{
//lock (TimeAndSalebuffer)
{
TimeAndSalebuffer.Add(newData);
}
} static void QuoteLevel1HandlerFunction(ref QuoteLevel1 newData)
{
//lock (QuoteLevel1buffer)
{
QuoteLevel1buffer.Add(newData);
}
//Console.WriteLine(((QuoteLevel1)newData).symbol);
}
static IList<IQuoteEntity> QuoteLevel1buffer = new List<IQuoteEntity>(1000000);
static IList<IQuoteEntity> TimeAndSalebuffer = new List<IQuoteEntity>(1000000);
如果是后者,把ref去掉。
前者,定义一个缓存队列最好。
至于你认为由于线程另外花费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接收,但这个似乎不可能吧
{
if (au.Equals(u)) Console.WriteLine("Equals");
}U为一个struct,UList是个List<U>。明显Ulist添加的引用。而程序中传入的是引用。如果接收数据的时候用的同一个对象,没有new新的对象,那么List中存储的都是同一个对象。
这位仁兄,本来这个帖子不是讨论这个问题的,但你的说法是错误的,所以我必须给出纠正。首先请看下面的代码: 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的了。
struct的equals确实不是引用比较。
UDP在接受端是会造成丢包的
因为UDP协议的操作过程在操作系统层面看,就是UDP协议栈接收到网络数据,通知操作系统,操作系统把这些网络数据保存到该端口对应的接收缓冲区,再通知应用程序来提取。如果应用程序因为某些耗时操作,提取速度慢了,接收缓冲区一直满的,而新的数据不停到来,新到的数据就会被丢弃。楼主所谓的“把socket的receivebuffersize设置足够大就不丢了”真的可行?windows操作系统,receivebuffersize默认值就是8K吧,最大值也只能调到8k。linux系统receivebuffersize默认值8k,倒是可以调到64k。
要解决,个人认为只有两种方法,
1,调大receivebuffersize值,但由于最大值限制,除非你从内核里面改
2,加快接受线程处理速度,用专门的线程把数据从接受缓冲区拷贝出来到自己设定的缓存区域
本人也在做这方面的工作,希望和楼主深入探讨。之前也从楼主的其他回帖里学到很多,非常感谢
我一直在测试这个东西,现在我已经改成自己创建buffer区,收到二进制丢到自己的buffer就立即返回,所以基本上从收到byte[]到beginreceive已不耗时间了,但中间偶尔会出现几毫秒至几十毫秒的情况。这个肯怕没办法解决了,现在关键是beginreceive和endreceive本身耗掉的时间没办法优化了,从现象看,通过设置buffersize能减少这种情况,我现在设置的buffersize,已经到7m了,现在的情况是,如果buffersize稍微大于网络上占用的带宽,就不会丢包。
想请教楼主,你说的receivebuffersize 是自己创建的BUFFER区,而不是系统的接收缓冲buffer区吧??
刚开始我也想直接扩大系统的接受缓冲buffer,但发现被系统定死了,除非改内核,而我显然做不来...
你的解决思路也是提高接收速度,使用自己的buffer?
client.Client.ReceiveBufferSize = bufferSize;client是UdpClient