2010-02-18 4 views
9

Lorsque j'appelle BeginSend sur un socket, je passe un délégué qui sera appelé (par un thread différent) lorsque les données ont été envoyées. Que se passerait-il si j'appelais BeginSend une autre fois alors que la première n'a pas encore été 'rappelée'?combien de demandes de socket asynchrone peuvent se produire sur la même socket?

Quel est le comportement correct pour envoyer des données? faire BeginSend, et sur le rappel faire EndSend et commencer un autre envoi? Ou est-il réellement sage d'avoir plusieurs BeginSends travaillant en même temps?

c'est la page BeginSend sur MSDN qui ne donne pas une réponse à cette question: BeginSend msdn

Répondre

16

En tant que O.K.W. dit, plusieurs appels BeginSend en attente fonctionnera bien. Vous avez probablement besoin de garder à l'esprit certaines choses cependant. Tout d'abord, s'il s'agit d'un socket TCP, il s'agit toujours d'un seul flux de données d'égal à égal.

Ensuite, si tous vos appels BeginSend se produisent sur le même thread alors le résultat sera que le pair reçoit les données dans l'ordre des appels. Si vos appels BeginSend se produisent à partir de threads différents, les données peuvent arriver dans n'importe quel ordre car il y aura probablement une condition de concurrence entre chacun des envois. Cela peut ou non vous importer (cela dépend si vous envoyez des messages discrets, complets à chaque envoi ou non).Troisièmement, si vous utilisez TCP et envoyez plus rapidement que le code à l'autre extrémité du socket peut recevoir, vous pouvez remplir la fenêtre TCP et les piles TCP va commencer à effectuer le contrôle de flux sur votre flux de données. Si vous continuez à émettre des appels BeginSend, vous risquez de vous retrouver dans une situation où vos rappels prendront plus de temps à être appelés car la pile TCP sur votre serveur met en file d'attente les données à envoyer (vous recevez uniquement le rappel une fois les données envoyées Le contrôle de flux basé sur la fenêtre TCP empêchera l'envoi de nouvelles données jusqu'à ce que la fenêtre TCP ne soit plus «pleine», c'est-à-dire que le pair a envoyé des ACK pour certaines données in flight).

Vous pouvez alors entrer dans une situation où vous utilisez des ressources sur l'expéditeur d'une manière incontrôlable (vous émettez un BeginSend et ne savez pas quand il sera terminé et chaque envoi utilise de la mémoire pour le tampon envoyé et potentiellement non-paged pool vers le bas dans le code Winsock ... Non-paged pool est une ressource à l'échelle du système et est assez rare sur les systèmes d'exploitation Vista antérieures et certains pilotes mal comportés peuvent bleu écran la boîte si non-paged pool est faible ou épuisé. mémoire en mémoire et il y a une autre limite du système sur le nombre de pages mémoire verrouillées

En raison de ces problèmes, il est généralement préférable d'implémenter votre propre flux de niveau de protocole. contrôle qui limite le nombre de BeginSend appels qui peuvent être en attente à tout moment (en utilisant un ACK de niveau de protocole, peut-être) ou de travailler avec le contrôle de flux TCP Window et utiliser l'achèvement d'un envoi en attente pour émettre un nouvel envoi file d'attente de données à envoyer dans votre propre mémoire et avoir un contrôle complet sur les ressources utilisées et ce que vous faites si vous mettez en file d'attente "trop" de données. Voir mon blog ici pour plus de détails à ce sujet: http://www.serverframework.com/asynchronousevents/2011/06/tcp-flow-control-and-asynchronous-writes.html

Voir cette réponse: what happens when tcp/udp server is publishing faster than client is consuming? pour plus d'informations sur le contrôle de flux TCP Window et ce qui se passe avec E/S chevauchée (en C++ terre) lorsque vous l'ignorez et question trop En résumé, l'envoi simultané de plusieurs appels BeginSend simultanés est le moyen d'optimiser le flux de données TCP, mais vous devez vous assurer de ne pas envoyer "trop ​​vite" car une fois que vous consommez des ressources dans un manière que vous ne pouvez pas contrôler et qui est potentiellement fatale pour la machine sur laquelle votre code est en cours d'exécution. Donc, ne laissez pas un nombre illimité d'appels BeginSend être exceptionnels et, idéalement, profilez la boîte pour vous assurer que vous n'épuisez pas les ressources du système.

+1

@Lens, c'est un bon +1 –

+0

@lens: J'ai un fenêtrage intégré dans le protocole que j'envoie (smpp) donc leur envoi ne serait jamais trop rapide. Le point de désordre que vous soulevez serait un problème cependant. – Toad

+0

@Len, mal orthographié votre nom dans mon commentaire ci-dessus, mes excuses :) –

4
I sur la base

ce que je read here, il semble comme avoir plusieurs BeginSend simultanée est faisable.

Extrait:

  1. Vous pouvez la file d'attente de plusieurs BeginSends en même temps. Vous n'avez pas besoin de verrouiller
  2. Si vous ne créez pas une méthode de rappel, comment savez-vous quand l'envoi a réussi? Vous pouvez ignorer le succès - plus comme un feu et oublier - méthode, mais alors vous au moins besoin de savoir quand il est sûr de terminer votre programme.
  3. L'autre chose que vous pouvez faire est d'utiliser le IAsyncResult que vous obtenez de BeginSend. Vous pouvez utiliser WaitHandle pour attendre la fin de l'opération. Mais cela bat tout le but de en utilisant Async en premier lieu.
  4. Ma recommandation est d'utiliser un rappel, et assurez-vous que le rappel est appelé et le nombre de octets que vous avez envoyé est réellement envoyé et terminez gracieusement l'opération d'envoi.

Mise à jour:
Essayé BeginSend simultanée sur la base des codes on MSDN et les données sont envoyées sans exception. Cependant, gardez à l'esprit que la connexion pour le même socket doit être ouverte avant. Simultanée BeginConnect ne fonctionnera pas.

+0

Donc, cela multiplexe plusieurs flux de données indépendants sur un seul socket? Est-ce la réponse de Microsoft à SCTP? –

+0

intéressant. Mais est-ce plus rapide que d'envoyer un paquet par paquet avec seulement un BeginSend en attente? Je demande ceci puisque je fais un serveur qui devrait avoir une bonne exécution, et il pourrait être que l'on devrait avoir au moins X attendant BeginSends pour avoir le débit décent. En ce moment, je ne saurais pas – Toad

+0

Robert - Non, pas du tout. Si le socket est un socket TCP, vous avez un seul flux. L'émission de plusieurs appels 'BeginSend' vous permet simplement de mettre en file d'attente plusieurs écritures dans le flux de façon asynchrone. –

Questions connexes