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