Frage

Wie verwenden Leute, die Netzwerkcode in Delphi schreiben, normalerweise überlappende asynchrone Socket-E/A im Windows-Stil?

Hier ist meine bisherige Recherche zu dieser Frage:

Der Indy Komponenten scheinen völlig synchron zu sein.Andererseits verwendet die ScktComp-Einheit zwar WSAAsyncSelect, asynchronisiert jedoch im Grunde nur eine Multiplex-Socket-App im BSD-Stil.Sie werden in einem einzelnen Event-Callback abgelegt, als wären Sie gerade von select() in einer Schleife zurückgekehrt und müssten die gesamte Zustandsmaschinennavigation selbst durchführen.

Die .NET-Situation ist mit Socket.BeginRead / Socket.EndRead wesentlich schöner, wo die Fortsetzung direkt an Socket.BeginRead übergeben wird und Sie dort weitermachen.Eine als Abschluss codierte Fortsetzung enthält offensichtlich den gesamten Kontext, den Sie benötigen, und noch mehr.

War es hilfreich?

Lösung

Für asynchrone Dinge versuchen Sie es mit ICS

http://www.overbyte.be/frame_index.html?redirTo=/products/ics.html

Andere Tipps

Ich habe festgestellt, dass Indy zwar am Anfang ein einfacheres Konzept ist, aber umständlich zu verwalten ist, da beim Beenden der Anwendung Sockets beendet werden müssen, um Threads freizugeben.Außerdem funktionierte die Indy-Bibliothek nach einem Betriebssystem-Patch-Upgrade nicht mehr.ScktComp funktioniert gut für meine Anwendung.

@Roddy – Synchrone Sockets sind nicht das, was ich suche.Das Brennen eines ganzen Threads im Interesse einer möglicherweise langlebigen Verbindung bedeutet, dass Sie die Anzahl gleichzeitiger Verbindungen auf die Anzahl der Threads beschränken, die Ihr Prozess enthalten kann.Da Threads viele Ressourcen verbrauchen – reservierten Stack-Adressraum, festgeschriebenen Stack-Speicher und Kernel-Übergänge für Kontextwechsel – können sie nicht skaliert werden, wenn Sie Hunderte von Verbindungen unterstützen müssen, geschweige denn Tausende oder mehr.

Wie können Personen mit dem Schreiben von Netzwerkcode in Delphi überlappte asynchrone Socket-I/O-Style-Art-Stile Windows-Stil verwenden?

Nun, Indy ist seit langem die „Standard“-Bibliothek für Socket-I/O – und sie basiert auf dem Blockieren von Sockets.Das heißt, wenn Sie asynchrones Verhalten wünschen, verwenden Sie zusätzliche Threads, um Daten zu verbinden/lesen/schreiben.Meiner Meinung nach ist dies tatsächlich ein großer Vorteil, da keine Notwendigkeit besteht, irgendeine Art von Zustandsmaschinennavigation zu verwalten oder sich um Rückrufprozesse oder ähnliches zu kümmern.Ich finde, dass die Logik meines „Lese“-Threads weniger überladen und viel portabler ist, als es nicht blockierende Sockets erlauben würden.

Indy 9 verlief für uns überwiegend bombensicher, schnell und zuverlässig.Allerdings bereitet mir der Wechsel von Tiburon zu Indy 10 ein wenig Sorgen.

@Mike:„…die Notwendigkeit, Sockets zu töten, um Threads freizugeben…“.

Das machte "Huh?" bisWir nennen QueueUserAPC um eine Funktion in die Warteschlange zu stellen, die eine C++-Ausnahme auslöst (NICHT von der Klasse Exception abgeleitet), die nur von unserer Thread-Wrapper-Prozedur abgefangen werden sollte.Alle Destruktoren werden aufgerufen, damit alle Threads sauber enden und auf dem Weg nach draußen aufgeräumt werden.

„Synchronsteckdosen sind nicht das, was ich suche.“

Verstanden – aber ich denke, in diesem Fall lautet die Antwort auf Ihre ursprüngliche Frage, dass es einfach kein Delphi gibt Idiom für asynchrone Socket-E/A, da es sich tatsächlich um eine hochspezialisierte und ungewöhnliche Anforderung handelt.

Nebenbei bemerkt könnten Sie diese Links interessant finden.Sie sind beide etwas älter und moderner als Windows.Die zweite impliziert, dass Threads in der richtigen Umgebung möglicherweise nicht so schlecht sind, wie Sie denken.

Das C10K-Problem

Warum Ereignisse eine schlechte Idee sind (für Server mit hoher Parallelität)

@Chris Miller – Was Sie in Ihrer Antwort angegeben haben, ist sachlich ungenau.

Die Asynchronisierung im Windows-Nachrichtenstil, wie sie über WSAAsyncSelect verfügbar ist, stellt in der Tat größtenteils eine Problemumgehung dar, da in den Tagen von Win 3.x kein geeignetes Threading-Modell vorhanden war.

.NET Begin/End ist jedoch nicht Verwendung zusätzlicher Threads.Stattdessen werden überlappende E/A-Vorgänge verwendet, wobei das zusätzliche Argument für WSASend/WSARecv, insbesondere die überlappende Abschlussroutine, verwendet wird, um die Fortsetzung anzugeben.

Das bedeutet, dass der .NET-Stil die asynchrone E/A-Unterstützung des Windows-Betriebssystems nutzt vermeiden Brennen eines Threads durch Blockieren eines Sockets.

Da Threads im Allgemeinen teuer sind (es sei denn, Sie geben für CreateThread eine sehr kleine Stapelgröße an), können Sie nicht auf 10.000 gleichzeitige Verbindungen skalieren, wenn Threads auf Sockets blockieren.

Aus diesem Grund ist es wichtig, dass asynchrone E/A verwendet wird, wenn Sie skalieren möchten, und auch .NET ist dies nicht, ich wiederhole, ist nicht, einfach „unter Verwendung von Threads, [...] nur vom Framework verwaltet“.

@Roddy – Ich habe die Links, auf die Sie verweisen, bereits gelesen. Beide beziehen sich auf Paul Tymas Präsentation „Tausende Threads und blockierende E/A – Die alte Art, Java-Server zu schreiben, ist wieder neu“.

Einige der Dinge, die Pauls Präsentation jedoch nicht unbedingt ins Auge fallen, sind, dass er beim Start der JVM -Xss:48k angegeben hat und dass er davon ausgeht, dass die NIO-Implementierung der JVM effizient ist, damit sie gültig ist Vergleich.

Indy tut es nicht Geben Sie eine ähnlich verkleinerte und streng eingeschränkte Stapelgröße an.In der Indy-Codebasis gibt es keine Aufrufe von BeginThread (der Delphi-RTL-Thread-Erstellungsroutine, die Sie in solchen Situationen verwenden sollten) oder CreateThread (dem rohen WinAPI-Aufruf).

Die Standardstapelgröße wird im PE gespeichert und beträgt für den Delphi-Compiler standardmäßig 1 MB reservierten Adressraum (der Speicherplatz wird vom Betriebssystem Seite für Seite in 4-KB-Blöcken festgeschrieben);Tatsächlich muss der Compiler Code generieren, um Seiten zu berühren, wenn es mehr als 4 KB lokaler Seiten in einer Funktion gibt, da die Erweiterung durch Seitenfehler gesteuert wird, jedoch nur für die unterste (Schutz-)Seite im Stapel.Das bedeutet, dass Ihnen nach maximal 2.000 gleichzeitigen Threads, die Verbindungen verarbeiten, der Adressraum ausgehen wird.

Jetzt können Sie die Standardstapelgröße im PE mit der Direktive {$M minStackSize [,maxStackSize]} ändern, aber das hat Auswirkungen alle Threads, einschließlich des Hauptthreads.Ich hoffe, Sie machen nicht viel Rekursion, denn 48K oder (ähnlich) ist nicht viel Platz.

Ob Paul Recht hat, was die mangelnde Leistung von asynchronen I/O-Vorgängen insbesondere für Windows betrifft, bin ich mir nicht 100 % sicher – ich müsste es messen, um sicherzugehen.Was ich jedoch weiß, ist, dass Argumente, dass Thread-Programmierung einfacher sei als asynchrone, ereignisbasierte Programmierung, eine Rolle spielen falsche Dichotomie.

Asynchroner Code funktioniert nicht brauchen ereignisbasiert sein;Es kann fortsetzungsbasiert sein, wie es in .NET der Fall ist, und wenn Sie einen Abschluss als Fortsetzung angeben, wird der Status kostenlos für Sie verwaltet.Darüber hinaus kann die Konvertierung von Code im linearen Thread-Stil in asynchronen Code im Continuation-Passing-Stil durch einen Compiler mechanisch erfolgen (die CPS-Transformation ist mechanisch), sodass auch keine Kosten für die Klarheit des Codes entstehen müssen.

Es gibt kostenlose IOCP-Socket-Komponenten (Completion Ports): http://www.torry.net/authorsmore.php?id=7131 (Quellcode enthalten)

„Von Naberegnyh Sergey N..Hoch Performance-Socket-Server basierend auf Windows-Vervollständigungsport und mit der Verwendung von Windows Socket-Erweiterungen.IPv6-Schnittstelle abgestützt."

Ich habe es gefunden, als ich nach besseren Komponenten/Bibliotheken gesucht habe, um meinen kleinen Instant-Messaging-Server neu zu strukturieren.Ich habe es noch nicht ausprobiert, aber für den ersten Eindruck sieht es gut codiert aus.

Indy verwendet synchrone Sockets, weil es eine einfachere Art der Programmierung ist.Die asynchrone Socket-Blockierung wurde bereits in den Tagen von Windows 3.x zum Winsock-Stack hinzugefügt.Windows 3.x unterstützte keine Threads und dort war keine Socket-I/O ohne Threads möglich.Weitere Informationen darüber, warum Indy das Blockierungsmodell verwendet, finden Sie unter Dieser Artikel.

Die .NET Socket.BeginRead/EndRead-Aufrufe verwenden Threads, sie werden nur vom Framework und nicht von Ihnen verwaltet.

@Roddy, Indy 10 wird seit Delphi 2006 mit Delphi gebündelt.Ich fand, dass die Migration von Indy 9 auf Indy 10 eine unkomplizierte Aufgabe ist.

Bei den ScktComp-Klassen müssen Sie einen ThreadBlocking-Server anstelle eines NonBlocking-Servertyps verwenden.Verwenden Sie das OnGetThread-Ereignis, um den ClientSocket-Parameter an einen neuen Thread Ihres Entwurfs zu übergeben.Sobald Sie eine geerbte Instanz von TServerClientThread instanziiert haben, erstellen Sie eine Instanz von TWinSocketStream (innerhalb des Threads), die Sie zum Lesen und Schreiben in den Socket verwenden können.Mit dieser Methode müssen Sie nicht versuchen, Daten im Event-Handler zu verarbeiten.Diese Threads können nur für den kurzen Zeitraum vorhanden sein, der zum Lesen oder Schreiben erforderlich ist, oder für die Dauer bestehen bleiben, um wiederverwendet zu werden.

Das Thema des Schreibens eines Socket-Servers ist ziemlich umfangreich.Es gibt viele Techniken und Praktiken, die Sie umsetzen können.Die Methode zum Lesen und Schreiben in denselben Socket im TServerClientThread ist einfach und eignet sich für einfache Anwendungen.Wenn Sie ein Modell für hohe Verfügbarkeit und hohe Parallelität benötigen, müssen Sie sich Muster wie das Proactor-Muster ansehen.

Viel Glück!

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top