最近在写一个接收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);

解决方案 »

  1.   

    补充的说明:
    程序监听了10来个端口,但只有一个端口会接收数据。我也改成了收到包就立马保存到队列,然后有一个线程异步从队列里拿数据包处理,这个时候从收到数据包程序处理的时间就只有0.008毫秒了,但只要把这些包转换成对象,并且存储到list就会丢,转换成对象但不存list就不会丢,(现象是这样的) 我实在无法解释为什么,只好请教各位高手了
      

  2.   

    把这些包转换成对象,并且存储到list就会丢.是怎么保存的?
      

  3.   

            static void TimeAndSAleHandlerFunction(ref TimeAndSale newData)
            {
                //lock (TimeAndSalebuffer)
                {
                    TimeAndSalebuffer.Add(newData);
                }
            }        static void QuoteLevel1HandlerFunction(ref QuoteLevel1 newData)
            {
                //lock (QuoteLevel1buffer)
                {
                    QuoteLevel1buffer.Add(newData);
                }
                //Console.WriteLine(((QuoteLevel1)newData).symbol);
            }
      

  4.   

            static IList<IQuoteEntity> QuoteLevel2buffer = new List<IQuoteEntity>(1000000);
            static IList<IQuoteEntity> QuoteLevel1buffer = new List<IQuoteEntity>(1000000);
            static IList<IQuoteEntity> TimeAndSalebuffer = new List<IQuoteEntity>(1000000);
      

  5.   

    多线程?怎么做?我现在用的是UdpClient的异步。
      

  6.   

    感觉用多线程也不会有用,因为处理时间已经够短了。现在的现象就是只要加上那句Add的语句(这句话花费的平均时间也只有0.001毫秒),就丢包,注释掉的话,每秒钟发到12000都不会丢,这就是我郁闷的地方。
      

  7.   

    你没看清楚我的内容,现象是添加到list才慢,对象都创建的,而且是strcut,是直接从内存转过来1秒钟可以创建500w次,应该不慢吧
      

  8.   

    是丢还是数量准确但数据会重复?
    如果是后者,把ref去掉。
    前者,定义一个缓存队列最好。
      

  9.   

    很正常,就算丢失几万个包也很正常。udp本来就是如此,它从来不在c/s链路上保证发送或者读取的可靠性的,如果你发的太快,或者读得太慢,或者任何网络上任何压力和干扰.....没有人能够说得清的无数原因,随随便便就丢包了。如果要可靠地通信,使用tcp。tcp基本上可以保证在网络不特别拥塞时有99.99%的可靠性。
      

  10.   

    这不等于说线程就不另外花费cpu时间了。
      

  11.   

    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接收,但这个似乎不可能吧
      

  12.   

                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中存储的都是同一个对象。
      

  13.   


    这位仁兄,本来这个帖子不是讨论这个问题的,但你的说法是错误的,所以我必须给出纠正。首先请看下面的代码:                     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的了。
      

  14.   

    那就没有办法了。顶多你可以尝试把 Client.BeginReceive(_callback, null); 这一句提前5行写。你说采取udp通信是为了效率,但是如果需要可靠性时,就需要自己设计协议来保证发送端重发丢失的包,这是必须的。如果你不实现相关的提高可靠性的协议,那么遇到这种“丢包”的担心就必须自己承担,因为udp本来就是这样设计的,就是经常丢包的!
      

  15.   

    据说QQ聊天时经常丢包。如果你的接受股票信息有QQ聊天那种容忍度,就可以采取udp。
      

  16.   

    是我搞错了。
    struct的equals确实不是引用比较。
      

  17.   

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

  18.   

    这个问题我之前在网络版发帖讨论过
    UDP在接受端是会造成丢包的
    因为UDP协议的操作过程在操作系统层面看,就是UDP协议栈接收到网络数据,通知操作系统,操作系统把这些网络数据保存到该端口对应的接收缓冲区,再通知应用程序来提取。如果应用程序因为某些耗时操作,提取速度慢了,接收缓冲区一直满的,而新的数据不停到来,新到的数据就会被丢弃。楼主所谓的“把socket的receivebuffersize设置足够大就不丢了”真的可行?windows操作系统,receivebuffersize默认值就是8K吧,最大值也只能调到8k。linux系统receivebuffersize默认值8k,倒是可以调到64k。
    要解决,个人认为只有两种方法,
    1,调大receivebuffersize值,但由于最大值限制,除非你从内核里面改
    2,加快接受线程处理速度,用专门的线程把数据从接受缓冲区拷贝出来到自己设定的缓存区域
    本人也在做这方面的工作,希望和楼主深入探讨。之前也从楼主的其他回帖里学到很多,非常感谢
      

  19.   

    最简单的解决办法是调用_OnReceiveBytes(receiveBytes)时也采用异步委托方式调用。
      

  20.   


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

  21.   

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

  22.   

     我认为我的程序已经优化到我能优化的顶端了,现在通过修改buffer确实能解决问题。
           
     client.Client.ReceiveBufferSize = bufferSize;client是UdpClient
      

  23.   

    另外你coppy到自己的buffer里,这个buffer必须是一开始就创建了的,不能每次去new,否则对速度有影响。
      

  24.   

    udp协议是一种无连接的协议,和tcp协议比较传输速度快,占用资源少,但容易出现丢包,可以在包头稍微加个校验,丢包重发。