El siguiente código espera datos sobre UDP. Tengo una función de prueba que envía 1000 paquetes (datagramas?) De 500 bytes cada uno. Cada vez que ejecuto la función de prueba, el receptor recibe solo las primeras docenas de paquetes, pero descarta el resto. Miré los datos de la red entrante usando Wireshark y veo que los 1000 paquetes realmente se reciben, pero simplemente no lo hacen al código de la aplicación may.¿Por qué Socket.BeginReceive pierde paquetes de UDP?
Aquí tienes lo relevante VB.NET 3.5 código:
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
estoy enviando los datos de la siguiente manera (no está preocupado por el contenido de los paquetes, por ahora):
For i as UShort = 0 to 999
Dim b(500) as Byte
_UdpClient.Send(b, b.Length)
Next
Si yo agregue un pequeño retraso después de cada llamada a Enviar, más paquetes lo hacen; sin embargo, dado que Wireshark dice que todos fueron recibidos de todos modos, parece que el problema está en mi código de recepción. Debo mencionar que UdpListen se ejecuta en un hilo separado.
¿Alguna idea de por qué estoy dejando caer paquetes? También probé UdpClient.BeginReceive/EndReceive pero tuve el mismo problema.
Un segundo problema que me molesta es la naturaleza global del búfer de recepción al usar Sockets y no estoy seguro si no proceso los paquetes entrantes lo suficientemente rápido como para sobrescribir el búfer. Todavía no estoy seguro de qué hacer con eso, pero estoy abierto a sugerencias.
26 de septiembre: Actualización
Sobre la base de las diversas sugerencias, un tanto contradictorias de las respuestas a este y otros mensajes, he hecho algunos cambios en mi código. Gracias a todos los que intervinieron en varios fragmentos; Ahora obtengo todos mis paquetes de acceso telefónico a Fast Ethernet. Como puede ver, fue mi código el que falló y no el hecho de que UDP omita paquetes (de hecho, no he visto más que un pequeño porcentaje de paquetes caídos o fuera de servicio desde mis correcciones).
Diferencias:
1) Sustituido BeginReceive()/EndReceive() con BeginReceiveFrom()/EndReceiveFrom(). Sin embargo, esto no tuvo ningún efecto notorio.
2) Encadenar llamadas BeginReceiveFrom() en lugar de esperar a que se establezca el async handle. No estoy seguro de si hay algún beneficio aquí.
3) Establecer explícitamente el Socket.ReceiveBufferSize en 500000 que es suficiente para 1 segundo de mis datos a la velocidad de Fast Ethernet. Resulta que este es un buffer diferente al que pasó a BeginReceiveFrom(). Esto tuvo el mayor beneficio.
4) También modifiqué mi rutina de envío para esperar un par de ms después de haber enviado un cierto número de bytes para acelerar según el ancho de banda esperado. Esto tuvo un gran beneficio para mi código de recepción a pesar de que Wireshark dijo que todos mis datos seguían llegando incluso sin este retraso.
NO terminé usando un hilo de procesamiento separado porque, según tengo entendido, cada llamada a BeginReceiveFrom invocará mi devolución de llamada en un nuevo hilo de trabajo. Esto significa que puedo ejecutar más de una devolución de llamada al mismo tiempo. También significa que una vez que llamo a BeginReceiveFrom tengo tiempo para hacer mis cosas (siempre que no me tome demasiado tiempo y agote los hilos de trabajo disponibles).
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
Lo que no se muestra arriba es el manejo de errores y el manejo de datos UDP que están fuera de servicio o que faltan.
Creo que esto maneja mi problema, pero si alguien todavía ve algo mal con lo de arriba (o algo que podría hacer mejor) Me encantaría saberlo.
¿Qué quiere decir por 'Una segunda cuestión que me molesta es la naturaleza global del búfer de recepción cuando se utiliza sockets'? Hay uno por socket. Nada global al respecto. – EJP
"No estoy seguro de que si no procesan los paquetes entrantes con suficiente rapidez que el búfer se sobrescribe". No será sobrescrito. Los paquetes entrantes se * soltarán * si el buffer está lleno. – EJP
@EJP, ese comentario se aplicó a mi código de código original. Dado que estaba usando el búfer, podría sobrescribirse con el siguiente grupo de datos mientras lo estaba leyendo. El nuevo código no tiene este problema ya que hace una copia en bloque del búfer (alrededor de 200 bytes) antes de volver para escuchar más datos. –