lNet Sockets in Konsolenanwendung

Alle Fragen zur Netzwerkkommunikation
Antworten
Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

lNet Sockets in Konsolenanwendung

Beitrag von Bergmann89 »

Hey Leute,

ich bin grad dabei nen kleinen Server zu schreiben. Der soll dann als Konsolenanwendung auf nem Linux-Server laufen. Das gaze sieht dann ungefähr so aus:

Code: Alles auswählen

//Logger zum loggen einiger nachrichten in die Console
  Logger := TConsoleLogger.Create;
  Logger.LogTime   := true;
  Logger.LogSender := true;
 
//eigentlicher Server, der das TLTCP-Objekt nutzt
  Server := TnetSessionServer.Create;
  Server.OnLog := @Logger.LogMsg;
 
  cmdList := TStringList.Create;
 
//Befehlsschleife zur Eingabe von Befehlen in der Konsole
  running := true;
  while running do begin
    ReadLn(cmd);
    try
      if not HandleCommand(cmd) then
        Logger.LogMsg(nil, 'unknown command: '''+cmd+'''');
    except
      on e: Exception do begin
        Logger.LogMsg(nil, 'Error: ' + e.Message);
      end;
    end;
  end;
 
  cmdList.Free;
  Server.Free;
  Logger.Free;
So startet und initialisiert sich das wunderbar, aber nach einigen Tests ist mir aufgefallen, das der TCP-Socket auf keine einzige Nachricht reagiert. Und dann ist mir eingefallen, das is gar kein Message-Loop wie bei ner normalen Application habe :/ Wie bekomm ich das jetzt am dümmsten in der Konsolenanwendung unter? Ich hab mir überlegt, den Server in einen Thread auszulagern, aber ich weiß nicht recht, was ich dann in die Arbeits-Methode des Threads packen soll, weil ja bei den Sockets alles Event-basiert ist :roll:

MfG & Thx Bergmann.

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: lNet Sockets in Konsolenanwendung

Beitrag von Displaced »

Ist normal, im "while running" teil musste auch nen "Server.CallAction" machen damit der reagiert =)
Allerdings, wird das bei dir auch so nicht klappe weil ja ReadLn das ganze gaube ich anhalten würde.
Das mit dem Thread habe ich ebenfalls gemacht. Nur mit der Besonderheit dass ich "OnDemand" sleeps raus nehme..
Damit bleibt der Thread nicht immer auf volle pulle, ist aber permanent da.
Oder natürlich man stellt sowas auf Blocking um, ich glaub das geht bei lNet auch, bin mir jetzt aber nicht 100% sicher.

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: lNet Sockets in Konsolenanwendung

Beitrag von Bergmann89 »

Hey,

so in der Art hab ich mir das auch gedacht. Die "CallAction" Methode hab ich auch gefunden, aber die wird bei mir nur an den TLCLEventer weitergegeben und bei dem steht nix in der Methode drin, außer "ressult := true".

MfG Bergmann.

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: lNet Sockets in Konsolenanwendung

Beitrag von Displaced »

Ich weiß, aber der benötigt den Aufruf um die Events abzutasten.
Ich hatte auch gehofft es gibt Socket basierende Events dass man sich nicht drum kümmern braucht, aber ohne geht das halt nicht.
Und das mit dem Blocking hab ich persönlich noch nicht getestet, so funktioniert das aber wunderbar. Ich hab nur noch nicht testen können wieviel Last das ganze konstrukt vertragen kann.

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: lNet Sockets in Konsolenanwendung

Beitrag von Bergmann89 »

Hey,

irgendwie steh ich grad auf'm Schlauch. Ich hab bei mir im eigentlichen Programm die Schleife, die den User nach Befehlen fragt (siehe oben) und nen Thread in dem ich das CallAction des TLTcp-Objekts aufrufe. Aber trotzdem passiert nix. Wenn ich das ganze normal mit der LCL und ner Form mach, dann bekommt das TLTcp-Objekt seine Events (über TLCLEventer.HandleEvents) vom Message-Loop der Anwendung. Das Problem bei der Konsolenanwendung ist jetzt, das er die Messages nicht abarbeitet, weil bei mir ja nur der ReadLn-Loop ausgeführt wird und der Thread in dem das TCP-Objekt läuft bekommt keine Messages die er abarbeiten könnte.
Könntest du mir vlt nochmal etwas genauer erklären, wie genau du das bei dir gelöst hast? Welche Objekte/Klassen du wo eingesetzt hast und was in welchem Thread läuft?

MfG & Thx Bergmann.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: lNet Sockets in Konsolenanwendung

Beitrag von mschnell »

Für Konsol-Anwenungen bietet Lazarus leider keinen Mechanismus um Events zu bearbeiten. Das musst Du selber ausprogrammieren.

Hierbei muss natürlich unbedingt darauf geachtet werden, dass keine Endlos-Schleifen mit permanent 100% CPU-Last produziert werden, weil dadurch natürlich die Performance aller anderen Tasks auf dem Server-Rechner leidet.

Das lässt sich z.B. durch sleep - Aufrufe vermeiden, was aber wieder zu einer hohen Latenz des Programms führt.

Ein echter, allgemein verwendbarer Event-Mechanismus (der weder die CPU-Last noch die Latenz unnötig erhöht) ist nicht so leicht zu bauen (sonst würde Lazarus das für Konsolen-Anwendungen ja auch anbieten). Man braucht eine Event-Queue und einen Mechansismus, im Betriebssystem zu Warten, bis irgendein Event auftritt. In Windows kann man das mit Messages machen, in Linux z.B. mit "select()" oder "epoll()".

Readln() und ähnliches kannst Du für ein solches Programm nur in einem eigenen Thread verwenden, weil da ja gewartet wird, bis die Eingabe beendet ist und ansonsten natürlich nichts läuft. (aber Vorsicht: Sachen wie "Synchronize() und QueueAsyncCall() funktionieren in Lazarus auch nicht in nicht-grafischen Programmen.)

-Michal
Zuletzt geändert von mschnell am Mo 25. Jun 2012, 10:56, insgesamt 1-mal geändert.

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: lNet Sockets in Konsolenanwendung

Beitrag von Displaced »

@mschnell
Sind eigentlich ganz gute Hinweise, mal sehen ob ich das in Zukunft mal umbauen kann.

@Bergman89
Naja bei ist ist es so, dass die Konsole das Containerprogramm quasi ist und meine Sockets sind in einem Extra Thread.
Das klappt auch super mit den Hauseigenen Lazarus Thread-Klassen.
Und damit das keine 100% CPU schleife wird. Füge ich einfach jedes mal nen Sleep rein wenn der nix machen muss..
Sobald der was machen muss, wird der Sleep wieder rausgenommen.
Das würde dann so aussehen:
IDLE
SLEEP
IDLE
SLEEP
WORK
(WORK DETECTED NO SLEEP)
WORK
WORK
IDLE
SLEEP

Es reicht wenn ich auf 1 event reagiere und das jede Millisekunde abfrage.. Dass ich dann vielleicht 10ms Verzögerung habe wird dem Spieler zumindest in meinem Fall nicht so sehr kratzen.

Amsonsten werde ich auch aus neugier mal die Sache mit den Events testen und auch mich mal bisschen mit Blocking-Lösungen beschäftigen.

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: lNet Sockets in Konsolenanwendung

Beitrag von Bergmann89 »

Hey,

@mschnell: Console in einen eigenen Thread geht nicht, weil bei ReadLn immer der MainThread wartet.
@Displaced: Socket im extra Thread ist auch umständlich, weil ich mir dann ein extra MessageLoop bauen müsste.
Irgendwie will das alles nicht so wie ich mir das vorstell. Ich bin kurz davor zwei Anwendungen zu machen. Eine für den Server, und eine für die Konsole. Und die Konsole beballert den Server dann mit Messages...

MfG Bergmann.

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: lNet Sockets in Konsolenanwendung

Beitrag von Displaced »

Wo liegt denn dein Problem mit einem extra Thread der den TCP Server beinhaltet?
Das ist quasi das selbe wie wenn du 2 Anwendungen hast.. Nur eben in einer. Den Hauptthread (Konsole) und den Nebenthread (TCPServer).

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: lNet Sockets in Konsolenanwendung

Beitrag von Bergmann89 »

Das Problem ist, das er keine Events bekommt, weil der Eventer, der die Events feuert, seine Befehle aus der WinProc bekommt. Und die hab ich bei der Consolenanwendung nicht. Der Message könnte ich zwar auch im Thread behandeln, aber der Thread bekommt die Message nie, weil die an den Prozess der Anwendung geht.
Achso, vergessen zu sagen. Ich entwickle mit Windows, das ganze soll dann aber auf Linux laufen. Warum ich das so mache? Weil ich kein ordentliches Linuxsystem hier habe, mit dem ich entwickeln kann. Aber rein theoretisch sollte das ja kein Problem sein, den Rest erledigt ja Lazarus für mich, wenn ich nicht alzu nah am System programmiere.

Displaced
Beiträge: 83
Registriert: So 12. Jul 2009, 10:08

Re: lNet Sockets in Konsolenanwendung

Beitrag von Displaced »

Wenn du dein Hauptthread normal abfängst mit ReadLN und im nebenthread die schleife hast wo der "CallActions" ausführt wo auch natürlich dann der TCP Server initialisiert ist, bekommst du die messages auch genau dort wo du die haben möchtest.
Ich mache doch selbst nichts anderes. Und wenn du etwas wieder zurück ausgeben möchtest in der Console ist das auch absolut kein Problem, da du ja auch ne vernünftige Println queue machen kannst und das dann im Hauptthread abarbeiten kannst.
Das funktioniert wunderbar.
Und ich entwickel auch auf Windows, compilier dann auf Linux und es läuft ;) Ich mache nix anderes.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: lNet Sockets in Konsolenanwendung

Beitrag von mschnell »

Bergmann89 hat geschrieben: weil bei ReadLn immer der MainThread wartet.
Das stimmt meiner Ansicht nach nicht. Readln hat mit Threads überhaupt nichts zu tun: es blockiert den Thread in dem es aufgerufen wird. Es ist allerdings vermutlich nicht threadsafe, darf also (zumeindest für dieselbe "Datei" (bzw. Device) ) nur in einem Thread verwendet werden.
Bergmann89 hat geschrieben: Ich bin kurz davor zwei Anwendungen zu machen. Eine für den Server, und eine für die Konsole. Und die Konsole beballert den Server dann mit Messages...
Der Unterschied zwischen zwei Anwendungen und zwei Threadsa ist nur, dass Thread einen (komplett) gemeinsamen Memory-Bereich verwenden. Das macht (Unabhängig von der Prorammiersprache) die Kommunikation zwischen Threads einfacher und effektiver, aber auch "gefährlicher" (weil gemeinsam genutzte Ressourcen explizit gegen konkurrierende Zugriffe geschützt werden müssen.

Ich glaube kaum, dass du irgendeines der angesprochenen Probleme durch Aufteilung in zwei Programme (einfacher) lösen kannst. Du würdest dann ja eine Kommunikation zwischen den Programmen ausprogrammieren müssen.

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: lNet Sockets in Konsolenanwendung

Beitrag von mschnell »

Bergmann89 hat geschrieben:Das Problem ist, das er keine Events bekommt,
In einer Konsolenanwendung gibt es keine Events ! (Tut mir leid, ist nicht schön, ist aber so.)

Wenn Du "normal Lazarus-mäßig" programmieren willst (also mit von der LCL aufgerufenen "On..." Eventhandlern), musst Du eine "Applikation" machen und kein "Programm". Und dann hast Du leider zwingend auch eine grafische Oberfläche.

Der Grund ist, dass Lazarus "im Moment" nur eine Event-Queue im Zusammenhang mit einer grafischen Oberfläche anbietet. Bisher hat leider noch keiner einen "Widget-Typ" programmiert, der Events (Timer, Synchronize, QueuAsyncCall, ...) aber keine GUI hat.

-Michael
Zuletzt geändert von mschnell am Mo 25. Jun 2012, 11:23, insgesamt 1-mal geändert.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: lNet Sockets in Konsolenanwendung

Beitrag von mschnell »

Bergmann89 hat geschrieben: den Rest erledigt ja Lazarus für mich, wenn ich nicht alzu nah am System programmiere.
System-Messages abfangen ist extrem nah am System. Da ist mit Windows/Linux Kompatibilität völlig Essig.

-Michael

Bergmann89
Beiträge: 98
Registriert: Di 15. Nov 2011, 11:36

Re: lNet Sockets in Konsolenanwendung

Beitrag von Bergmann89 »

Hey,

ich habs gefunden. Eig total dummer Fehler -.- Ich hab dem Socket nen LCLEventer mitgegeben. Is klar, das ein LCLEventer ohne LCL nicht richtig arbeitet. Das kommt davon, wenn man copy&paste aus alten Projekten macht ^^ Wenn ich ein einfaches TLTcp-Objekt erstelle, dann wird automatisch ein TSelectEventer initialisiert, un der arbeitet auch so wie gewünscht. Nochma Danke an alle für die Hilfe :)
@mschnell: ReadLn legt sowohl den Thread in dem es aufgerufen wird, als auch den MainThread schlafen. Ich habs ausprobiert. Das ist so, weil die Konsole im MainThread hängt.

MfG Bergmann.

Antworten