Was ist der idiomatische Weise, das zu tun asynchrone socket-Programmierung in Delphi?
Was ist die normale Art, wie Menschen schreiben, Netzwerk-code in Delphi verwenden Windows-Stil überlappt asynchrone socket-I/O?
Hier ist mein Stand der Forschung in dieser Frage:
Den Indy Komponenten scheinen ganz synchron. Auf der anderen Seite, während die unit ScktComp nicht WSAAsyncSelect, es ist im Grunde nur asynchronizes ein BSD-style-Multiplex-Buchse app. Sie bekommen gedumpten in einem einzigen Ereignis-Rückruf, als wenn Sie war gerade von select() in einer Schleife, und alle die state-machine für die navigation selbst.
Den .NETTO-situation ist wesentlich schöner, mit Sockel.BeginRead /- Buchse.EndRead, wo die Fortsetzung ist bestanden, direkt für die Steckdose.BeginRead, und das ist, wo Sie Holen wieder. Eine Fortsetzung codiert als Verschluss hat offensichtlich alle Rahmen, die Sie brauchen, und mehr.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Für async-Zeug versuchen, ICS
http://www.overbyte.be/frame_index.html?redirTo=/products/ics.html
Habe ich festgestellt, dass Indy, während ein einfacheres Konzept am Anfang, ist umständlich zu verwalten, aufgrund der Notwendigkeit, zu töten-sockets an freien threads, die auf Beendigung der Anwendung. Außerdem hatte ich die Indy library aufhören zu arbeiten, nach einem OS-upgrade-patch. ScktComp funktioniert gut für meine Anwendung.
@Roddy - Synchron-buchsen sind nicht das, was ich bin nach. Das brennen einer ganzen thread wegen der möglicherweise langen Dauer der Verbindung bedeutet, dass Sie begrenzt die Anzahl der gleichzeitigen verbindungen, die Anzahl der threads an, die Ihren Prozess enthalten können. Da threads verwenden eine Menge von Ressourcen - reserviert-stack Adressraum, begangen stack-Speicher und kernel-übergänge für context-switches - Sie nicht skalieren, wenn Sie brauchen, um Unterstützung für Hunderte von verbindungen, viel weniger die Tausende oder mehr.
Gut, Indy hat schon die "standard" - Bibliothek für socket-I/O für eine lange Zeit jetzt - und es basiert auf blockierende sockets. Dies bedeutet, wenn Sie möchten, asynchrone Verhalten, Sie verwenden die zusätzlichen thread(s) zu schließen/Lesen/schreiben von Daten. Meiner Meinung nach ist das eigentlich ein großer Vorteil, da es keine Notwendigkeit, Sie zu verwalten jede Art von state-machine für navigation, oder sich sorgen über die callback ausgelöst wird oder ähnliches. Ich finde die Logik meiner 'zu Lesen' - thread ist weniger unübersichtlich und viel mehr tragbar als nicht-blockierende sockets erlauben würde.
Indy 9 wurde meist bombenfest, schnell und zuverlässig für uns. Jedoch das bewegen zu Indy 10 für Tiburon ist was mich ein wenig beunruhigt.
Diesem aus gehen, "huh?", bis ich mich erinnerte unsere threading-Bibliothek nutzt eine Ausnahme-basierte Technik, um zu töten 'waiting' - threads sicher. Wir nennen QueueUserAPC, um eine Funktion in eine Warteschlange, die wirft eine C++ - Ausnahme (NICHT abgeleitet von der Klasse Exception), die sollten nur gefangen werden von unseren thread-wrapper-Prozedur. Alle Destruktoren aufgerufen werden, so dass die threads alle zu kündigen, sauber und ordentlich auf dem Weg nach draußen.
Verstanden - aber ich denke, in diesem Fall die Antwort auf deine ursprüngliche Frage ist, dass es gerade kein Delphi idiom für async-IO-socket-denn es ist tatsächlich eine hoch spezialisierte und ungewöhnliche Anforderung.
Als eine Seite-Problem, finden Sie vielleicht diese links interessant. Sie sind beide ein wenig alt, - und mehr *nxy als Windows. Die zweite impliziert, dass - im richtigen Umfeld - threads vielleicht nicht so schlimm wie du denkst.
Der C10K problem
Warum Veranstaltungen Sind Eine Schlechte Idee (für High-concurrency Server)
@Chris Miller - Was Sie erklärt habe, Ihre Antwort ist sachlich falsch.
Meldung Windows-Stil async, wie Sie durch WSAAsyncSelect, ist in der Tat weitgehend ein workaround für das fehlen einer ordnungsgemäßen threading-Modell in Win 3.x Tage.
.NET Begin/End, jedoch ist nicht mit extra threads. Stattdessen ist es mit overlapped I/O, mit dem zusätzlichen argument, auf WSASend /Sie wsarecv, insbesondere die überlappende Ende-routine angeben, die Fortsetzung.
Dies bedeutet, dass die .NET-Stil nutzt das Windows-Betriebssystem asynchrone I/O-support für vermeiden brennen ein thread blockiert auf einem sockel.
Da threads sind generell teuer (es sei denn, Sie geben Sie eine sehr kleine stack-Größe zu CreateThread), dass threads blockiert sockets wird, stoppen Sie von der Skalierung bis 10.000 s von gleichzeitigen verbindungen.
Deshalb ist es wichtig, dass die async-I/O verwendet werden, wenn Sie wollen, zu skalieren, und auch warum .NET ist nicht, ich wiederhole, ist nicht, einfach "die Verwendung von threads, [...] gelang es gerade durch den Rahmen".
@Roddy - hab ich schon gelesen, die links, die Sie zeigen Sie auf, Sie sind beide über einen Verweis von Paul Tyma die Präsentation "Tausende von Threads und Blockierenden I/O - Die alte Weise zu schreiben-Java-Server ist wieder wie Neu".
Einige der Dinge, die nicht unbedingt springen, von Paul ' s Präsentation ist jedoch, dass er angegeben -Xss:48k, die der JVM beim Start, und dass er unter der Annahme, dass die JVM die NIO-Implementierung ist effizienter, damit es ein Gültiger Vergleich.
Indy hat nicht geben Sie ein ähnlich stark geschrumpft und eng eingeschränkt stack-Größe. Es gibt keine Aufrufe von BeginThread (die Delphi-RTL-thread erstellen-routine, die Sie verwenden sollten für solche Situationen) oder CreateThread (die raw-WinAPI-Aufruf) in die Indy codebase.
Den Standard-stack-Größe gespeichert wird, in der PE, und für den Delphi-compiler standardmäßig auf 1MB reserved address space (Raum engagiert sich Seite für Seite durch das OS in 4K-Blöcken; in der Tat, der compiler muss code generiert werden, um touch-Seiten, wenn es >4K von einheimischen in eine Funktion, da die Erweiterung wird gesteuert durch die Seitenfehler, aber nur für die niedrigste (guard) - Seite in den Stapel). Das bedeutet, dass Sie gehen, um ausreichend Adressraum nach max 2.000 gleichzeitige threads, die die Verarbeitung von verbindungen.
Nun, Sie können ändern Sie den Standard-stack-Größe in der PE mit dem {$M minStackSize [,maxStackSize]} Richtlinie, aber das wird beeinflussen alle threads, einschließlich der Haupt-thread. Ich hoffe, dass Sie nicht viel tun, Rekursion, weil 48K oder (ähnliche) ist nicht viel Platz.
Nun, ob Paul Recht hat, über nicht-performance von async I/O für Windows in allem, bin ich mir nicht 100% sicher - müsste ich Messen, um sicher zu sein. Was ich aber weiß, ist, dass Argumente über die threaded-Programmierung einfacher als asynchrone event-basierte Programmierung, präsentieren ein falsche Dichotomie.
Async-code nicht müssen werden event-basiert; es kann eine Fortsetzung-basierend, wie es ist .NET, und wenn Sie angeben, dass eine Schließung als Ihre Fortsetzung, Sie erhalten, Zustand gepflegt für Sie kostenlos. Zudem die Umwandlung von linear-thread-Stil-code continuation-passing-style async-code gemacht werden kann, mechanische, von einem compiler (CPS-Transformation mechanisch), so gibt es keine Kosten-code Klarheit entweder.
Gibt es eine Kostenlose IOCP (completion ports) Buchse Komponenten : http://www.torry.net/authorsmore.php?id=7131 (Quellcode im Lieferumfang enthalten)
ich fand es während der Suche die besseren Komponenten/Bibliothek rearchitecture meine kleine instant-messaging-server. Ich habe nicht versucht es noch aber es sieht gut codiert als einen ersten Eindruck.
Indy verwendet synchrone sockets, da es eine einfachere Art der Programmierung. Die asynchrone socket-Blockierung wurde etwas Hinzugefügt, um die winsock-stack zurück in die Windows-3.x Tage. Windows 3.x noch keine Unterstützung für threads und es man konnte keine socket-I/O ohne Gewinde. Für einige zusätzliche Informationen darüber, warum Indy nutzt die Sperrung Modell finden dieser Artikel.
Den .NET-Buchse.BeginRead/EndRead Anrufe sind mit threads, es ist nur gelungen durch die Rahmenbedingungen, statt durch Sie.
@Roddy, Indy 10 wurde gebündelt mit Delphi seit Delphi 2006. Ich fand, dass die Migration von Indy 9 auf Indy 10 eine unkomplizierte Aufgabe.
Mit der ScktComp-Klassen, die Sie benötigen, um ein ThreadBlocking server eher als ein einen nicht Blockierenden server-Typ. Verwenden Sie die OnGetThread Veranstaltung, die hand von der ClientSocket param, um einen neuen thread ausdenken. Sobald Sie instanziiert eine geerbte Instanz TServerClientThread erstellen Sie eine Instanz von TWinSocketStream (innerhalb des thread), die Sie verwenden können, um Lesen und schreiben in den socket. Diese Methode bringt Sie Weg von dem Versuch zur Verarbeitung von Daten im Ereignis-handler. Diese threads existieren könnte, die nur für die kurze Zeit brauchen, um zu Lesen oder zu schreiben, oder hängen für die Dauer für die Zwecke wiederverwendet werden.
Den Betreff des Schreibens ein socket-server ist ziemlich groß. Es gibt viele Techniken und Methoden, die Sie wählen könnten, zu implementieren. Die Methode des Lesens und Schreibens, um das gleiche socket mit in die TServerClientThread ist geradlinig und gut für einfache Anwendungen. Wenn Sie brauchen ein Modell für eine hohe Verfügbarkeit und die hohe Parallelität dann müssen Sie sich in mustern wie das Proactor-Muster.
Glück!