Question

Quelle est la manière normale dont les personnes qui écrivent du code réseau dans Delphi utilisent les E/S de socket asynchrone superposées de style Windows ?

Voici mes recherches antérieures sur cette question :

Le Inde les composants semblent entièrement synchrones.D'un autre côté, bien que l'unité ScktComp utilise WSAAsyncSelect, elle asynchronise uniquement une application de socket multiplexée de style BSD.Vous êtes vidé dans un seul rappel d'événement, comme si vous veniez de revenir de select() dans une boucle, et devez effectuer vous-même toute la navigation dans la machine à états.

La situation .NET est considérablement plus agréable, avec Socket.BeginRead / Socket.EndRead, où la suite est transmise directement à Socket.BeginRead, et c'est là que vous reprenez.Une continuation codée comme une fermeture a évidemment tout le contexte dont vous avez besoin, et plus encore.

Était-ce utile?

La solution

Autres conseils

J'ai constaté qu'Indy, bien qu'il s'agisse d'un concept plus simple au début, est difficile à gérer en raison de la nécessité de supprimer les sockets pour libérer les threads à la fin de l'application.De plus, la bibliothèque Indy a cessé de fonctionner après une mise à niveau du correctif du système d'exploitation.ScktComp fonctionne bien pour mon application.

@Roddy - Les sockets synchrones ne sont pas ce que je recherche.Graver un thread entier pour une connexion éventuellement de longue durée signifie que vous limitez le nombre de connexions simultanées au nombre de threads que votre processus peut contenir.Étant donné que les threads utilisent beaucoup de ressources - espace d'adressage de pile réservé, mémoire de pile réservée et transitions du noyau pour les changements de contexte - ils ne s'adaptent pas lorsque vous devez prendre en charge des centaines de connexions, et encore moins des milliers ou plus.

Quelle est la façon normale dont les gens écrivant du code réseau à Delphi utilisent des E / S de socket asynchrones de style Windows?

Eh bien, Indy est la bibliothèque « standard » pour les E/S de socket depuis longtemps maintenant – et elle est basée sur le blocage des sockets.Cela signifie que si vous souhaitez un comportement asynchrone, vous utilisez des threads supplémentaires pour connecter/lire/écrire des données.À mon avis, c'est en fait un avantage majeur, car il n'est pas nécessaire de gérer un quelconque type de navigation dans la machine à états, ni de s'inquiéter des procédures de rappel ou d'autres choses similaires.Je trouve que la logique de mon fil de « lecture » est moins encombrée et beaucoup plus portable que ne le permettraient les sockets non bloquants.

Inde 9 a été pour l'essentiel à l'épreuve des bombes, rapide et fiable pour nous.Cependant, le passage à Indy 10 pour Tiburon me pose un peu d'inquiétude.

@Mike:"...la nécessité de tuer les sockets pour libérer les threads...".

Cela a fait aller "hein?" Jusqu'à ce que je me souvienne de notre bibliothèque de threading utilise une technique basée sur des exceptions pour tuer les threads «d'attente» en toute sécurité.Nous appelons QueueUserAPC pour mettre en file d'attente une fonction qui déclenche une exception C++ (NON dérivée de la classe Exception) qui ne doit être interceptée que par notre procédure wrapper de thread.Tous les destructeurs sont appelés afin que les threads se terminent tous proprement et soient rangés à la sortie.

"Les sockets synchrones ne sont pas ce que je recherche."

Compris - mais je pense que dans ce cas, la réponse à votre question initiale est qu'il n'y a tout simplement pas de Delphi idiome pour les sockets asynchrones IO, car il s'agit en fait d'une exigence hautement spécialisée et rare.

En guise de problème secondaire, vous pourriez trouver ces liens intéressants.Ils sont tous les deux un peu vieux et plus *nxy que Windows.La seconde implique que - dans le bon environnement - les threads ne seront peut-être pas aussi mauvais que vous le pensez.

Le problème du C10K

Pourquoi les événements sont une mauvaise idée (pour les serveurs à haute concurrence)

@Chris Miller - Ce que vous avez déclaré dans votre réponse est factuellement inexact.

L'asynchrone de style message Windows, disponible via WSAAsyncSelect, est en effet en grande partie une solution de contournement faute d'un modèle de thread approprié dans les jours Win 3.x.

.NET Begin/End, cependant, est pas en utilisant des fils supplémentaires.Au lieu de cela, il utilise des E/S superposées, en utilisant l'argument supplémentaire sur WSASend / WSARecv, en particulier la routine d'achèvement superposée, pour spécifier la continuation.

Cela signifie que le style .NET exploite la prise en charge des E/S asynchrones du système d'exploitation Windows pour éviter brûler un thread en bloquant sur un socket.

Étant donné que les threads sont généralement coûteux (sauf si vous spécifiez une très petite taille de pile pour CreateThread), le blocage des threads sur les sockets vous empêchera d'évoluer jusqu'à 10 000 connexions simultanées.

C'est pourquoi il est important d'utiliser les E/S asynchrones si vous souhaitez évoluer, et aussi pourquoi .NET est pas, je le répète, c'est pas, simplement "en utilisant des threads, [...] juste gérés par le Framework".

@Roddy - J'ai déjà lu les liens que vous pointez, ils sont tous deux référencés dans la présentation de Paul Tyma "Des milliers de threads et blocage des E/S - L'ancienne façon d'écrire des serveurs Java est à nouveau nouvelle".

Cependant, certaines choses qui ne ressortent pas nécessairement de la présentation de Paul sont qu'il a spécifié -Xss:48k à la JVM au démarrage et qu'il suppose que l'implémentation NIO de la JVM est efficace pour qu'elle soit valide. comparaison.

Indy le fait pas spécifiez une taille de pile également réduite et étroitement contrainte.Il n'y a aucun appel à BeginThread (la routine de création de thread Delphi RTL, que vous devez utiliser dans de telles situations) ou à CreateThread (l'appel brut WinAPI) dans la base de code Indy.

La taille de pile par défaut est stockée dans le PE, et pour le compilateur Delphi, elle est par défaut de 1 Mo d'espace d'adressage réservé (l'espace est validé page par page par le système d'exploitation en morceaux de 4 Ko ;en fait, le compilateur doit générer du code pour toucher les pages s'il y a > 4 Ko de locaux dans une fonction, car l'extension est contrôlée par des défauts de page, mais uniquement pour la page la plus basse (de garde) de la pile).Cela signifie que vous allez manquer d'espace d'adressage après un maximum de 2 000 threads simultanés gérant les connexions.

Maintenant, vous pouvez modifier la taille de pile par défaut dans le PE à l'aide de la directive {$M minStackSize [,maxStackSize]}, mais cela affectera tous threads, y compris le thread principal.J'espère que vous ne faites pas beaucoup de récursion, car 48 Ko ou (similaire) ne représentent pas beaucoup d'espace.

Maintenant, si Paul a raison sur la non-performance des E/S asynchrones pour Windows en particulier, je n'en suis pas sûr à 100 % - je devrais le mesurer pour en être certain.Ce que je sais, cependant, c'est que les arguments selon lesquels la programmation threadée est plus facile que la programmation asynchrone basée sur les événements présentent un risque. fausse dichotomie.

Le code asynchrone ne le fait pas besoin être basé sur des événements ;il peut être basé sur la continuation, comme c'est le cas dans .NET, et si vous spécifiez une fermeture comme continuation, vous obtenez gratuitement l'état maintenu pour vous.De plus, la conversion du code de style thread linéaire en code asynchrone de style passage de continuation peut être rendue mécanique par un compilateur (la transformation CPS est mécanique), il n'y a donc pas non plus de coût en termes de clarté du code.

Il existe des composants de socket IOCP (ports de complétion) gratuits : http://www.torry.net/authorsmore.php?id=7131 (code source inclus)

"Par Naberegnyh Sergey N..Serveur de socket haute performance basé sur le port de réalisation de Windows et avec l'utilisation d'extensions de socket Windows.IPv6 pris en charge."

je l'ai trouvé en cherchant de meilleurs composants/bibliothèque pour réarchitecturer mon petit serveur de messagerie instantanée.Je ne l'ai pas encore essayé mais ça a l'air bien codé comme première impression.

Indy utilise des sockets synchrones car c'est un moyen de programmation plus simple.Le blocage de socket asynchrone était quelque chose ajouté à la pile Winsock à l'époque de Windows 3.x.Windows 3.x ne prenait pas en charge les threads et là, vous ne pouviez pas effectuer d'E/S de socket sans threads.Pour plus d'informations sur les raisons pour lesquelles Indy utilise le modèle de blocage, veuillez consulter Cet article.

Les appels .NET Socket.BeginRead/EndRead utilisent des threads, ils sont simplement gérés par le Framework plutôt que par vous.

@Roddy, Indy 10 est fourni avec Delphi depuis Delphi 2006.J'ai trouvé que la migration d'Indy 9 vers Indy 10 était une tâche simple.

Avec les classes ScktComp, vous devez utiliser un serveur ThreadBlocking plutôt qu'un type de serveur NonBlocking.Utilisez l'événement OnGetThread pour transmettre le paramètre ClientSocket à un nouveau thread de votre conception.Une fois que vous avez instancié une instance héritée de TServerClientThread, vous créerez une instance de TWinSocketStream (à l'intérieur du thread) que vous pourrez utiliser pour lire et écrire sur le socket.Cette méthode vous évite d'essayer de traiter les données dans le gestionnaire d'événements.Ces threads peuvent exister uniquement pendant la courte période nécessaire pour lire ou écrire, ou rester en attente pendant toute la durée nécessaire pour être réutilisés.

Le sujet de l’écriture d’un serveur socket est assez vaste.Il existe de nombreuses techniques et pratiques que vous pouvez choisir de mettre en œuvre.La méthode de lecture et d'écriture sur le même socket avec TServerClientThread est simple et convient aux applications simples.Si vous avez besoin d'un modèle pour une haute disponibilité et une simultanéité élevée, vous devez examiner des modèles tels que le modèle Proactor.

Bonne chance!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top