当前位置:  开发笔记 > 程序员 > 正文

为什么Socket.BeginReceive会丢失来自UDP的数据包?

如何解决《为什么Socket.BeginReceive会丢失来自UDP的数据包?》经验,为你挑选了0个好方法。

The following code waits for data over UDP. I have a test function that sends 1000 packets (datagrams?) of 500 bytes each. Each time I run the test function, the receiver gets only the first few dozen packets but drops the rest. I looked at the incoming network data using Wireshark and I see all 1000 packets are actually received, but just don't make it to may app's code.

Here is some of the relevant VB.NET 3.5 code:

Private _UdbBuffer As Byte()
Private _ReceiveSocket As Socket
Private _NumReceived As Integer = 0
Private _StopWaitHandle As AutoResetEvent

Private Sub UdpListen()
    _StopWaitHandle = New AutoResetEvent(False)
    _UdpEndPoint = New Net.IPEndPoint(Net.IPAddress.Any, UDP_PORT_NUM)

    _ReceiveSocket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
    _ReceiveSocket.Bind(_UdpEndPoint)

    ReDim _UdbBuffer(10000)

    While Not _StopRequested
        Dim ir As IAsyncResult = _ReceiveSocket.BeginReceive(_UdbBuffer, 0, 10000, SocketFlags.None, AddressOf UdpReceive, Nothing)

        If Not _StopRequested Then
            Dim waitHandles() As WaitHandle = {_StopWaitHandle, ir.AsyncWaitHandle}
            If (WaitHandle.WaitAny(waitHandles) = 0) Then
                Exit While
            End If
        End If
    End While

    _ReceiveSocket.Close()
End Sub

Private Sub UdpReceive(ByVal ar As IAsyncResult)
    Dim len As Integer
    If ar.IsCompleted Then
        len = _ReceiveSocket.EndReceive(ar)
        Threading.Interlocked.Increment(_NumReceived)
        RaiseStatus("Got " & _NumReceived & " packets")
    End If
End Sub

I am sending the data as follows (not worried about the packet content for now):

For i as UShort = 0 to 999
   Dim b(500) as Byte
   _UdpClient.Send(b, b.Length)       
Next

If I add a small delay after each call to Send, more packets make it through; however since Wireshark says that they were all received anyways, it seems that the problem is in my receive code. I should mention that UdpListen is running on a separate thread.

Any idea why I am dropping packets? I also tried UdpClient.BeginReceive/EndReceive but had the same problem.

A second issue that bothers me is the global nature of the receive buffer when using Sockets and I am not sure if I don't process incoming packets quickly enough that the buffer will be overwritten. Not sure what to do about that just yet but I am open to suggestions.

Sep 26: Update


Based on the various, somewhat conflicting suggestions from replies to this and other posts, I made some changes to my code. Thanks to all who chimed in various bits; I now get all my packets from dial-up to Fast Ethernet. As you can see, it was my code at fault and not the fact that UDP drops packets (in fact I have not seen more than a tiny percentage of packets being dropped or out of order since my fixes).

Differences:

1) Replaced BeginReceive()/EndReceive() with BeginReceiveFrom()/EndReceiveFrom(). By itself this had no notible effect though.

2) Chaining BeginReceiveFrom() calls instead of waiting for the async handle to set. Not sure if any benefit here.

3) Explicitly set the Socket.ReceiveBufferSize to 500000 which is enough for 1 second worth of my data at Fast Ethernet speed. Turns out this is a different buffer than the one passed to BeginReceiveFrom(). This had the biggest benefit.

4) I also modified my send routine to wait a couple of ms after having sent a certain number of bytes to throttle based on expected bandwidth. This had a big benefit for my receiving code even though Wireshark said all my data still made it across even without this delay.

我没有最终使用单独的处理线程,因为据我所知,每次调用BeginReceiveFrom都会在新的工作线程上调用我的回调.这意味着我可以同时运行多个回调.这也意味着一旦我调用BeginReceiveFrom,我就有时间做我的事情(只要我不花太长时间并且放弃可用的工作线程).

Private Sub StartUdpListen()
    _UdpEndPoint = New Net.IPEndPoint(Net.IPAddress.Any, UDP_PORT_NUM)
    _ReceiveSocket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
    _ReceiveSocket.ReceiveBufferSize = 500000
    _ReceiveSocket.Bind(_UdpEndPoint)

    ReDim _Buffer(50000)

    _ReceiveSocket.BeginReceiveFrom(_Buffer, 0, _Buffer.Length, SocketFlags.None, _UdpEndPoint, AddressOf UdpReceive, Nothing)

End Sub

Private Sub UdpReceive(ByVal ar As IAsyncResult)
    Dim len As Integer = _ReceiveSocket.EndReceiveFrom(ar, _UdpEndPoint)
    Threading.Interlocked.Increment(udpreceived)

    Dim receiveBytes As Byte()
    ReDim receiveBytes(len - 1)
    System.Buffer.BlockCopy(_Buffer, 0, receiveBytes, 0, len)

    _ReceiveSocket.BeginReceiveFrom(_Buffer, 0, _UdbBuffer.Length, SocketFlags.None, _UdpEndPoint, AddressOf UdpReceive, Nothing)

    //' At this point, do what we need to do with the data in the receiveBytes buffer
    Trace.WriteLine("count=" & udpreceived)
End Sub

上面没有显示的是错误处理和处理UDP数据乱序或丢失.

我认为这可以处理我的问题,但是如果有人仍然看到上面的任何问题(或者我可以做得更好的话),我很乐意听到它.

推荐阅读
pan2502851807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有