Je suis en phase de conception d'écrire une nouvelle application de service Windows qui accepte les connexions TCP / IP pour les connexions de longue durée (c'est-à-dire que ce n'est pas comme HTTP où il y a beaucoup de connexions courtes, mais plutôt un client se connecte et reste connecté pendant des heures ou des jours ou même des semaines).
Je recherche des idées sur la meilleure façon de concevoir l'architecture du réseau. Je vais avoir besoin de démarrer au moins un thread pour le service. J'envisage d'utiliser l'API Asynch (BeginRecieve, etc.) car je ne sais pas combien de clients j'aurai connecté à un moment donné (peut-être des centaines). Je ne veux certainement pas démarrer un fil pour chaque connexion.
Les données seront principalement transmises aux clients depuis mon serveur, mais il y aura parfois des commandes envoyées par les clients. Il s'agit principalement d'une application de surveillance dans laquelle mon serveur envoie périodiquement des données d'état aux clients.
Avez-vous des suggestions sur la meilleure façon de rendre cela aussi évolutif que possible? Flux de travail de base? Merci.
EDIT: Pour être clair, je recherche des solutions basées sur .net (C # si possible, mais n'importe quel langage .net fonctionnera)
NOTE DE PRIME: Pour recevoir la prime, j'attends plus qu'une simple réponse. J'aurais besoin d'un exemple fonctionnel d'une solution, soit comme un pointeur vers quelque chose que je pourrais télécharger ou un court exemple en ligne. Et il doit être basé sur .net et Windows (toute langue .net est acceptable)
EDIT: Je tiens à remercier tous ceux qui ont donné de bonnes réponses. Malheureusement, je ne pouvais en accepter qu'une seule et j'ai choisi d'accepter la méthode Begin / End, plus connue. La solution d'Esac est peut-être meilleure, mais elle est encore assez récente pour que je ne sache pas avec certitude comment cela fonctionnera.
J'ai voté pour toutes les réponses que je pensais bonnes, j'aimerais pouvoir faire plus pour vous les gars. Merci encore.
la source
Réponses:
J'ai écrit quelque chose de similaire dans le passé. D'après mes recherches d'il y a des années, l'écriture de votre propre implémentation de socket était le meilleur choix, en utilisant les sockets asynchrones. Cela signifiait que les clients qui ne faisaient vraiment rien nécessitaient en fait relativement peu de ressources. Tout ce qui se produit est géré par le pool de threads .net.
Je l'ai écrit en tant que classe qui gère toutes les connexions pour les serveurs.
J'ai simplement utilisé une liste pour contenir toutes les connexions client, mais si vous avez besoin de recherches plus rapides pour des listes plus grandes, vous pouvez l'écrire comme vous le souhaitez.
Vous avez également besoin de la prise qui écoute les connexions entrantes.
La méthode start démarre en fait le socket serveur et commence à écouter les connexions entrantes.
Je voudrais juste noter que le code de gestion des exceptions semble mauvais, mais la raison en est que j'avais un code de suppression d'exception afin que toutes les exceptions soient supprimées et renvoyées
false
si une option de configuration était définie, mais je voulais la supprimer pour souci de brièveté.Le _serverSocket.BeginAccept (new AsyncCallback (acceptCallback)), _serverSocket) ci-dessus définit essentiellement notre socket serveur pour appeler la méthode acceptCallback chaque fois qu'un utilisateur se connecte. Cette méthode s'exécute à partir du pool de threads .Net, qui gère automatiquement la création de threads de travail supplémentaires si vous avez de nombreuses opérations de blocage. Cela devrait gérer de manière optimale toute charge sur le serveur.
Le code ci-dessus vient de terminer d'accepter la connexion qui entre, des files d'attente
BeginReceive
qui est un rappel qui s'exécutera lorsque le client envoie des données, puis met en file d'attente la suivanteacceptCallback
qui acceptera la prochaine connexion client qui entrera.L'
BeginReceive
appel de méthode est ce qui indique à la socket ce qu'elle doit faire lorsqu'elle reçoit des données du client. PourBeginReceive
, vous devez lui donner un tableau d'octets, où il copiera les données lorsque le client envoie des données. LaReceiveCallback
méthode sera appelée, c'est ainsi que nous gérons la réception de données.EDIT: Dans ce modèle, j'ai oublié de mentionner que dans cette zone de code:
Ce que je ferais généralement, c'est dans le code que vous voulez, c'est de réassembler les paquets en messages, puis de les créer en tant que travaux sur le pool de threads. De cette façon, le BeginReceive du bloc suivant du client n'est pas retardé pendant l'exécution du code de traitement des messages.
Le rappel d'acceptation termine la lecture du socket de données en appelant end receive. Cela remplit le tampon fourni dans la fonction de début de réception. Une fois que vous avez fait ce que vous voulez là où j'ai laissé le commentaire, nous appelons la
BeginReceive
méthode suivante qui exécutera à nouveau le rappel si le client envoie plus de données. Maintenant, voici la partie vraiment délicate, lorsque le client envoie des données, votre rappel de réception peut être appelé uniquement avec une partie du message. Le remontage peut devenir très très compliqué. J'ai utilisé ma propre méthode et créé une sorte de protocole propriétaire pour ce faire. Je l'ai laissé de côté, mais si vous le demandez, je peux l'ajouter. Ce gestionnaire était en fait le morceau de code le plus compliqué que j'aie jamais écrit.La méthode d'envoi ci-dessus utilise en fait un
Send
appel synchrone , ce qui me convenait parfaitement en raison de la taille des messages et de la nature multithread de mon application. Si vous souhaitez envoyer à tous les clients, il vous suffit de parcourir la liste _sockets.La classe xConnection que vous voyez référencée ci-dessus est fondamentalement un simple wrapper pour une socket pour inclure le tampon d'octets, et dans mon implémentation, quelques extras.
Aussi pour référence, voici les
using
s que j'inclus car je suis toujours ennuyé quand ils ne sont pas inclus.J'espère que cela est utile, ce n'est peut-être pas le code le plus propre, mais cela fonctionne. Il y a aussi quelques nuances dans le code que vous devriez vous lasser de changer. D'une part, n'en avoir qu'un seul
BeginAccept
appelé à la fois. Il y avait un bogue .net très ennuyeux à ce sujet, il y a des années, donc je ne me souviens pas des détails.De plus, dans le
ReceiveCallback
code, nous traitons tout ce qui est reçu de la socket avant de mettre en file d'attente la prochaine réception. Cela signifie que pour un seul socket, nous ne sommes en fait jamais connectés qu'une seuleReceiveCallback
fois à un moment donné et nous n'avons pas besoin d'utiliser la synchronisation des threads. Cependant, si vous réorganisez ceci pour appeler la prochaine réception immédiatement après avoir extrait les données, ce qui peut être un peu plus rapide, vous devrez vous assurer que vous synchronisez correctement les threads.De plus, j'ai piraté une grande partie de mon code, mais j'ai laissé l'essentiel de ce qui se passe en place. Cela devrait être un bon début pour votre conception. Laissez un commentaire si vous avez d'autres questions à ce sujet.
la source
Send
méthodes se bloqueront indéfiniment des deux côtés, car personne ne lit les données d'entrée.Il existe de nombreuses façons d'effectuer des opérations réseau en C #. Tous utilisent différents mécanismes sous le capot et souffrent donc de problèmes de performances majeurs avec une concurrence élevée. Les opérations Begin * sont l'une de celles que beaucoup de gens confondent souvent comme étant le moyen le plus rapide / le plus rapide de faire du réseautage.
Pour résoudre ces problèmes, ils ont introduit l'ensemble de méthodes * Async: à partir de MSDN http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
La classe SocketAsyncEventArgs fait partie d'un ensemble d'améliorations de la classe System.Net.Sockets .. ::. Socket qui fournissent un autre modèle asynchrone qui peut être utilisé par des applications de socket hautes performances spécialisées. Cette classe a été spécialement conçue pour les applications de serveur réseau qui nécessitent des performances élevées. Une application peut utiliser le modèle asynchrone amélioré exclusivement ou uniquement dans les zones sensibles ciblées (par exemple, lors de la réception de grandes quantités de données).
La principale caractéristique de ces améliorations est d'éviter l'allocation et la synchronisation répétées des objets pendant les E / S de socket asynchrone à volume élevé. Le modèle de conception Begin / End actuellement implémenté par la classe System.Net.Sockets .. ::. Socket nécessite qu'un objet System .. ::. IAsyncResult soit alloué pour chaque opération de socket asynchrone.
Sous les couvertures, l'API * Async utilise les ports d'achèvement d'E / S, qui est le moyen le plus rapide d'effectuer des opérations de mise en réseau, voir http://msdn.microsoft.com/en-us/magazine/cc302334.aspx
Et juste pour vous aider, j'inclus le code source d'un serveur telnet que j'ai écrit en utilisant l'API * Async. J'inclus uniquement les parties pertinentes. À noter également, au lieu de traiter les données en ligne, j'opte plutôt pour les pousser dans une file d'attente sans verrouillage (attente gratuite) qui est traitée sur un thread séparé. Notez que je n'inclut pas la classe Pool correspondante qui est juste un simple pool qui créera un nouvel objet s'il est vide, et la classe Buffer qui est juste un tampon auto-extensible qui n'est pas vraiment nécessaire sauf si vous recevez un indéterministe quantité de données. Si vous souhaitez plus d'informations, n'hésitez pas à m'envoyer un PM.
la source
Il y avait une très bonne discussion sur le TCP / IP évolutif utilisant .NET écrit par Chris Mullins de Coversant, malheureusement, il semble que son blog ait disparu de son emplacement précédent, donc je vais essayer de rassembler ses conseils de mémoire (quelques commentaires utiles de lui apparaissent dans ce fil: C ++ vs C #: Développement d'un serveur IOCP hautement évolutif )
Tout d'abord, notez que l'utilisation
Begin/End
et lesAsync
méthodes de laSocket
classe utilisent les ports d'achèvement IO (IOCP) pour fournir l'évolutivité. Cela fait une bien plus grande différence (lorsqu'elle est utilisée correctement; voir ci-dessous) pour l'évolutivité que celle des deux méthodes que vous choisissez réellement pour implémenter votre solution.Les articles de Chris Mullins étaient basés sur l'utilisation
Begin/End
, qui est celle avec laquelle j'ai personnellement l'expérience. Notez que Chris a mis au point une solution basée sur cela qui a évolué jusqu'à 10 000 connexions client simultanées sur une machine 32 bits avec 2 Go de mémoire, et jusqu'à 100 000 sur une plate-forme 64 bits avec suffisamment de mémoire. D'après ma propre expérience avec cette technique (bien que loin de ce genre de charge), je n'ai aucune raison de douter de ces chiffres indicatifs.IOCP versus thread par connexion ou primitives de sélection
La raison pour laquelle vous souhaitez utiliser un mécanisme qui utilise IOCP sous le capot est qu'il utilise un pool de threads Windows de très bas niveau qui ne réveille aucun thread tant qu'il n'y a pas de données réelles sur le canal IO que vous essayez de lire ( notez que l'IOCP peut également être utilisé pour les E / S de fichiers). L'avantage de ceci est que Windows n'a pas besoin de basculer vers un thread uniquement pour constater qu'il n'y a pas encore de données, ce qui réduit le nombre de changements de contexte que votre serveur devra effectuer au strict minimum requis.
Les changements de contexte sont ce qui tuera définitivement le mécanisme de «thread par connexion», bien que ce soit une solution viable si vous ne traitez que quelques dizaines de connexions. Ce mécanisme est cependant loin d'être «évolutif».
Considérations importantes lors de l'utilisation de l'IOCP
Mémoire
Tout d'abord, il est essentiel de comprendre que l'IOCP peut facilement entraîner des problèmes de mémoire sous .NET si votre implémentation est trop naïve. Chaque
BeginReceive
appel IOCP entraînera "l'épinglage" du tampon dans lequel vous lisez. Pour une bonne explication des raisons pour lesquelles il s'agit d'un problème, voir: Weblog de Yun Jin: OutOfMemoryException and Pinning .Heureusement, ce problème peut être évité, mais il nécessite un peu de compromis. La solution suggérée est d'allouer une grande
byte[]
mémoire tampon au démarrage de l'application (ou à proximité), d'au moins 90 Ko ou plus (à partir de .NET 2, la taille requise peut être plus grande dans les versions ultérieures). La raison à cela est que les allocations de mémoire volumineuses se retrouvent automatiquement dans un segment de mémoire non compacté (le tas d'objets volumineux) qui est effectivement épinglé automatiquement. En allouant une grande mémoire tampon au démarrage, vous vous assurez que ce bloc de mémoire inamovible se trouve à une «adresse faible» où il ne gênera pas et ne provoquera pas de fragmentation.Vous pouvez ensuite utiliser des décalages pour segmenter cette grande mémoire tampon en zones distinctes pour chaque connexion qui a besoin de lire certaines données. C'est là qu'un compromis entre en jeu; puisque ce tampon doit être pré-alloué, vous devrez décider de la quantité d'espace tampon dont vous avez besoin par connexion et de la limite supérieure que vous souhaitez définir sur le nombre de connexions que vous souhaitez mettre à l'échelle (ou, vous pouvez implémenter une abstraction qui peut allouer des tampons épinglés supplémentaires une fois que vous en avez besoin).
La solution la plus simple consisterait à affecter à chaque connexion un seul octet à un décalage unique dans ce tampon. Ensuite, vous pouvez faire un
BeginReceive
appel pour qu'un seul octet soit lu, et effectuer le reste de la lecture à la suite du rappel que vous obtenez.En traitement
Lorsque vous obtenez le rappel de l'
Begin
appel que vous avez effectué, il est très important de réaliser que le code du rappel s'exécutera sur le thread IOCP de bas niveau. Il est absolument essentiel que vous évitiez de longues opérations dans ce rappel. L'utilisation de ces threads pour un traitement complexe tuera votre évolutivité tout aussi efficacement que l'utilisation de 'thread-per-connection'.La solution suggérée consiste à utiliser le rappel uniquement pour mettre en file d'attente un élément de travail pour traiter les données entrantes, qui seront exécutées sur un autre thread. Évitez toute opération potentiellement bloquante à l'intérieur du rappel afin que le thread IOCP puisse retourner dans son pool le plus rapidement possible. Dans .NET 4.0, je suggérerais que la solution la plus simple consiste à générer un
Task
, en lui donnant une référence au socket client et une copie du premier octet déjà lu par l'BeginReceive
appel. Cette tâche est ensuite chargée de lire toutes les données du socket qui représentent la demande que vous traitez, de l'exécuter, puis de passer un nouvelBeginReceive
appel pour mettre à nouveau le socket en file d'attente pour IOCP. Avant .NET 4.0, vous pouvez utiliser ThreadPool ou créer votre propre implémentation de file d'attente de travail threadée.Résumé
Fondamentalement, je suggérerais d'utiliser l'exemple de code de Kevin pour cette solution, avec les avertissements ajoutés suivants:
BeginReceive
est déjà `` épinglé ''BeginReceive
ne fait rien de plus que mettre en file d'attente une tâche pour gérer le traitement réel des données entrantesLorsque vous faites cela, je ne doute pas que vous puissiez répliquer les résultats de Chris en augmentant potentiellement des centaines de milliers de clients simultanés (avec le bon matériel et une implémentation efficace de votre propre code de traitement bien sûr;)
la source
Vous avez déjà obtenu l'essentiel de la réponse via les exemples de code ci-dessus. L'utilisation d'un fonctionnement d'E / S asynchrone est absolument la voie à suivre ici. Async IO est la façon dont le Win32 est conçu en interne pour évoluer. Les meilleures performances possibles que vous pouvez obtenir sont obtenues en utilisant les ports d'achèvement, en liant vos sockets aux ports d'achèvement et en ayant un pool de threads en attente de l'achèvement du port d'achèvement. La sagesse commune est d'avoir 2 à 4 threads par CPU (cœur) en attente de fin. Je recommande vivement de passer en revue ces trois articles de Rick Vicik de l'équipe Windows Performance:
Lesdits articles couvrent principalement l'API Windows native, mais ils sont indispensables pour quiconque tente de comprendre l'évolutivité et les performances. Ils ont également des mémoires sur le côté géré des choses.
La deuxième chose à faire est de vous assurer de consulter le livre Amélioration des performances et de l'évolutivité des applications .NET , disponible en ligne. Vous trouverez des conseils pertinents et valables sur l'utilisation des threads, des appels asynchrones et des verrous au chapitre 5. Mais les vrais joyaux se trouvent au chapitre 17 où vous trouverez des avantages tels que des conseils pratiques sur le réglage de votre pool de threads. Mes applications ont eu de sérieux problèmes jusqu'à ce que j'aie ajusté les maxIothreads / maxWorkerThreads selon les recommandations de ce chapitre.
Vous dites que vous voulez faire un serveur TCP pur, donc mon prochain point est faux. Cependant , si vous vous trouvez coincé et utilisez la classe WebRequest et ses dérivés, sachez qu'un dragon garde cette porte: le ServicePointManager . Il s'agit d'une classe de configuration qui n'a qu'un seul but dans la vie: ruiner vos performances. Assurez-vous de libérer votre serveur du ServicePoint.ConnectionLimit artificiel imposé ou votre application ne sera jamais mise à l'échelle (je vous laisse découvrir par vous-même quelle est la valeur par défaut ...). Vous pouvez également reconsidérer la politique par défaut d'envoi d'un en-tête Expect100Continue dans les requêtes http.
Maintenant, à propos de l'API gérée par socket de base, les choses sont assez faciles du côté de l'envoi, mais elles sont beaucoup plus complexes du côté de la réception. Afin d'obtenir un débit et une mise à l'échelle élevés, vous devez vous assurer que le socket n'est pas contrôlé par le flux car vous n'avez pas de tampon posté pour la réception. Idéalement, pour des performances élevées, vous devez publier 3-4 tampons et publier de nouveaux tampons dès que vous en récupérez un ( avant de traiter celui qui est revenu) afin de vous assurer que le socket a toujours un endroit où déposer les données provenant du réseau. Vous verrez pourquoi vous ne pourrez probablement pas y parvenir sous peu.
Une fois que vous avez fini de jouer avec l'API BeginRead / BeginWrite et que vous commencez le travail sérieux, vous vous rendrez compte que vous avez besoin de sécurité sur votre trafic, c'est-à-dire. Authentification NTLM / Kerberos et chiffrement du trafic, ou au moins protection contre la falsification du trafic. Pour ce faire, vous utilisez le System.Net.Security.NegotiateStream intégré (ou SslStream si vous avez besoin de traverser des domaines disparates). Cela signifie qu'au lieu de compter sur des opérations asynchrones de socket direct, vous vous ferez aux opérations asynchrones AuthenticatedStream. Dès que vous obtenez un socket (soit de la connexion sur le client, soit de l'acceptation sur le serveur), vous créez un flux sur le socket et le soumettez pour authentification, en appelant BeginAuthenticateAsClient ou BeginAuthenticateAsServer. Une fois l'authentification terminée (au moins votre coffre-fort de la folie native InitiateSecurityContext / AcceptSecurityContext ...), vous ferez votre autorisation en vérifiant la propriété RemoteIdentity de votre flux authentifié et en effectuant la vérification ACL que votre produit doit prendre en charge. Après cela, vous enverrez des messages en utilisant BeginWrite et vous les recevrez avec BeginRead. C'est le problème dont je parlais auparavant: vous ne pourrez pas publier plusieurs tampons de réception, car les classes AuthenticateStream ne le prennent pas en charge. L'opération BeginRead gère en interne tous les E / S jusqu'à ce que vous ayez reçu une trame entière, sinon elle ne pourrait pas gérer l'authentification du message (décrypter la trame et valider la signature sur la trame). Cependant, d'après mon expérience, le travail effectué par les classes AuthenticatedStream est assez bon et ne devrait pas avoir de problème avec cela. C'est à dire. vous devriez pouvoir saturer le réseau GB avec seulement 4-5% de CPU. Les classes AuthenticatedStream vous imposeront également les limitations de taille de trame spécifiques au protocole (16k pour SSL, 12k pour Kerberos).
Cela devrait vous aider à démarrer sur la bonne voie. Je ne vais pas publier de code ici, il y a un très bon exemple sur MSDN . J'ai réalisé de nombreux projets comme celui-ci et j'ai pu passer à environ 1000 utilisateurs connectés sans problème. Au-dessus de cela, vous devrez modifier les clés de registre pour permettre au noyau d'avoir plus de poignées de socket. et assurez-vous de déployer sur un OS serveur , c'est-à-dire W2K3 et non XP ou Vista (c'est-à-dire OS client), cela fait une grande différence.
BTW assurez-vous que si vous avez des opérations de bases de données sur le serveur ou le fichier IO, vous utilisez également la saveur asynchrone pour eux, ou vous viderez le pool de threads en un rien de temps. Pour les connexions SQL Server, assurez-vous d'ajouter le «traitement asynchrone = true» à la chaîne de connexion.
la source
J'ai un tel serveur en cours d'exécution dans certaines de mes solutions. Voici une explication très détaillée des différentes façons de le faire dans .net: Rapprochez-vous du fil avec des sockets hautes performances dans .NET
Dernièrement, j'ai cherché des moyens d'améliorer notre code et je me pencherai sur ceci: " Améliorations des performances de socket dans la version 3.5 " qui a été inclus spécifiquement "pour une utilisation par les applications qui utilisent des E / S réseau asynchrones pour atteindre les meilleures performances".
«La principale caractéristique de ces améliorations est d'éviter l'allocation et la synchronisation répétées d'objets pendant les E / S de socket asynchrone à volume élevé. Le modèle de conception Début / Fin actuellement implémenté par la classe Socket pour les E / S de socket asynchrone nécessite un système. L’objet IAsyncResult doit être alloué pour chaque opération de socket asynchrone. »
Vous pouvez continuer à lire si vous suivez le lien. Je testerai personnellement leur exemple de code demain pour le comparer à ce que j'ai.
Edit: Ici, vous pouvez trouver du code de travail pour le client et le serveur en utilisant le nouveau 3.5 SocketAsyncEventArgs afin que vous puissiez le tester en quelques minutes et parcourir le code. C'est une approche simple, mais c'est la base pour démarrer une implémentation beaucoup plus large. Aussi cet article d'il y a près de deux ans dans MSDN Magazine est une lecture intéressante.
la source
Avez-vous envisagé d'utiliser simplement une liaison TCP réseau WCF et un modèle de publication / abonnement? WCF vous permettrait de vous concentrer [principalement] sur votre domaine plutôt que sur la plomberie.
Il existe de nombreux exemples WCF et même un framework de publication / abonnement disponible dans la section de téléchargement d'IDesign qui peut être utile: http://www.idesign.net
la source
Je m'interroge sur une chose:
Pourquoi donc? Windows pourrait gérer des centaines de threads dans une application depuis au moins Windows 2000. Je l'ai fait, il est vraiment facile de travailler avec si les threads n'ont pas besoin d'être synchronisés. Surtout étant donné que vous faites beaucoup d'E / S (donc vous n'êtes pas lié au processeur, et beaucoup de threads seraient bloqués sur le disque ou la communication réseau), je ne comprends pas cette restriction.
Avez-vous testé la méthode multi-thread et trouvé qu'il manquait quelque chose? Avez-vous l'intention d'avoir également une connexion à la base de données pour chaque thread (cela tuerait le serveur de base de données, c'est donc une mauvaise idée, mais c'est facilement résolu avec une conception à 3 niveaux). Craignez-vous d'avoir des milliers de clients au lieu de centaines, et ensuite vous aurez vraiment des problèmes? (Bien que j'essaye un millier de threads ou même dix mille si j'avais plus de 32 Go de RAM - encore une fois, étant donné que vous n'êtes pas lié au processeur, le temps de changement de thread ne devrait absolument pas être pertinent.)
Voici le code - pour voir à quoi cela ressemble, allez sur http://mdpopescu.blogspot.com/2009/05/multi-threaded-server.html et cliquez sur l'image.
Classe de serveur:
Programme principal du serveur:
Classe de client:
Programme principal du client:
la source
Utiliser Async IO (
BeginRead
, etc.) intégré à .NET est une bonne idée si vous pouvez obtenir tous les détails correctement. Lorsque vous configurez correctement vos poignées de socket / fichier, il utilisera l'implémentation IOCP sous-jacente du système d'exploitation, permettant à vos opérations de se terminer sans utiliser de threads (ou, dans le pire des cas, en utilisant un thread qui, je pense, provient du pool de threads IO du noyau à la place. du pool de threads de .NET, ce qui permet de réduire la congestion du pool de threads.)Le principal problème est de vous assurer que vous ouvrez vos sockets / fichiers en mode non bloquant. La plupart des fonctions pratiques par défaut (comme
File.OpenRead
) ne le font pas, vous devrez donc écrire les vôtres.L'une des autres principales préoccupations est la gestion des erreurs - gérer correctement les erreurs lors de l'écriture de code d'E / S asynchrone est beaucoup, beaucoup plus difficile que de le faire en code synchrone. Il est également très facile de se retrouver avec des conditions de concurrence et des blocages même si vous n'utilisez peut-être pas directement les threads, vous devez donc en être conscient.
Si possible, vous devriez essayer d'utiliser une bibliothèque pratique pour faciliter le processus d'E / S asynchrones évolutives.
Le runtime de coordination de la concurrence de Microsoft est un exemple de bibliothèque .NET conçue pour soulager la difficulté de faire ce type de programmation. Cela a l'air génial, mais comme je ne l'ai pas utilisé, je ne peux pas dire à quel point il évoluerait.
Pour mes projets personnels nécessitant des E / S réseau ou disque asynchrones, j'utilise un ensemble d'outils de concurrence / E / S .NET que j'ai construits au cours de l'année écoulée, appelé Squared.Task . Il est inspiré par des bibliothèques comme imvu.task et twisted , et j'ai inclus quelques exemples de travail dans le référentiel qui font des E / S réseau. Je l'ai également utilisé dans quelques applications que j'ai écrites - la plus grande publication publique étant NDexer (qui l'utilise pour les E / S de disque sans thread). La bibliothèque a été écrite sur la base de mon expérience avec imvu.task et dispose d'un ensemble de tests unitaires assez complets, je vous encourage donc vivement à l'essayer. Si vous rencontrez des problèmes, je serais ravi de vous aider.
À mon avis, sur la base de mon expérience avec l'utilisation d'E / S asynchrones / sans thread au lieu de threads, c'est une entreprise intéressante sur la plate-forme .NET, tant que vous êtes prêt à gérer la courbe d'apprentissage. Cela vous permet d'éviter les tracas d'évolutivité imposés par le coût des objets Thread, et dans de nombreux cas, vous pouvez complètement éviter l'utilisation de verrous et de mutex en utilisant avec soin les primitives de concurrence comme Futures / Promises.
la source
J'ai utilisé la solution de Kevin mais il dit que la solution manque de code pour le réassemblage des messages. Les développeurs peuvent utiliser ce code pour le réassemblage des messages:
la source
Vous pouvez trouver un bel aperçu des techniques sur la page des problèmes C10k .
la source
Vous pouvez essayer d'utiliser un framework appelé ACE (Adaptive Communications Environment) qui est un framework C ++ générique pour les serveurs réseau. Il s'agit d'un produit très solide et mature, conçu pour prendre en charge des applications à haut volume et à haute fiabilité jusqu'à la qualité des télécommunications.
Le cadre traite un assez large éventail de modèles de concurrence et en a probablement un adapté à votre application. Cela devrait faciliter le débogage du système car la plupart des problèmes de concurrence désagréables ont déjà été résolus. Le compromis ici est que le framework est écrit en C ++ et n'est pas la base de code la plus chaleureuse et la plus moelleuse. D'autre part, vous bénéficiez d'une infrastructure réseau testée de qualité industrielle et d'une architecture hautement évolutive prête à l'emploi.
la source
J'utiliserais SEDA ou une bibliothèque de threads légère (erlang ou linux plus récent voir évolutivité NTPL côté serveur ). Le codage async est très lourd si votre communication ne l'est pas :)
la source
Eh bien, les sockets .NET semblent fournir select () - c'est le meilleur pour gérer les entrées. Pour la sortie, j'aurais un pool de threads d'écriture de socket écoutant sur une file d'attente de travail, acceptant le descripteur / objet de socket dans le cadre de l'élément de travail, vous n'avez donc pas besoin d'un thread par socket.
la source
J'utiliserais les méthodes AcceptAsync / ConnectAsync / ReceiveAsync / SendAsync qui ont été ajoutées dans .Net 3.5. J'ai fait un benchmark et ils sont environ 35% plus rapides (temps de réponse et débit) avec 100 utilisateurs qui envoient et reçoivent constamment des données.
la source
aux personnes qui copient la réponse acceptée, vous pouvez réécrire la méthode acceptCallback, en supprimant tous les appels de _serverSocket.BeginAccept (new AsyncCallback (acceptCallback), _serverSocket); et placez-le dans une clause finally {}, de cette façon:
vous pouvez même supprimer la première capture car son contenu est le même mais c'est une méthode de modèle et vous devez utiliser une exception typée pour mieux gérer les exceptions et comprendre ce qui a causé l'erreur, alors implémentez simplement ces captures avec du code utile
la source
Je recommanderais de lire ces livres sur ACE
pour avoir des idées sur les modèles vous permettant de créer un serveur efficace.
Bien que ACE soit implémenté en C ++, les livres couvrent de nombreux modèles utiles qui peuvent être utilisés dans n'importe quel langage de programmation.
la source
Vous n'obtiendrez pas le plus haut niveau d'évolutivité si vous optez uniquement pour .NET. Les pauses GC peuvent entraver la latence.
Les E / S superposées sont généralement considérées comme l'API la plus rapide de Windows pour la communication réseau. Je ne sais pas si c'est la même chose que votre API Asynch. N'utilisez pas select car chaque appel doit vérifier chaque socket ouvert au lieu d'avoir des rappels sur les sockets actifs.
la source
Vous pouvez utiliser l'infrastructure open source Push Framework pour le développement de serveurs hautes performances. Il est construit sur IOCP et convient aux scénarios push et à la diffusion de messages.
http://www.pushframework.com
la source