Ein Client<->Server System.. Wie?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Hallo zusammen.

Ich würde gerne folgendes in meinem Programm einbauen, weiß allerdings nicht genau wie ich das am besten realisiere:
  • Ein Accountsystem.
  • Nutzer melden sich an einem Server an.
  • Der Server speichert welche Nutzer sich angemeldet haben.
  • Nutzer können ihren "Status" ändern, sofern sie angemeldet sind.
  • Nutzer werden vom Server auf Aktivitäten anderer Nutzer hingewiesen.
  • Nutzer melden sich nicht zwingend ordnungsgemäß ab. (Keepalive-Signale notwendig)
  • Erweiterbarkeit sollte gewährleistet sein, zb. im Falle dass Nutzer miteinander interagieren.
  • Das System sollte relativ Bandbreiten sparend agieren, besonders in Hinsicht auf schwache Internetverbindungen mit Hang zu hohen Pings. (Das Programm läuft parallel zu einem anderen Netzwerklastigen Programm, um genau zu sein ein Spiel ;) )
Um es nochmal kurz zu machen: Ich möchte eine Art "Freundeliste" in mein Programm einbauen.

Was ist hier empfehlenswert?

Eine xmlRPC Lösung? (Wobei die Sache mit den Hinweisen glaub schwierig werden dürfte, oder?) Ein eigenes System mit TCP bzw. noch besser UDP? Welcher Typ von Datenbank? MySQL? PostGreSQL? ...?

Eins noch vorweg: Ich hab sowas bisher noch nie gemacht, d.h. Client->Server schon (UDP), aber nicht Client<->Server. Und vor allem nicht Datenbankprogrammierung.

Wäre für jeden Vorschlag dankbar. :) (Selbstverständlich aaalles mit Python :D, möglichst StdLib)
0x1cedd1ce
User
Beiträge: 31
Registriert: Sonntag 3. Oktober 2010, 12:21

Die Datenbank ist relativ egal. Nimm am besten das was schon vorhanden ist.
Speichern musst du den Account-Name, das Passwort (als Hash), die Freunde. Damit das mit den Freunden einfacher funktioniert könntest du das in eine eigen Tabelle auslagern mit referenzen auf die Tabelle mit den Account-Namen. Für jeden Freund den du in deiner Liste hat gibt es dann eine Zeile mit (Du, Freund).
Dann kannst du einfach kucken wenn du benachrichten musst wenn jemand online kommt
Um das ganze bandbreite-sparend zu implementieren musst du udp benutzen. Dann kannst du selbst bestimmen wann Keep-Alives gesendet werden und du hast weniger Overhead als bei TCP.
Accountstatus, etc ist simpel. Wenn du für jeden verbundenden Benutzer ein Objekt hast kannst du da eine Variable und eine Funktion zum Statusändern einbauen und das wars.
jtk
User
Beiträge: 37
Registriert: Montag 19. November 2007, 17:16

du XMPP(jabber) verwenden, da müsste es fertige bibliotheken geben und es erfüllt alle deine kriterien, es ist vielleicht etwas überladen
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

@0x1cedd1ce
Udp, ok, aber wenn ich damit eine Verbindung mit einem Client fordere, braucht der doch einen offenen Port? Denn der Port den er zuletzt zum anmelden benutzt hat muss ja nicht zwingend noch aktiv sein, oder? Oder muss ich dann im Client einen Socket öffnen der "zuhört", ähnlich wie der auf dem Server?

Bis jetzt hab ich mir da nicht mehr als Gedanken darum gemacht, ausprobiert hab ich noch gar nichts, deshalb hakts auch noch ein wenig bei dem Thema wie die Verbindung vom Server zum Client zu Stande kommt/kommen soll...

@jtk
Danke, werd ich mir mal ansehen.
deets

@Gremlin

Mit TCP ist eine Verbindung vom client zum server bi-direktional.
0x1cedd1ce
User
Beiträge: 31
Registriert: Sonntag 3. Oktober 2010, 12:21

Wenn du ein UDP Socket öffnest musst du einen Port angeben auf dem dieser Socket lauscht. Der Port von diesem Socket ändert sich nicht, kann daher vom Server angesprochen werden. Du musst nur dafür sorgen, das der Client auch zuhört und den Socket nicht schließt.

TCP kannst du natürlich auch verwenden. Vorteil ist, das jedes Paket garantiert ankommt und auch sehr warscheinlich Fehlerfrei. Bei UDP können Packete in der falschen Reihenfolge ankommen, gar nicht ankommen oder Fehlerhaft ankommen ohne das es jemand merkt. Allerdings hat TCP einen höheren Overhead aufgrund der ganzen Sicherheitsmechanismen. TCP hat auch ein Keep-Alive system.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Ich würde als Protokoll auf HTTP setzen, da ich die Befürchtung hätte, irgendwelche eigenen Protokoll auf TCP- oder UDP-Basis bleiben in Firewalls hängen.

Wäre der Client ein moderner Webbrowser, könnte man auf auch Websockets unterstützen, indem man eine passende fertige JavaScript-Bibliothek nutzt, die das alles für einen regelt. Wäre sogar für den Server JavaScript eine Option, könnte man sich etwas wie http://nowjs.com/ anschauen, da muss man sich nur noch um die Persistenz kümmern.

Bei einem Client in Python würde ich wohl einfach auf long-polling setzen und dann für den Server als erstes http://www.tornadoweb.org/ evaluieren. Das scheint mir alles einfacher, als selbst low-level http://docs.python.org/library/asyncore.html zu benutzen.

Als Datenbank lohnt ein Blick auf Redis. Möglicherweise kann man auch einfach die Clients gegen das pubsub-Protokoll von Redis laufen lassen, allerdings müsste das ganze dann schon in einem geschützten Umfeld stattfinden, weil Redis glaube ich einen nur sehr primitiven Zugangsschutz hat. Bei Redis muss zudem alles in den Hauptspeicher passen. Auf einem 256MB VServer fühlt sich diese NOSQL-DB nicht wohl - dafür ist sie extrem schnell.

Wenn das Spiel allerdings eh schon TCP/UPD benutzt, ist meine Überlegungen hinfällig. Anders herum kann man natürlich auch überlegen, die gesamte Kommunikation eines (M)MOs über dieses Protokoll laufen zu lassen.

Stefan
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Hmmm, danke schonmal für die bisherigen Antworten. Bis jetzt spricht ja doch einiges für etwas UDP basiertes.

Aber dann wäre da noch die Frage, wie ich dafür sorge, dass der Server auch immer erreichbar ist, bzw. läuft. Bei meinem "Server" handelt es sich um einen Webserver von Raumopol, kann ich sowas dann überhaupt damit realisieren? (cgi skripte laufen ja eigentlich permanent, oder?) Denn ich weiß ehrlich gesagt (noch) nicht ob es mir möglich ist da etwas mit Linux Bordmitteln zu regeln.
BlackJack

@Gremlin: CGI-Skripte laufen nicht permanent sondern nur wenn der Webserver eine Anfrage für das jeweilige Skript bekommt. Und Webserver bekommen Anfragen per TCP und nicht per UDP.
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Huar.. So langsam sollte ich mir merken dass man hier sehr genau formulieren muss. :lol: Ich meinte natürlich keinen "Web"server (im sinne von apache) sondern einen vserver, der allerdings bisher nur als "webserver" dient.

Aber ist der Sinn von fcgi nicht der, dass der Interpreter nicht andauernd neu gestartet werden muss? Oder bezieht sich das dann tatsächlich nur auf den Interpreter und nicht auf das Skript.. wenn ich in das Skript eine unendliche Schleife einbaue, muss ich also damit rechnen dass es trotzdem irgendwann stoppt?
BlackJack

@Gremlin: Du solltest echt genauer Formulieren, denn von *F*CGI hast Du ja vorher auch nichts gesagt, da stand nur CGI ohne F. Ein FCGI-Programm kann nicht davon ausgehen das es ständig läuft. Der Webserver kann die zum Beispiel killen wenn zu viele davon da sind und längere Zeit keine Anfragen mehr kamen. Umgekehrt können von einem Skript auch mehrere Exemplare gleichzeitig parallel laufen. Oder der Webserver kann so konfiguriert sein, dass er an einen FCGI-Prozess maximal N Anfragen schickt und ihn danach killt und einen neuen startet.

Wenn Du in das Skript eine unendliche Schleife baust, also nicht die, welche die Anfragen bearbeitet, dann wird es nach einer gewissen Zeit auch vom Server gekillt. Zumindest wenn der nicht völlig Banane ist.
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

BlackJack hat geschrieben:Oder der Webserver kann so konfiguriert sein, dass er an einen FCGI-Prozess maximal N Anfragen schickt und ihn danach killt und einen neuen startet.
Gut, dann heißt das hier wohl Hoster fragen.
BlackJack hat geschrieben:Wenn Du in das Skript eine unendliche Schleife baust, also nicht die, welche die Anfragen bearbeitet, dann wird es nach einer gewissen Zeit auch vom Server gekillt. Zumindest wenn der nicht völlig Banane ist.
Wie jetzt, der Webserver beachtet "was" das Skript macht? Ich hätte jetzt vermutet da gibt es einfach so was wie eine Maximaldauer die ein Prozess aktiv sein darf. (wie bei PHP)
BlackJack

@Gremlin: Es wird nicht geschaut *was* das Programm macht, aber wenn es in einer Endlosschleife steckt, dann kann es keine Anfragen beantworten, und wenn es das nicht tut, dann sollte ein Webserver so schlau sein und es abschiessen. Dafür wird es in der Regel einen Timeout geben.

Du solltest bei FCGI einfach davon ausgehen, dass Du nicht wirklich in der Hand hast wie lange ein einzelnes Exemplar von dem Skript läuft und wieviele es daneben noch gibt. Selbst wenn Du das alles selber konfigurieren kannst, würde ich das für unsauber halten, weil das Skript dann nicht mehr nur die FCGI-Bedingungen erfüllen muss, sondern noch zusätzlich die Einschränkungen, die Du dafür definierst. Insbesondere habe ich so ein bisschen das Gefühl, dass Du einen dauerhaft laufenden Server der über UDP kommuniziert in ein FCGI-Skript prügeln willst!?
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

BlackJack hat geschrieben:Insbesondere habe ich so ein bisschen das Gefühl, dass Du einen dauerhaft laufenden Server der über UDP kommuniziert in ein FCGI-Skript prügeln willst!?
Nein, das möchte ich nicht (mehr). :lol:

Inzwischen bin ich soweit, ein fcgi-skript dazu zu benutzen um dafür zu sorgen dass mein server dauerhaft läuft, falls er mal abschmiert oder die vm neugestartet wird. (aufgerufen per cronjob, da sind bei dem hoster nur fcgi-skripte möglich)
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

So, inzwischen bin ich fertig mit meinem ersten eigens geschriebenen Daemon der mit einem Desktop-Client auf UDP-Basis kommuniziert. 8) (Mag für einige nix besonderes sein, aber ich finds trotzdem toll :P )

Nun, so toll nun auch wieder nicht. Also, es funktioniert zwar alles wie ich es gerne hätte und so, aber ein Problem gibts halt doch noch. Und das tritt auf wenn die Ping zwischen Client und Server relativ hoch ist und (vermutlich) auch die Verbindung ziemlich kacke ist. Zum einen bin da ich mit meinem Smartphone/UMTS Internet und ein Tester der irgendwo aus Russland kommt. Wenn ich hingegen mit meinem "normalen" Internet die Verbindung herstelle oder der andere Tester aus Frankreich (ja, wirklich), läuft die Synchronisation zwischen Client und Server tadellos ab.

Das eigentliche Problem ist, dass zum Teil KeepAlive Pakete die der Server als Antwort schickt nicht beim Client ankommen. Die Pakete hingegen die der Client zum Server schickt kommen zu 90% beim Server an. Firewalls und andere Späße traue ich mich auszuschließen, denn auf meinem Smartphone gibt es definitiv keine Firewall und auch mein russischer Tester hat das bereits ausprobiert. Die Pakete kommen aber auch definitiv nicht an. (Mit Wireshark kontrolliert.)

Was ich inzwischen festgestellt habe, aber nicht auf russischer Seite ausprobieren konnte, ist dass es nicht direkt die Ping ist bzw. die Qualität der Leitung (So weit wie ich das eben beurteilen kann) sondern die Zeitspanne zwischen zwei Kontaktaufnahmen. Je höher ich die KeepAlive-Rate einstelle desto eher bekomme ich eine Antwort vom Server, wenn jedoch seltener KeepAlives verschickt werden, tritt das Problem wieder auf. (Jede Minute ein KeepAlive funktioniert, alle fünf Minuten ein KeepAlive funktioniert nicht) Das deckt sich auch mit anderen Arten von Paketen, Pakete die häufiger verschickt werden (können). Logge ich mich relativ zügig ein/aus funktioniert das relativ gut.

Meine Frage ist nun, da hier doch bestimmt der ein oder andere mit ein wenig Erfahrung in dem Bereich rumgeistert, wie ich soetwas am besten kompensiere?

Ich dachte da im ersten Moment daran die KeepAlive-Rate abhängig von der Ping zu definieren. Je höher die Ping, desto höher die KeepAlive-Rate, je niedriger die Ping, desto niedrige die Rate. Aber würde das auf Dauer wirklich etwas bringen?

Ich hab auch schonmal die Größe der Pakete erhöht (von ~60byte auf ~200byte) aber auch das hat nichts gebracht.

Was gibt es noch für Sachen die ich ausprobieren könnte?

Ach und nein ich gebe nicht sofort auf wenn ich auf *ein* Paket keine Antwort erhalte. Bevor ich den Server als nicht erreichbar abstemple versuch ichs weitere zehn Mal innerhalb von 30sek. (D.h. alle 3 Sekunden ein Versuch)
deets

Wie waere es, wenn du einfach UDP in die Tonne kloppst, und das ganze mit TCP machst? Dann sollte dir naemlich schon das System diese Garantien geben. Dazu ist es nunmal gedacht - und wenn das, was du jetzt programmierst, am Ende auf dasselbe hinauskommt, kannst du dir die Arbeit gleich sparen.

UDP sollte man fuer Daten verwenden, die auch problemlos mal nicht ankommen duerfen. ZB Koordinaten oder Steuerinformationen in Spielen oder so - die werden eh immer wieder neu gesendet, und damit ist es nicht wichtig, ob der Strom stetig ist, oder Luecken hat. Aber eine Nachricht sollte schon garantiert ankommen...
Gremlin
User
Beiträge: 166
Registriert: Freitag 28. Mai 2010, 23:49

Ja selbstverständlich könnte ich das auch mit TCP machen und natürlich kommt das, was ich da programmiert habe, auf das selbe hinaus wie wenn ich gleich TCP nehmen würde. Allerdings interessiere ich mich nunmal für diese Thematik und möchte daher nicht auf TCP ausweichen. Es ist sicherlich eine feine Sache sich um nichts kümmern zu müssen, weil schon etwas anderes das tut, sei es nun eine library oder das Protokoll, das man verwendet, aber was lerne ich denn dabei?

Zumal ich nicht umsonst ~2 Monate Konzeption in den Wind schieße weil ich das System nun auf TCP umbaue. Also, danke für die Antwort, aber ich bleibe bei UDP.

Und letztlich ist TCP und UDP doch alles dasselbe, vom Grundaufbau her, oder nicht? Also warum sollte ich nicht das was TCP schafft auch mit UDP schaffen?

Ach und wenn du schon das mit dem steten Strom erwähnst... kommt es nicht einem steten Strom gleich, wenn ich bis zu 11 Pakete innerhalb von 33 Sekunden an den Server bzw. den Client schicke und auf eine Antwort der Gegenstelle warte? An dieser Stelle möchte ich dann auch nochmal auf die Sache mit der Zeitspanne hinweisen. Ich erwarte ja nicht dass ein Paket sofort ankommt, so weit habe ich schon gedacht bzw. programmiert, aber diese Ausfallrate die jetzt auftritt ist doch nichtmal für UDP (das Protokoll an sich) normal. :?
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Gremlin hat geschrieben:Und letztlich ist TCP und UDP doch alles dasselbe, vom Grundaufbau her, oder nicht?
Nein, ist es nicht. Aber das hätte dir nach Lektüre von Wikipedia-Artikeln oder ähnlichem schon klar sein müssen.
Gremlin hat geschrieben:Also warum sollte ich nicht das was TCP schafft auch mit UDP schaffen?
Anscheinend sind deine Korrektur- und Absicherungsmechanismen nicht so ausgeklügelt wie die von TCP. Weswegen es dir ja auch immer und immer wieder empfohlen wird.
Gremlin hat geschrieben:Zumal ich nicht umsonst ~2 Monate Konzeption in den Wind schieße weil ich das System nun auf TCP umbaue.
Erläutere doch mal was an Refactoring verkehrt sein soll wenn es den Code erheblich verbessert.
Zuletzt geändert von webspider am Samstag 24. März 2012, 12:14, insgesamt 1-mal geändert.
BlackJack

@Gremlin: Also bei einer „jetzt habe ich schon so viel unnütze Zeit verschwendet, da will ich dann noch mehr verschwenden”-Einstellung kann man Dir echt nicht mehr helfen. Was Du daraus gelernt haben solltest, ist dass man für so etwas TCP verwendet und das nicht selber, aber in schlechter — da nicht funktionierend – nachbaut.

TCP ist mit UDP nachbaubar, aber was ist der Sinn davon? Das TCP den Strom garantiert liegt daran, dass es über Sequenznummern und Zeitüberschreitungen erkennen kann wenn Pakete verloren gegangen sind, welche das sind, über Steuerpakete signalisiert das bestimmte Datenpakete noch einmal neu gesendet werden müssen, und Schlangen für die Auslieferung an die Anwendung verwaltet, wo Datenpakete zwischengelagert werden wenn in der Sequenz welche Fehlen. Das müsstest Du alles nachbauen.

Falls das eine Produktivanwendung ist, macht es absolut keinen Sinn da nicht etwas fertiges, getestetes, ja einen *Standard*, zu verwenden. Sollte es nur ums Lernen gehen, dann schau Dir halt wie das bei TCP gelöst ist, und implementiere dass dann nach. Es läuft letztendlich auf TCP hinaus.
deets

@Gremlin

Du hast das falsche Tool gewaehlt - nun steh dazu, und korrigier deine Entscheidung. Das gehoert zum programmieren nunmal dazu. Wenn du alles selbst programmieren willst (warum benutzt du dann Python und nicht assembler, btw?) - dann mache es um Himmels willen, aber dann hol dir Literatur zu dem Thema und implementier TCP halt nach. Nur frag hier nicht nach Hilfe, denn Beratungsresistente zu unterstuetzen hat hier niemand Zeit & Lust zu.
Antworten