Il seguente codice attende i dati su UDP. Ho una funzione di test che invia 1000 pacchetti (datagrammi?) Di 500 byte ciascuno. Ogni volta che eseguo la funzione di test, il ricevitore riceve solo le prime dozzine di pacchetti ma cade il resto. Ho guardato i dati della rete in entrata usando Wireshark e vedo che tutti i 1000 pacchetti sono stati effettivamente ricevuti, ma semplicemente non ce l'hanno con il codice dell'app.Perché Socket.BeginRece perde i pacchetti da UDP?
Ecco alcuni dei VB.NET 3.5 codice rilevante:
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
sto trasmettendo i dati nel seguente modo (non sono preoccupato per il contenuto del pacchetto, per ora):
For i as UShort = 0 to 999
Dim b(500) as Byte
_UdpClient.Send(b, b.Length)
Next
Se io aggiungi un piccolo ritardo dopo ogni chiamata a Invia, più pacchetti riescono a passare; tuttavia dal momento che Wireshark dice che sono stati tutti ricevuti comunque, sembra che il problema sia nel mio codice di ricezione. Dovrei ricordare che UdpListen è in esecuzione su un thread separato.
Qualche idea del motivo per cui ho interrotto i pacchetti? Ho anche provato UdpClient.BeginReceive/EndReceive ma ho avuto lo stesso problema.
Un secondo problema che mi dà fastidio è la natura globale del buffer di ricezione quando si utilizzano Socket e non sono sicuro se non elaboro i pacchetti in ingresso abbastanza rapidamente da sovrascrivere il buffer. Non sono sicuro di cosa fare al riguardo, ma sono aperto a suggerimenti.
26 settembre: aggiornamento
Sulla base dei vari suggerimenti, un po 'contrastanti da risposte a questo e altri messaggi, ho fatto alcune modifiche al mio codice. Grazie a tutti quelli che suonavano in vari pezzi; Ora prendo tutti i miei pacchetti da dial-up a Fast Ethernet. Come potete vedere, è stato il mio codice in errore e non il fatto che UDP lasci cadere i pacchetti (in effetti non ho visto più di una piccola percentuale di pacchetti rilasciati o fuori servizio dopo le mie correzioni).
Differenze:
1) sostituiti BeginReceive()/EndReceive() con BeginReceiveFrom()/EndReceiveFrom(). Di per sé questo non ha avuto alcun effetto notabile però.
2) Concatenare BeginReceiveFrom() chiama invece di attendere l'impostazione della maniglia asincrona. Non sono sicuro se qualche vantaggio qui.
3) Impostare Socket.ReceiveBufferSize su 500000 in modo esplicito, che è sufficiente per 1 secondo dei miei dati alla velocità Fast Ethernet. Risulta che questo è un buffer diverso da quello passato a BeginReceiveFrom(). Questo ha avuto il più grande beneficio.
4) Ho modificato anche la mia routine di invio per attendere un paio di ms dopo aver inviato un certo numero di byte al throttle in base alla larghezza di banda prevista. Ciò ha avuto un grande vantaggio per il mio codice ricevente, anche se Wireshark ha detto che tutti i miei dati sono riusciti a superarlo anche senza questo ritardo.
NON ho finito per utilizzare un thread di elaborazione separato perché, a quanto ho capito, ogni chiamata a BeginReceiveFrom invocherà il mio callback su un nuovo thread di lavoro. Ciò significa che posso avere più di una callback in esecuzione allo stesso tempo. Significa anche che una volta chiamo BeginReceiveFrom ho tempo per fare le mie cose (a patto che non impieghi troppo tempo ed esaurisca i thread di lavoro disponibili).
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
Ciò che non è indicato sopra è l'errore di gestione e gestione dei dati UDP che è fuori servizio o mancante.
Penso che questo gestisca il mio problema, ma se qualcuno ancora vede qualcosa di sbagliato con quanto sopra (o qualcosa che potrei fare meglio) mi piacerebbe sentirne parlare.
Cosa intendi con 'Un secondo problema che mi dà fastidio è la natura globale del buffer di ricezione quando si usano Socket'? Ce n'è uno per socket. Niente di globale a riguardo. – EJP
"Non sono sicuro se non elaboro i pacchetti in entrata abbastanza rapidamente da sovrascrivere il buffer". Non verrà sovrascritto. I pacchetti in arrivo verranno * eliminati * se il buffer è pieno. – EJP
@EJP, quel commento applicato al mio bit di codice originale. Dato come stavo usando il buffer, potrebbe essere sovrascritto dal prossimo gruppo di dati mentre lo stavo leggendo. Il nuovo codice non presenta questo problema poiché esegue una copia di blocco del buffer (circa 200 byte) prima di tornare ad ascoltare per ulteriori dati. –