Почему Socket.BeginReceive теряет пакеты из UDP?

Следующий код ожидает данных по UDP. У меня есть тестовая функция, которая отправляет 1000 пакетов (датаграмм?) По 500 байт каждый. Каждый раз, когда я запускаю тестовую функцию, получатель получает только первые несколько десятков пакетов, но отбрасывает остальные. Я просмотрел входящие сетевые данные с помощью Wireshark и увидел, что все 1000 пакетов действительно получены, но просто не делайте этого с кодом приложения.

Вот некоторые из соответствующих кодов VB.NET 3.5:

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

Я отправляю данные следующим образом (пока не беспокоюсь о содержимом пакета):

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

Если я добавлю небольшую задержку после каждого вызова Send, больше пакетов пройдет; однако, поскольку Wireshark говорит, что все они все равно были получены, похоже, что проблема в моем коде приема. Я должен упомянуть, что UdpListen работает в отдельном потоке.

Есть идеи, почему я отбрасываю пакеты? Я также пробовал UdpClient.BeginReceive / EndReceive, но имел ту же проблему.

Вторая проблема, которая меня беспокоит, - это глобальный характер приемного буфера при использовании сокетов, и я не уверен, что обрабатываю входящие пакеты достаточно быстро, чтобы буфер был перезаписан. Пока не знаю, что с этим делать, но я открыт для предложений.

26 сен: Обновление


Основываясь на различных, несколько противоречивых предложениях из ответов на этот и другие сообщения, я внес некоторые изменения в свой код. Спасибо всем, кто вмешивался в различные вопросы; Теперь я получаю все свои пакеты от коммутируемого доступа до Fast Ethernet. Как видите, виноват мой код, а не тот факт, что UDP отбрасывает пакеты (на самом деле я не видел более крошечного процента отбрасываемых или выходящих из строя пакетов с момента моих исправлений).

Отличия:

1) BeginReceive () / EndReceive () заменены на BeginReceiveFrom () / EndReceiveFrom (). Само по себе это не имело заметного эффекта.

2) Привязка вызовов BeginReceiveFrom () вместо ожидания установки асинхронного дескриптора. Не уверен, есть ли здесь какая-либо выгода.

3) Явно установите Socket.ReceiveBufferSize равным 500000, что достаточно для 1 секунды моих данных на скорости Fast Ethernet. Оказывается, это другой буфер, чем тот, который был передан в BeginReceiveFrom (). Это имело наибольшее преимущество.

4) Я также изменил свою процедуру отправки, чтобы подождать пару мс после того, как отправил определенное количество байтов для регулирования в зависимости от ожидаемой полосы пропускания. Это было большим преимуществом для моего принимающего кода, хотя Wireshark сказал, что все мои данные по-прежнему передавались даже без этой задержки.

Я НЕ использовал отдельный поток обработки, потому что, как я понимаю, каждый вызов 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, вышедшими из строя или отсутствующими.

Я думаю, что это решит мою проблему, но если кто-то по-прежнему видит что-то не так в приведенном выше (или что-то, что я могу сделать лучше) Я хотел бы услышать об этом.

7
задан Dan C 27 September 2010 в 02:55
поделиться