Marktsimulation (Teil 12): Sockets (VI)
Einführung
Im vorangegangenen Artikel „Marktsimulation (Teil 11): Sockets (V)“ haben wir erklärt, wie man eine Python-Anwendung zur Verwendung in Excel erstellt. Der Zweck dieser Anwendung war es, zu demonstrieren, wie man einen Echo-Server in Python erstellt. Es zeichnete sich dadurch aus, dass die Daten zu den Verbindungs- und Trennungsereignissen direkt in Excel angezeigt wurden.
In Wahrheit ist dieser Server für uns nicht besonders nützlich, vor allem, weil er nur eine einzige Verbindung zulässt – und Server, die nur für eine einzige Verbindung ausgelegt sind, sind von begrenztem praktischen Wert. Ich möchte jedoch nicht, dass Sie sich zu sehr mit diesem Detail aufhalten. Ziel war es zu zeigen, wie ein in Python geschriebenes Skript transparent in Excel arbeiten kann. Aber für unsere tatsächlichen Bedürfnisse muss der Server etwas fortschrittlicher sein. Um dies zu erreichen, müssen wir mehrere zusätzliche Funktionen implementieren.
Das Ziel ist hier nicht, eine produktionsreife Anwendung zu erstellen. Wie bereits erwähnt, sind Sockets ein sehr komplexes Thema, für dessen Erforschung man viel Zeit aufwenden muss. Erwarten Sie nicht, dass Sie an nur einem Tag etwas wirklich Robustes und Ausgefeiltes entwickeln können. Wenn man sich mit Sockets beschäftigt, muss man sich mit vielen Details befassen – manche sind einfacher, andere sind wesentlich komplizierter.
Da unsere Anwendung jedoch eine anspruchsvollere Lösung erfordert, um die Kommunikation zwischen Excel und MetaTrader 5 zu ermöglichen, wurde beschlossen, die Lernkurve etwas zu erleichtern. Denn Ziel ist es, allen, die sich gerade erst mit dem Thema Sockets beschäftigen, zu zeigen, wie es in der Praxis funktionieren kann.
Ich entschuldige mich bei denjenigen, die mehr Erfahrung haben, wenn dies wie eine Wiederholung erscheint oder wenn der Artikel keine neuen Erkenntnisse liefert. Aber dieses Material wird für Anfänger sehr nützlich sein, vor allem, da wir alles in reinem Python machen werden.
In diesem Artikel werden wir nicht direkt mit Excel oder MQL5 arbeiten. Im Falle von MQL5 werden wir es jedoch verwenden können. Genauer gesagt, werden wir uns auf das stützen, was in dieser Reihe bereits entwickelt wurde. Für ein vollständiges Verständnis der Materie ist es auch hilfreich, sich anzusehen, was bereits in MQL5 implementiert wurde.
Wenn Sie Interesse haben, können Sie die folgenden Artikel lesen:
In diesen beiden Artikeln haben wir die Grundlage für den vorliegenden Artikel geschaffen. In diesen Artikeln haben wir einen Mini-Chat entwickelt, mit dem Nutzer des MetaTrader 5 Informationen über Textnachrichten austauschen können. Die Sache ist die, dass der Servercode damals in C++ geschrieben war. Jetzt werden wir einen ähnlichen Server entwickeln, aber dieses Mal in Python.
Lassen Sie uns zunächst ein paar Dinge klären
Die Umwandlung eines Echoservers in einen Miniserver scheint auf den ersten Blick keine komplizierte Aufgabe zu sein. Auch die Anpassung eines Echoservers an die Form, die wir für die Kommunikation zwischen Excel und MetaTrader 5 benötigen, ist nicht besonders schwierig – zumindest für diejenigen, die bereits über ausreichende Erfahrungen und Kenntnisse verfügen. Wenn es jedoch um Sockets geht, insbesondere wenn mehrere Clients gleichzeitig denselben Server nutzen müssen, wird die Situation etwas komplexer. Das liegt daran, dass viele Menschen, insbesondere Anfänger, keine wirkliche Vorstellung davon haben, was eigentlich zu tun ist.
Kehren wir zu dem im vorigen Artikel besprochenen Python-Code zurück. Er ist unten zu sehen:
01. import socket 02. import xlwings as xw 03. 04. def Echo(): 05. wb = xw.Book.caller() 06. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 07. server.bind(("127.0.0.1", 27015)) 08. wb.sheets[0]['A1'].value = "Waiting connection..." 09. server.listen(1) 10. client, addr = server.accept() 11. client.send("Welcome to Server in Python\n\r".encode()) 12. wb.sheets[0]['A2'].value = str(f"Client connected by {addr}") 13. while True: 14. info = client.recv(512) 15. if not info: 16. break 17. client.send(b"Echo Server:" + info) 18. wb.sheets[0]['A3'].value = "Client disconnected" 19. client.close()
Der Python-Server
Obwohl dieser Code ohne das im vorigen Artikel vorgestellte Material nicht funktionieren wird, kann er funktionsfähig gemacht und anschließend als Modul verwendet werden. Da Excel nicht in Verbindung mit Python verwendet wird, müssen bestimmte Teile des Codes entfernt werden, um diese Änderungen umzusetzen. Der Code wird dann wie folgt aussehen:
01. import socket 02. 03. def Echo(): 04. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 05. server.bind(("127.0.0.1", 27015)) 06. server.listen() 07. client, addr = server.accept() 08. client.send("Welcome to Server in Python\n\r".encode()) 09. while True: 10. info = client.recv(512) 11. if not info: 12. break 13. client.send(b"Echo Server:" + info) 14. client.close() 15. 16. if __name__ == "__main__": 17. Echo()
Der Python-Server
Nun gut. Wenn Sie mit Python bereits einigermaßen vertraut sind, haben Sie wahrscheinlich schon bemerkt, was hier vor sich geht. Aber wenn Sie mit Python nicht vertraut sind, werden Sie vielleicht denken: „Was ist das für ein Wahnsinn?“ Tatsächlich ist daran nichts Verrücktes. Es ist einfach so, dass der frühere Code, der nicht direkt vom Python-Interpreter ausgeführt werden konnte, dies nun kann. Dies wurde durch die Hinzufügung der Zeilen 16 und 17 möglich – dennoch kann der Code weiterhin innerhalb eines Moduls verwendet werden, eben wegen dieser Zeilen.
Aber das ist nicht das, worüber wir hier sprechen. Die Frage ist, wie der vorgestellte Code in die Lage versetzt werden kann, mehrere Clients zu akzeptieren, auch wenn er das im Prinzip derzeit nicht kann.
Nun gut. Wenn Sie die Referenzmaterialien in den vorangegangenen Artikeln studiert haben, sollten Sie bereits einige der Details und Probleme in diesem Code verstehen. Der Grund dafür ist, dass er im Wesentlichen nur eine Verbindung zur gleichen Zeit zulässt. Aber wie ist das möglich? Das liegt daran, dass die Funktion accept in Zeile 07 nach ihrer Ausführung nicht erneut ausgeführt wird. Dies verhindert, dass weitere Clients eine Verbindung herstellen können. Auch wenn derselbe Client die Verbindung beendet, kann er sich nicht erneut verbinden.
Der Grund ist derselbe: Die Akzeptfunktion wird nicht mehr ausgeführt. „Ist es möglich, diesem Code eine weitere Schleife hinzuzufügen, sodass der Inhalt zwischen den Zeilen 07 und 14 erneut ausgeführt wird, wenn der Client sich entscheidet, zurückzukehren?“ Ja, das ist möglich. Und in einigen einfacheren Fällen ist es tatsächlich genau das, was ein Programmierer tun würde. Denken wir jedoch etwas weiter voraus, denn auch wenn wir diese Lösung umsetzen, hätten wir immer noch nur einen verbundenen Client. Und für die Zwecke unseres Mini-Chats wird das nicht ausreichen.
Zusätzlich zu diesem Problem gibt es noch ein weiteres. Wenn Sie getestet haben, was in den beiden vorangegangenen Artikeln beschrieben wurde, haben Sie vielleicht festgestellt, dass Excel praktisch nicht mehr reagiert, wenn der Server in Betrieb ist. Zu den erwähnten Artikeln gehören die folgenden:
Dies ist für diejenigen, die diese Artikel noch nicht gelesen haben. Und nun stellt sich die Frage: Warum lässt sich Excel nicht mehr verwenden, während der Echoserver läuft? Um diese Frage zu beantworten, muss man verstehen, was innerhalb des Servercodes geschieht. Man muss bedenken, dass Excel über VBA den auszuführenden Python-Code aufruft. Sobald der Python-Code ausgeführt wird, beginnt er, mit Excel um die CPU-Auslastung zu konkurrieren.
Für das Betriebssystem ist der Python-Code kein eigenständiger Prozess. Eigentlich ist es eher ein Thread von Excel. Das Wort „Thread“ ist in diesem Fall eigentlich nicht der am besten geeignete Begriff, da ein Thread normalerweise nicht mit dem Hauptprogramm um CPU-Zeit konkurriert. Da das Schließen von Excel jedoch auch den Python-Code beendet, sollten wir das Wort „Thread“ hier mit Vorsicht verwenden. Aber man sollte mit dieser Terminologie vorsichtig sein, und der Grund dafür wird etwas später erklärt.
Dennoch bleibt eine berechtigte Frage: Warum konkurriert der Python-Code um die CPU-Nutzung, wenn Excel sein eigenes Zeitfenster für die Verarbeitung hat? Dies ist auf zwei spezifische Punkte im Kodex zurückzuführen. Kehren wir zum ursprünglichen Echo-Server-Code zurück. Beachten Sie, dass in Zeile 10 ein Aufruf an die Funktion accept erfolgt. Diese Funktion wird blockiert. Das heißt, dass der Code bei der Ausführung an dieser Stelle anhält und erst dann weiterläuft, wenn eine Verbindung hergestellt ist. Diese Funktion stellt den ersten Engpass dar – das erste Mal, dass das Python-Skript mit Excel um die CPU-Auslastung konkurriert.
Sobald eine Verbindung hergestellt ist, tritt ein zweites Problem auf. Dies geschieht genau in Zeile 14, wo das Python-Skript wieder mit Excel konkurriert. Dieser Wettbewerb wiederholt sich bei jeder Interaktion mit der Linie 14. Dies hat zur Folge, dass Excel weniger Aufmerksamkeit erhält als das Python-Skript, sodass es bei laufendem Server nur schwer zu verwenden ist.
Aber existiert eine Möglichkeit, dieses Problem zu lösen? Ja, es gibt mehrere Möglichkeiten. Die erste besteht darin, das Python-Skript zu einem echten Thread von Excel zu machen. Wenn Excel sein Ausführungsfenster betritt, konkurrieren Excel und das Python-Skript nicht mehr um die CPU-Auslastung. Dies geschieht, weil das Betriebssystem die Tasks so plant, dass jeder einen Teil der Verarbeitungszeit erhält. Bei Prozessoren mit mehreren Kernen könnte das Betriebssystem Excel einen Kern und das Python-Skript einen anderen Kern verwenden lassen. Dazu muss Excel dem Betriebssystem jedoch mitteilen, dass das Python-Skript zu einem Thread werden soll.
Aber um was für ein Thema handelt es sich eigentlich? Um dies zu verstehen, sollten wir uns einem neuen Thema zuwenden.
Verstehen wir, was ein Thread ist
Grob gesagt, ist ein Thread wie ein weiteres Programm, das innerhalb des Hauptprogramms existiert. Das ist in der Tat eine einfache Art, ein Thema zu beschreiben. Ein Thread existiert nur so lange, wie das Hauptprogramm läuft. Wenn das Hauptprogramm – in unserem Fall Excel – nicht mehr existiert, wird auch alles, was mit ihm verbunden ist, beendet.
Das Konzept der Threads ist zugleich mit der Parallelverarbeitung entstanden. Genau das ist der Zweck eines Threads: Er hilft dem Hauptprogramm bei der Ausführung von Aufgaben, die direkt im Hauptcode nur sehr schwer zu realisieren wären. Um dies zu erreichen, platzieren wir einen Teil des Codes in einem Segment, das parallel zum Hauptprogramm läuft.
Sobald der Thread seine Arbeit beendet hat, kann das Hauptprogramm die berechneten oder empfangenen Daten verwenden. Diese Daten werden in dem dafür reservierten Bereich verfügbar sein. Dies ist eine recht interessante Form der Programmierung, denn durch die Verwendung eines Threads können Sie eine Aufgabe ausführen, während das Hauptprogramm weiterläuft, selbst wenn die dem Thread zugewiesene Aufgabe mehrere Minuten in Anspruch nimmt.
Diese Fähigkeit – sowohl in Python als auch in anderen Sprachen – ist sehr überzeugend. Um jedoch wirklich zu verstehen, was geschieht und welche Vorteile die Verwendung von Threads hat, muss man sie in Aktion sehen. Um das Thema nicht ohne ein praktisches Beispiel zu verlassen, werden wir uns einen Fall der Verwendung von Threads in Python ansehen.
01. import socket as sock 02. import threading as thread 03. 04. def NewClientHandler(conn, addr): 05. print(f"Client [%s:%s] is online..." % addr) 06. while True: 07. msg = conn.recv(512).decode().rstrip(None) 08. if msg: 09. print(f"Message from [%s:%s]: {msg}" % addr) 10. if msg.lower() == "/see you later": 11. break 12. print(f"Client [%s:%s] is disconnected..." % addr) 13. conn.close() 14. 15. server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 16. server.bind(('localhost', 27015)) 17. server.listen() 18. print("Waiting connections...") 19. while True: 20. conn, addr = server.accept() 21. conn.send("Welcome to Server.\n\r".encode()) 22. thread.Thread(target=NewClientHandler, args=(conn, addr)).start()
Der Python-Server
Dieser Server, dessen Code oben angegeben ist, verwendet Threads, um mehrere Clients gleichzeitig zu bedienen. Bitte beachten Sie einige wichtige Details. Erstens leitet der Server die von einem Client bereitgestellten Informationen nicht an andere weiter. Zweitens ist sie nur für Lehrzwecke gedacht, d. h., um die Verwendung von Fäden zu demonstrieren. Wenn Sie jedoch verstehen, was passiert, können Sie viele interessante Dinge erstellen, indem Sie einfach den obigen Code ändern.
Aber lassen Sie uns zunächst verstehen, was in diesem Code passiert. Wenn Sie bereits mit Python vertraut sind, müssen Sie vielleicht ein wenig Geduld mit Programmieranfängern haben. Erinnern Sie sich daran, dass Sie irgendwann einmal von jemandem unterrichtet wurden – oder Sie aus Unterlagen gelernt haben, die von anderen geschrieben wurden? Bitte haben Sie Geduld.
In Zeile 01 importieren wir das Paket socket. Beachten Sie, dass wir die Dinge ein wenig anders darstellen. In Zeile 02 importieren wir das Paket, mit dem wir Threads erstellen können. Wie bei Zeile 01 gibt es auch hier etwas, das ungewöhnlich erscheinen mag, aber seien Sie nicht beunruhigt. Wir erstellen einen Alias, um die Dinge in Python angenehmer und verständlicher zu machen.
Zwischen den Zeilen 04 und 13 gibt es ein Verfahren. Wir lassen sie vorerst beiseite und gehen zu Zeile 15 über. Beachten Sie, dass der Code zwischen den Zeilen 15 und 18 fast identisch mit den vorherigen Beispielen im heutigen Artikel ist. Dieser Teil ist für die Erstellung des Server-Sockets zuständig, der Code ist also derselbe. Ein wichtiger Punkt: In diesen Beispielen verwenden wir das TCP-Protokoll. Es ist jedoch zu bedenken, dass es noch andere Netzwerkprotokolle gibt, sodass der Code von Server zu Server leicht variieren kann, vorwiegend aufgrund bestimmter Aspekte, die wir später erörtern werden.
In jedem Fall wird in Zeile 18 angezeigt, dass der Server bereit ist, Verbindungen anzunehmen. Erinnern Sie sich an das bereits erwähnte Detail – die Notwendigkeit, eine Schleife vor den Aufruf von Accept zu setzen? Nun, hier tun wir genau das. Auch wenn ein Client die Verbindung unterbricht, kann er sich erneut mit dem Server verbinden, da die Schleife mehrere Interaktionen mit dem Accept Call zulässt. Erinnern wir uns an ein weiteres Detail: Der Accept-Aufruf blockiert den Code und zwingt ihn, auf eine Verbindung zu warten. In diesem Fall wird die Ausführung mit Zeile 21 fortgesetzt, in der eine Willkommensnachricht an den neuen Client gesendet wird, in der bestätigt wird, dass die Verbindung hergestellt wurde. In Zeile 22 wird dann ein Thread in Python erstellt.
An dieser Stelle wird es wirklich interessant, und es wird etwas sehr Wichtiges deutlich. Wenn wir den Code hier zwischen Zeile 04 und Zeile 13 statt in Zeile 22 platzieren würden, wäre das Verhalten des Servers vollkommen anders. Das liegt daran, dass der Code nicht mehr als eine Verbindung zulässt, sondern nur noch eine akzeptiert.
„Moment, wie ist das möglich?“ Um dies zu verstehen, muss man wissen, dass Zeile 22 keine magische Zeile ist. Vielmehr wird die in Zeile 04 definierte Prozedur aufgerufen. Es gibt jedoch einen entscheidenden Unterschied zwischen einem Aufruf, der zu einem Thread wird, und einem, der dies nicht tut. Sobald der Aufruf zu einem Thread wird – wie in diesem Code gezeigt –, tut das Betriebssystem Folgendes: Es nimmt den Thread-Code, weist ihn im Speicher zu und teilt die Ausführungszeit oder das Fenster des Hauptcodes zwischen dem Hauptcode selbst und dem Thread auf.
Dies mag zunächst etwas verwirrend sein. Aber stellen Sie sich das einmal so vor: Die Zeit, die das Betriebssystem für die Programmausführung zur Verfügung stellt, ist wie eine Tafel Schokolade. Wenn ein Programm keine Threads hat, kann es die ganze Tafel „aufessen“. Wenn es jedoch Threads hat, wird die Tafel in immer kleinere Stückchen zerlegt. Das Hauptprogramm kann also nur eines dieser Stückchen verbrauchen, während der Rest von den Threads verbraucht wird. Dies wäre eine recht einfache Erklärung, wenn unser Prozessor nur einen Kern hätte.
Um zu unserem Code zurückzukehren, beachten Sie, dass sich das Programm nach der Ausführung von Zeile 22 in zwei Teile „aufspaltet“. Ein Teil kehrt sofort zur Zeile 19 zurück, wo er in Zeile 20 erneut wartet. Es ist wichtig, daran zu denken, dass die Akzeptfunktion den Code blockiert und die weitere Ausführung verhindert. Der andere Teil wird in die Zeile 04 geleitet.
Nun gut. „Jetzt bin ich völlig verwirrt: Wie kann der Code in Zeile 04 ausgeführt werden, wenn der Code in Zeile 20 blockiert ist? Das ist sehr verwirrend.“ Beruhigen Sie sich, liebe Leserinnen und Leser – es gibt keinen Grund, verwirrt zu sein. Erinnern Sie sich an die Analogie mit dem Schokoriegel? Der Hauptcode wird in Zeile 20 blockiert und wartet auf eine neue Client-Verbindung. Der Code zwischen den Zeilen 4 und 13 wird jedoch unabhängig von der neuen Client-Verbindung ausgeführt. Wichtig ist, dass der Server beginnt, auf Informationen von dem verbundenen Client zu warten. Deshalb führen wir in Zeile 06 eine neue Schleife ein. Beachten Sie dies bitte, denn wenn Sie diesen Teil nicht verstehen, werden Sie bei der Funktionsweise der Threads völlig den Überblick verlieren.
Diese Schleife, die in Zeile 06 beginnt und in Zeile 11 endet, wird nur innerhalb des Threads existieren. Und jeder Client – das ist wichtig zu verstehen – hat seinen eigenen Kreislauf. Selbst wenn zwei oder mehr Clients miteinander verbunden sind, wird der Betrieb eines Clients nicht durch den anderen gestört. Stellen Sie sich nun vor, dass jeder Client seinen eigenen Server hat, der nur für diesen Kunden bestimmt ist. Mit anderen Worten: Jeder Client, der verbunden ist oder verbunden werden soll, erstellt für sich selbst einen Server. Und dieser Server wird nur diesem Client antworten.
Das ist der Zauber der Threads. Doch nicht alle diese Wunder sind perfekt. Es gibt Probleme, die bei der Verwendung von Threads auftreten. Viele dieser Probleme treten genau dann auf, wenn wir als Programmierer anfangen, sie wahllos zu verwenden. Denken Sie immer daran, dass ein Thread dem Hauptprogramm Zeit wegnimmt, auch wenn er scheinbar parallel läuft. Denn egal, wie viele Kerne unser Prozessor hat, sie sind nicht unendlich. Daher kann die übermäßige Verwendung von Threads alle verfügbaren Kerne beanspruchen, was zu einer längeren Ausführungszeit führt und folglich den Vorteil der Verwendung von Threads schmälert.
Vielleicht fragen Sie sich auch etwas anderes. Dieser Code kann zwar mit mehreren Clients umgehen, aber er erlaubt ihnen nicht, miteinander zu kommunizieren. Können Informationen zwischen Clients nicht über Threads weitergegeben werden? Wenn Ihnen dieser Gedanke durch den Kopf geht, bedeutet das, dass Sie immer noch nicht ganz verstehen, wie Threads funktionieren. Aber lassen Sie sich nicht entmutigen. Im Gegenteil, Sie sollten zufrieden sein, weil Sie diese Frage gestellt haben. Wenn Sie beim Betrachten des Codes und beim Lesen des Artikels genau das gedacht haben, zeigt das, dass Sie wirklich verstanden haben, wie der Server funktioniert. Allein die Tatsache, dass die Frage aufkam, zeigt jedoch, dass Sie das Konzept der Threads noch nicht vollständig beherrschen. Lassen Sie uns also diese Zweifel ausräumen. Um die Begriffe zu trennen, werden wir dies im nächsten Abschnitt untersuchen.
Die Kommunikation von Client zu Client wird vom Thread verwaltet
Um zu verstehen, wie das funktioniert, müssen Sie verstehen, was bis zu diesem Punkt behandelt wurde. Wenn Sie die vorherigen Themen nicht ganz verstanden haben, lesen Sie sie noch einmal. Es ist wichtig, sie zu verstehen – sonst werden Sie in diesem Abschnitt, der zwar relativ kurz ist, noch mehr verwirrt sein und, was noch schlimmer ist, keine Ahnung haben, was eigentlich passiert.
Werfen wir einen Blick auf den Code.
01. import socket as sock 02. import threading as thread 03. 04. CONN_LIST = [] 05. 06. def NewClientHandler(conn, addr): 07. global CONN_LIST 08. CONN_LIST.append(conn) 09. print(f"Client [%s:%s] is online..." % addr) 10. while True: 11. msg = conn.recv(512).decode().rstrip(None) 12. if msg: 13. print(f"Message from [%s:%s]: {msg}" % addr) 14. for slave in CONN_LIST: 15. if slave != conn: 16. slave.send(f"[{addr[0]}]: {msg}\n\r".encode()) 17. if msg.lower() == "/see you later": 18. break 19. print(f"Client [%s:%s] is disconnecting..." % addr) 20. conn.close() 21. CONN_LIST.remove(conn) 22. 23. server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 24. server.bind(('0.0.0.0', 27015)) 25. server.listen() 26. print("Waiting connections...") 27. while True: 28. conn, addr = server.accept() 29. conn.send("Welcome to Server.\n\r".encode()) 30. thread.Thread(target=NewClientHandler, args=(conn, addr)).start()
Der Python-Server
„Aber was ist das? Sie haben nur ein paar Zeilen Code hinzugefügt – sind Sie sicher, dass die Clients sich damit gegenseitig Nachrichten schicken können?“ Ja, ich versichere Ihnen, dass dieser Code nicht nur die Weiterleitung von Informationen zwischen den Clients ermöglicht, sondern auch dafür sorgt, dass der Server genau diese Nachrichten anzeigt. Dies ist in dem Fenster der Eingabeaufforderung zu sehen, in dem der Server ausgeführt wird.
Bei diesem Code ist jedoch ein wichtiger Aspekt zu beachten: Auf alte Nachrichten kann nicht zugegriffen werden. Dies bedeutet, dass nur die Clients, die derzeit mit dem Server verbunden sind, Nachrichten erhalten. „Aber wie genau funktioniert das? Ich schaue mir den Code an und verstehe ihn einfach nicht.“
Um zu verstehen, wie der Server die an andere Clients gesendeten Nachrichten übermittelt, müssen Sie wissen, wie Threads funktionieren. Wir haben dies im vorherigen Abschnitt behandelt. Wenn Sie die Funktionsweise von Threads verstanden haben, können wir uns nun damit befassen, wie sie miteinander kommunizieren. Das ist richtig. Damit ein Thread Nachrichten an andere Clients senden kann, müssen sie Informationen austauschen. Aber wie wird das gemacht? Im einfachsten Fall geschieht dies über einen gemeinsam genutzten Speicherbereich, der in Zeile 04 explizit definiert wird. Mit anderen Worten: Die in Zeile 04 angegebene Liste ist für alle Threads sichtbar.
Um sicherzustellen, dass alles reibungslos abläuft, deklarieren wir in Zeile 07 des Threads die Liste als global. Dadurch wird verhindert, dass Python sie lokal erstellt. Wenn Python sie lokal erstellen würde, würde der Speicher nicht zwischen Threads aufgeteilt werden, was genau das ist, was wir benötigen. In Zeile 08 fügen wir die Verbindung, die den Thread erstellt hat, zu unserer Liste hinzu. Dieser Schritt ist von entscheidender Bedeutung, da jede neue Verbindung einen neuen Thread erzeugt und der Liste einen neuen Eintrag hinzufügt.
Wenn Zeile 11 ausgeführt wird, blockiert sie, während sie darauf wartet, Daten von der Verbindung zu erhalten – und dies geschieht in jedem Thread unabhängig. Sobald ein Client etwas an den Server sendet, beginnen wir in Zeile 14 eine kleine Schleife. Dies ist der Ort, an dem der „Zauber“ geschieht. Beachten Sie, dass wir die Liste der Verbindungen, die von den einzelnen Threads erstellt wurde, iterativ durchgehen. Die Schleife ist jedoch unabhängig davon – da der Speicher von den Threads gemeinsam genutzt wird, durchläuft sie alle Verbindungen nacheinander.
Wenn die Bedingung in Zeile 15 falsch ist (d. h., wenn die Verbindung mit der Verbindung übereinstimmt, die zur Schleife des aktuellen Threads gehört), unternehmen wir nichts. Aber wenn der Test wahr ist, wird Zeile 16 ausgeführt. Er sendet an die von einem anderen Thread verwaltete Verbindung die von diesem Thread empfangenen Daten. Ich hoffe, Sie haben diesen Teil verstanden. Zur Erinnerung: Jeder Thread entspricht einer Verbindung. Wenn eine Verbindung Daten empfängt, löst sie die Schleife zur Weiterleitung dieser Daten an andere Verbindungen aus. Dies führt jedoch nicht dazu, dass die anderen Themen aktiv werden.
„Warten Sie einen Moment. Wie ist das möglich? Wenn ein Thread Daten an andere Verbindungen sendet, sollten die anderen Threads aktiv werden.“ Nein, lieber Leser. Ihre Argumentation ist fehlerhaft. Nicht ganz falsch, aber Sie haben Folgendes übersehen: Der Server hört nicht auf das, was innerhalb der Threads passiert. Es hört zu, was auf der Client-Seite passiert. Für jeden Client sieht es so aus, als sei der Server ausschließlich mit einem bestimmten Server verbunden. Und der Client hat keine Ahnung, dass er sich innerhalb eines Threads befindet.
Wenn schließlich ein Client die Verbindung trennt, wird die in Zeile 21 hinzugefügte Verbindungskennung aus der Liste entfernt. Dies hat zur Folge, dass alle anderen Threads die kürzlich geschlossene Verbindung nicht mehr sehen.
Abschließende Gedanken
Obwohl dies sehr verwirrend erscheinen mag, ist das Thema Threads genauso ernst wie das Thema Sockets. Diese Lösung mit Threads ist jedoch nur einer von vielen möglichen Ansätzen. Im nächsten Artikel werden wir eine andere Möglichkeit zur Lösung dieses Problems erörtern, da diese threadbasierte Lösung das Excel-Problem nicht behebt. Das liegt daran, dass wir immer noch das Problem haben, dass die Accept-Funktion den Server blockiert, was die Verwendung von Python in Excel nicht besonders angenehm macht – zumindest für diejenigen, die Sockets benötigen.
| Datei | Beschreibung |
|---|---|
| Experts\Expert Advisor.mq5 | Demonstriert die Interaktion zwischen Chart Trade und dem Expert Advisor (erfordert Mausstudium für die Interaktion). |
| Indicators\Chart Trade.mq5 | Erzeugt ein Fenster zur Konfiguration des zu versendenden Auftrags (erfordert Mausstudie zur Interaktion). |
| Indicators\Market Replay.mq5 | Erstellt Steuerelemente für die Interaktion mit dem Wiedergabe-/Simulationsdienst (erfordert Mausstudie für die Interaktion). |
| Indicators\Mouse Study.mq5 | Ermöglicht die Interaktion zwischen den grafischen Bedienelementen und dem Nutzer (erforderlich sowohl für das Wiedergabesystem als auch für den realen Marktbetrieb). |
| Services/Market Replay.mq5 | Erstellt und pflegt den Marktwiedergabe-/Simulationsdienst (Hauptdatei des gesamten Systems). |
| Code VS C++ Server.cpp | Erstellt und pflegt einen in C++ entwickelten Socket-Server (Mini-Chat-Version). |
| Code in Python\Server.py | Erstellt und pflegt einen Python-Socket für die Kommunikation zwischen MetaTrader 5 und Excel. |
| ScriptsCheckSocket.mq5 | Ermöglicht das Testen der Verbindung mit einem externen Socket. |
| Indicators\Mini Chat.mq5 | Implementiert einen Mini-Chat über einen Indikator (erfordert den Server, um zu funktionieren). |
| Experts\Mini Chat.mq5 | Implementiert einen Mini-Chat in einem Expert Advisor (erfordert den Server, um zu funktionieren). |
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/12745
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Trend-Kriterien. Abschluss
Vom Neuling zum Experten: Navigieren durch die Unregelmäßigkeiten des Marktes
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Entwicklung eines Toolkits zur Preisaktionsanalyse (Teil 56): Interpretation von Annahme und Ablehnung bei Sitzungen anhand des CPI
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Danke für die Informationen... wie können Steckdosen helfen, zwei MT5-Terminals von verschiedenen Brokern zu verbinden? auf einem Forex auf dem anderen Moex - Paired Trading von zwei Terminals kann durch Steckdosen realisiert werden?
Ich grabe mich gerade in dieses Thema ein... entschuldigen Sie bitte im Voraus, wenn meine Frage ein wenig vom Thema abweicht.... Ich lese immer noch Artikel und suche nach Lösungen für zwei MT5, die zusammen handeln und Kurse von Symbolen von verschiedenen Börsen im Wesentlichen erhalten und zusammen handeln, nachdem sie Daten über Kurse von 2-3-4-5 Symbolen analysiert haben.....
Ich werde sockeln:
Inter-Terminal-Austausch: Daten gehen direkt zwischen MT5 A und MT5 B.
Ereignisse: OnSocketEvent() wird sofort ausgelöst , wenn Daten empfangen werden.
Datenflexibilität: JSON, binäre Strukturen, Arrays können übertragen werden.
Geschwindigkeit: Die Latenzzeit ist um eine Größenordnung geringer als beim variablen Polling.
Zuverlässigkeit: Es gibt Mechanismen zum erneuten Senden und zur Bestätigung.
In diesem Zusammenhang plane ich einen fortgeschrittenen Python-Server mit Spread-Berechnung, ACK/NACK, Speicherung von Positionszuständen und Web-Interface zur Überwachung;
Ich grabe mich gerade in dieses Thema ein... entschuldige im Voraus, wenn meine Frage ein wenig vom Thema abweicht.... Ich lese immer noch Artikel und suche nach Lösungen für den Handel mit zwei MT5-Terminals in einem Socket und das Erhalten von Kursen von Symbolen von verschiedenen Börsen im Wesentlichen und den Handel in einem Socket nach der Analyse von Daten zu Kursen von 2-3-4-5 Symbolen....
hier werde ich Steckdosen zu spinnen:
Inter-Terminal-Austausch : Daten gehen direkt zwischen MT5 A und MT5 B.
Ereignisse : OnSocketEvent() wird sofort ausgelöst , wenn Daten empfangen werden.
Datenflexibilität : JSON, binäre Strukturen, Arrays können übertragen werden.
Geschwindigkeit : Die Latenzzeit ist um eine Größenordnung geringer als beim variablen Polling.
Zuverlässigkeit : Es gibt Mechanismen zum erneuten Senden und zur Quittierung.
In diesem Zusammenhang plane ich: einen fortgeschrittenen Python-Server mit Spread-Berechnung, ACK/NACK, Speicherung von Positionszuständen und Web-Interface zur Überwachung;
Ihre Frage ist relevant und interessant. Aber ich denke, Sie ziehen voreilige Schlüsse. Lassen Sie mich klarstellen: Obwohl MQL5 Sockets implementiert, wie ich in meinen Artikeln erkläre, erlaubt es Ihnen nicht, einen Server zu erstellen. Nur einen Client. Daher ist vieles von dem, was Sie vorhaben, unmöglich. ES IST UNMÖGLICH in MQL5 implementiert zu werden. Sie werden externen Code benötigen. In Ihrem Fall erwähnen Sie die Verwendung von Python, was an sich schon eine Lösung ist.
Tatsächlich kann vieles von dem, was Sie brauchen, in Python implementiert werden. Allerdings gibt es ein kleines Problem bei dem, was Sie versuchen: die direkte Interaktion mit dem Broker. Warum sage ich das? Aus Sicherheitsgründen akzeptieren Broker im Allgemeinen keinen Zugriff über Sockets. Es gibt ein spezielles Protokoll für eine solche Interaktion, das speziell dafür entwickelt wurde, Unterbrechungen in den internen Mechanismen des Brokers zu verhindern. Aber es ist nicht unmöglich, es zu versuchen. Sie können Ihnen das Kommunikationsprotokoll mitteilen, damit Sie darauf zugreifen können, wenn es Ihnen passt. Aber glauben Sie nicht, dass dies einfach sein wird, es sei denn, Sie haben einen SEHR engen Freund bei der Maklerfirma, der Ihnen die benötigten Informationen geben kann.
Ein weiterer Punkt, der mir aufgefallen ist, ist die Interaktion und der Informationsaustausch zwischen zwei verschiedenen Instanzen von MetaTrader 5. Meiner bescheidenen Meinung nach ist das, was Sie zu tun versuchen, keine gute Idee. Sie verstehen einige Konzepte der parallelen Programmierung und die damit verbundenen Probleme nicht. Wenn es Ihnen nichts ausmacht, versuchen Sie, die "Producer-Consumer"-Aufgabe zu studieren. Es wird Ihnen helfen, die Komplexität und die Fallstricke zu verstehen, die bei der Übertragung von Informationen zwischen verschiedenen MetaTrader 5-Instanzen zu Handelszwecken auftreten können.
Wie auch immer, viel Glück mit Ihrem Projekt 🙂👍
Ihre Frage ist relevant und interessant. Aber ich denke, Sie ziehen voreilige Schlüsse. Lassen Sie mich klarstellen: Obwohl MQL5 Sockets implementiert, wie ich in meinen Artikeln erkläre, erlaubt es Ihnen nicht, einen Server zu erstellen. Nur einen Client. Daher ist vieles von dem, was Sie vorhaben, unmöglich. ES IST UNMÖGLICH in MQL5 implementiert werden. Sie werden externen Code benötigen. In Ihrem Fall erwähnen Sie die Verwendung von Python, was an sich schon eine Lösung ist.
Tatsächlich kann vieles von dem, was Sie brauchen, in Python implementiert werden. Allerdings gibt es ein kleines Problem bei dem, was Sie versuchen: die direkte Interaktion mit dem Broker. Warum sage ich das? Aus Sicherheitsgründen akzeptieren Broker im Allgemeinen keinen Zugriff über Sockets. Es gibt ein spezielles Protokoll für eine solche Interaktion, das speziell dafür entwickelt wurde, Unterbrechungen in den internen Mechanismen des Brokers zu verhindern. Aber es ist nicht unmöglich, es zu versuchen. Sie können Ihnen das Kommunikationsprotokoll mitteilen, damit Sie darauf zugreifen können, wenn es Ihnen passt. Aber glauben Sie nicht, dass dies einfach sein wird, es sei denn, Sie haben einen SEHR engen Freund bei der Maklerfirma, der Ihnen die benötigten Informationen geben kann.
Eine weitere Sache, die mir aufgefallen ist, ist die Interaktion und der Informationsaustausch zwischen zwei verschiedenen Instanzen von MetaTrader 5. Meiner bescheidenen Meinung nach ist das, was Sie hier versuchen, keine gute Idee. Sie verstehen einige Konzepte der parallelen Programmierung und die damit verbundenen Probleme nicht. Wenn es Ihnen nichts ausmacht, versuchen Sie, die "Producer-Consumer"-Aufgabe zu studieren. Dies wird Ihnen helfen, den Grad der Komplexität und die Fallstricke zu verstehen, auf die Sie stoßen können, wenn Sie Informationen zwischen verschiedenen MetaTrader 5-Instanzen zu Handelszwecken übertragen.
Wie auch immer, viel Glück mit Ihrem Projekt 🙂👍
DANKE für Ihr Feedback..... keine freunde in brokerages!!! ) habe MT5 terminals in zwei verschiedenen brokerages! muss sie zu freunden machen..... ) das ist ein Projekt für nächstes Jahr!!!
Hier - ein Versuch... danke für die Artikel - ich lese sie und studiere den Inhalt!!! wenn die Geschwindigkeit es zulässt - kann ich es direkt über die Dateien machen.... Zugang - wie bei früheren Festplatten, die im Bios in zwei Teilen angeschlossen waren: ein Master und ein Slave... )
also hier... auf einem leistungsfähigen Computer zwei MT5 Terminals ein Master (der Haupt) - der andere Slave (der zweite), auf dem einen die Börse - auf dem anderen MT5 Forex! vielleicht Realisierung über Dateien ) Lesen - Schreiben wird reichen ... aber ich würde gerne eine schnellere einfach in Bezug auf Lesen - Empfangen von Daten Variante.... nach Art der globalen Variablen des Client-Terminals (ich benutze keine Dateien - lange Zeit), aber globale Variablen des Client-Terminals sind nur in diesem Terminal sichtbar..... Sie müssen externe Prozesse verbinden... vielleicht Tabellen im Speicher erstellen... wie dll Bibliotheken der Verbindung werde ich realisieren!!!
Einen schönen Tag noch!