7
votes

Pourquoi Socket.BeginReceive Perding Packets de UDP?

Le code suivant attend les données sur UDP. J'ai une fonction de test qui envoie 1000 paquets (datagrammes?) De 500 octets chacun. Chaque fois que j'exécute la fonction de test, le récepteur ne reçoit que les premières douzaines de paquets, mais goutte le reste. J'ai examiné les données du réseau entrant à l'aide de Wireshark et je vois que les 1000 paquets sont effectivement reçus, mais ne vous rendez tout simplement pas au code de l'application.

Voici une partie du code VB.NET 3.5 correspondant: P> xxx pré>

J'envoie les données comme suit (non inquiet sur le contenu du paquet pour l'instant): P>

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


4 commentaires

Qu'entendez-vous par «un deuxième problème qui me dérange-t-il est la nature globale du tampon de réception lorsque vous utilisez des sockets? Il y a un par socket. Rien de global à ce sujet.


"Je ne suis pas sûr que je ne traite pas assez rapidement les paquets entrants pour que le tampon soit écrasé". Ce ne sera pas écrasé. Les paquets entrants seront abandonnés si le tampon est plein.


@EJP, ce commentaire s'appliquait à mon bit de code d'origine. Étant donné que j'utilisais le tampon, il pourrait être écrasé par le prochain groupe de données pendant que je le lisais. Le nouveau code n'a pas ce problème car il effectue une copie de bloc de la mémoire tampon (environ 200 octets) avant de revenir à l'écoute pour plus de données.


La méthode 'Raisestatus' est très Heavy. Vous ne voulez pas l'appeler pour chaque paquet reçu.


5 Réponses :


4
votes

UDP peut déposer des paquets chaque fois qu'il aime. Dans ce cas, vous pouvez essayer de définir un tampon de prise beaucoup plus grand de prise au récepteur pour atténuer.


5 commentaires

Le problème est que, selon Wireshark, tous les paquets le rendus à l'ordinateur distant, donc je ne pense pas qu'ils sont effectivement abandonnés par UDP. De plus, chaque appel à endreceive obtient 500 octets et lorsque je regarde le contenu du tampon, seuls 500 des 10000 octets sont remplis, donc je ne pense pas que cela déborde. Faites-vous référence à un tampon différent?


UDP ne s'arrête pas sur l'ordinateur distant. Il inclut la pile UDP dans l'ordinateur distant et le tampon de réception de socket fait partie de cela. S'il n'y a pas de place, le paquet est tombé. Alors, faites-la assez grosse, ou faites suffisamment de code de réception assez rapidement ou faites suffisamment de code d'envoi. Et même alors vous pouvez toujours obtenir des paquets abandonnés. Donc, votre application doit simplement faire face. Si vous souhaitez la fiabilité, utilisez TCP.


Mon exemple de code ci-dessus envoie 500 000 octets dans environ 1 seconde. Êtes-vous en train de dire que le tampon que je passe à DébutReceive / BeginReceivefrom doit être aussi grand? Semble plutôt excessif, d'autant plus que chaque fois que j'appelle final, le reste de ce tampon semble inutilisé. Y a-t-il un tampon différent, je devrais être initialiser?


Je parle du tampon de réception de la prise. Il s'agit d'une structure de données dans le noyau dont vous pouvez contrôler la taille via l'API. Par défaut, il est 8K sous Windows, absurdement petit.


Merci, c'est le tampon que j'ai fini par trouver et en augmentant à 500 000 dans mon élever udpate.



1
votes

désolé. Je ne comprends pas votre code. Pourquoi enveloppez-vous des méthodes asynchrones dans une boucle? Vous devriez commencer par lire sur la manipulation asynchrone.

UDP garantit uniquement que les messages complets sont reçus, rien d'autre. Un message peut être supprimé ou arriver à une commande incorrecte. Vous devez appliquer votre propre algorithme pour gérer cela. Il y en a un appelé répétition sélective par exemple.

Ensuite, vous ne devez pas traiter chaque message avant de recevoir un nouveau si vous vous attendez à recevoir des messages beaucoup de messages dans une courte période. Au lieu de cela, en faisez chaque message entrant et avoir un fil séparé qui prend soin du traitement.

Troisième: les messages UDP doivent être traités avec BEDREReceiveFromfromfromfrom pour le traitement asynchrone ou la réception de from pour synchrones.


2 commentaires

Merci pour vos suggestions. C'est dans une boucle parce que je veux lire plus d'un paquet. Lorsque l'événement ASYNC survient, il signale la poignée d'attente pour continuer et je commence à rechercher le paquet suivant pendant que je traite le courant actuel. C'est l'une des deux techniques que j'ai vues pour une utilisation efficace du processeur (l'autre option consiste à chaîner l'appel BettReceive dans le rappel lui-même). Aussi, j'ai essayé de remplacer mon code avec BeintureReceivefrom / Endreceivefrom mais avait toujours le même problème. Avez-vous des suggestions de code d'échantillon?


J'ajouterai également que le problème se produit avec le code de test de rappel coupé à rien de plus que d'incrémenter un compteur afin de traiter de manière très minimum.



1
votes

Comme indiqué ci-dessus, UDP n'est pas un protocole fiable. C'est un protocole sans connexion qui met beaucoup moins de frais généraux sur des paquets IP, que TCP ne le fait. UDP est assez bon pour de nombreuses fonctions (y compris les messages de diffusion et de multidiffusion), mais il ne peut pas être utilisé pour une livraison de messages fiable. Si le tampon est surchargé, le pilote de réseau ne sera que des datagrammes. Si vous avez besoin de communication basée sur des messages avec une livraison fiable ou si vous vous attendez à ce que de nombreux messages soient envoyés, vous pouvez consulter notre msgconnect Produit (version libre open source disponible), qui fournit un transfert de données basé sur des messages sur les sockets ainsi que sur UDP.


3 commentaires

Merci, s'il vous plaît voir ma réponse ci-dessus car il ne me semble pas que je dépose des paquets selon Wireshark. Je sais que UDP n'est pas fiable, mais 80% des paquets sont tombés sur un réseau local lorsque d'autres applications de test UDP fonctionne bien me dit que j'ai un problème avec mon code.


@Danc: Les paquets seront supprimés par le pilote de réception s'il n'y a pas de tampon en attente de les recevoir. C'est à dire. Ils ont atteint la carte réseau, mais pas votre application.


Oui, alors que JGAuffin a pointé ci-dessus, vous ne devez pas traiter les messages immédiatement. Passez-les à un autre thread si vous devez traiter des messages à la volée. Enfin, il peut y avoir une certaine question avec DébutReceive en particulier, mais je ne suis pas au courant des spécificités des opérations UDP dans ce scénario - nous utilisons des opérations synchrones dans le transport UDP de msgConnect



0
votes

Essayez une approche plus simple. Demandez au récepteur dans un fil séparé qui ressemblerait à ceci comme ceci dans le pseudo code: xxx pré>

dans une autre boucle de fil sur la taille de la file d'attente pour déterminer si vous avez reçu des paquets. Si vous avez reçu des paquets les processez, sinon dormez. P>

do

if queue count > 0

  do 

    rcvdata = queue dequeue

    process the rcvdata

  loop while queue count > 0

else

  sleep

end if

loop while socket_is_open???


2 commentaires

Merci, je garderai cela à l'esprit lorsque je commence à faire quelque chose avec le tampon (bien que j'utilise plus probablement le signal au lieu de dormir car les données peuvent venir à tout moment). Cependant, mon exemple ci-dessus comptage littéralement les paquets reçus afin que le temps de traitement ne soit pas un facteur, le devrait-il?


Notez que j'utilise la version bloquante de la réception, qui bloque ce thread sauf s'il existe des données. Lorsqu'il y a des données, cela le place simplement dans une file d'attente que l'autre thread traite. Le fil qui traite les données doit avoir un moyen de renoncer au contrôle lorsqu'il n'y a pas de données à traiter. Il y a des années, j'ai écrit une application UDP qui a testé la bande passante à l'aide de la méthode décrite. Je n'ai pas eu de problème de test 100Mbps FDX commutateurs à la vitesse du fil (200 Mbps).



0
votes

Comme d'autres l'ont déjà dit, UDP n'est pas un mécanisme de livraison garanti. Donc, même si WireShark vous montre que les paquets ont été envoyés, cela ne signifie pas que les paquets ont été reçus à la destination. La pile TCP / IP sur l'hôte récepteur pourrait toujours supprimer les datagrammes.

Vous pouvez confirmer que cela se produit en surveillant les compteurs de performance suivants dans perfmon.exe.

ordinateur local \ ipv4 \ datagrammes reçus Rejeté

ou

ordinateur local \ ipv6 \ datagrammes reçus Rejeté

Si vous utilisez le protocole IPv6.

Vous pouvez également essayer de réduire la vitesse à laquelle vous envoyez des datagrammes et de voir si cela réduit la vitesse de défausse.


1 commentaires

J'avais WireShark en cours d'exécution sur l'expéditeur et le récepteur. Il a montré que tous les paquets rèventont sur le fil même avec l'ancienne version. Selon System.net.NetworkInformation.IPV4Interfacetatistics, aucun paquets n'a été chuté de l'une ou l'autre extrémité. Je ne sais pas comment ou à quelle couche Wireshark obtient ses données de, mais ma conclusion est que mon code ne tirait pas suffisamment du réseau (même si j'étais dans une boucle assez serrée). L'augmentation de la socket.ReceiveBufferSize semblait me donner plus de temps pour que tous les paquets soient traités. Réduire le taux d'envoi a également amélioré les choses.