Seite 1 von 2
IRC-Client
Verfasst: Freitag 4. Januar 2008, 22:41
von Nocta
Ich hab mir vorgenommen, einen IRC-Client zu schreiben.
Erstmal wollte ich nur ein wenig damit rumspielen und das ganze später vielleicht so strukturieren (evtl. OOP, wenns sinnvoll ist), dass ich bei bedarf einfach ne GUI daufhau'n kann.
Aber wie gesagt, erstmal nur damit rumspielen

Und wie erwartet kommen da schon die ersten Probleme.
Ich poste erstmal das Programm, damit ihr besser nachvollziehen könnt, was ich meine.
Code: Alles auswählen
import socket
import string
HOST="irc.eu.gamesurge.net"
PORT=6667
NICK="PyNocta"
IDENT="PyNocta"
REALNAME="PyJohannes"
s=socket.socket( )
s.connect((HOST, PORT))
while(True):
response = s.recv(1024)
print response
if response != "":
if "Checking Ident" in response:
print "Sending Informations ..."
s.send("NICK %s\r\n" % NICK)
s.send("USER %s %s bla :%s\r\n" % (IDENT, HOST, REALNAME))
if "ERROR :Closing Link:" in response:
break
if "PING" in response:
print "replying to PING ..."
pong = string.split(response, " ")
s.send("PONG %s" % pong[1])
print "PONG %s sent" % string.replace(pong[1], "\r\n", "")
s.close()
Erstmal geht es mir um folgendes Problem:
Ich habe eine Endlosschleife, in der ich daten vom Server lese und darauf reagiere.
Nur wie kann ich jetzt auch noch schreiben?
Entweder eine Endlosschleife zum schreiben, oder eine zum lesen, beides auf einmal geht irgendwie nicht.
Jetzt hab ich von Threads gehört. Wäre das eine (vernünftige) Lösung? Einfach 2 Threads starten, jede eine Endlosschleife?
Oder wie würdet ihr das machen?
Naja ansonsten kann natürlich auch die übliche Kritik kommen

Ich hab mir jetzt sogar angewöhnt Strings und Variablen nicht zu nem + str(var) + zu verunstalten :p
Das ist das erste, was ich mit Sockets mache, vielleicht gibt es ja auch 'ne ganz simple Lösung dafür.
Verfasst: Freitag 4. Januar 2008, 22:49
von Nicht_zu_definieren
So wie das im Moment ausschaut, wirst du darum nie eine GUI basteln können ;)
Die GUI müsste ja irgendwie auf irgendwas zugreifen, hier schickst du alles an sys.stdout ;) (Ja, die GUI könnte dein Programm als Subprocess starten und dein sys.stdout natürlich als Eingabe verwenden...aber wie würdest du was senden?)
Aus dem was ich hier sehe, willst du vermutlich das schreiben per msg = raw_input() machen. Dann ist allerdings schon vorprogrammiert, dass du während dem Schreiben eine Ausgabe bekommst.
Zumindest ein bisschen OOP wäre sinnvoll, eine Klasse die zum Beispiel die Verbindung verwaltet.
Diese Klasse könnte eine Methode mainloop() haben, die einen thread startet der dann alles was vom Server empfangen wird in eine Queue schreibt. Zusätzlich eine Methode send_raw() um bieliebigen Text unbearbeitetzum Server zu schicken, eine Methode send_to_channel(channel, msg) etc.
Das Interface müsste dann immer die Queue auslesen, wohin auch immer das ausgeben und auf deinen Input warten.
Edit: Ausserdem solltest du statt if 'PING' in response lieber folgendes verwenden:
Code: Alles auswählen
if response.lower().startswith('ping ') or response.split(' ')[1].lower() == 'ping'
Denn 1. muss PING nicht immer groß geschrieben sein soweit ich weiss (ist es aber bei eigentlich allen Servern IMHO)
und 2. kommt vom Server folgendes:
<message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
Wobei <command> 'PING' ist.
Bei deiner Methode wird an den Server gepongt wenn ich dir irgendwas schreibe wo PING vorkommt ;)
Verfasst: Freitag 4. Januar 2008, 23:01
von Nocta
1. Mir ist klar, dass ich DAMIT niemals ne GUI hinbekomme

deswegen meinte ich auch, dass ich erstmal "nur rumspiele". Einfach um mich ein bisschen in Sockets usw. einzuarbeiten.
2. OOP hatte ich auch eventuell vor, kommt halt drauf an, wie sinnvoll es dann letztendlich ist. Darüber muss ich dann nachdenken, wenn ich's machen will
Ich hatte ganz vergessen zu fragen, auf was ich denn achten muss, wenn ich das Programm ohne großen Aufwand GUI-erweiterbar machen will. Da ich mich mit GUIs nicht gut auskenne, hab ich keine Ahnung davon :p Wäre blöd wenn ich dann nochmal alles komplett umstrukturieren müsste, um eine GUI dafür zu erstellen.
Die GUI sollte eigentlich nichts im Programm verändern sondern eben nur eine Oberfläche für die Befehle, die man genauso gut als Text schreiben könnte, bieten. Und eben mehr Komfort
Du meinst also, dass Threads eine sinnvolle Lösung wären? Dann müsst ich mir das wohl nur mal anschaun

Aber auf den ersten Blick sah das gar nicht so schwer aus, einfach nur eine Funktion aufrufen, die dann Parallel arbeitet.
Edit:
@ your edit :p
Da hast du natürlich recht

Ich würde jedes mal einen PONG schicken, wenn mir jemand PING schreibt. Daran hab ich noch gar nicht gedacht

den String zu "lowern" ist auch ne gute Idee, aber da im rfc PING und PONG auch großgeschrieben sind, denk ich mal, dass das bei allen Servern so sein wird. Aber natürlich besser das ganze ein bisschen flexibler gestalten als dass man am Ende wieder alles ändern muss, weil irgendwein Server wiedermal ne Ausnahme macht.
Verfasst: Freitag 4. Januar 2008, 23:04
von Nicht_zu_definieren
Entweder threads, oder eventuell ginge es rein theoretisch, auch mit nonblocking-Sockets (würd ich dir aber nicht empfehlen, du erschwerst dir das Leben damit...)
Verfasst: Freitag 4. Januar 2008, 23:11
von Nocta
Hm nonblocking Sockets würden dann im Prinzip alles puffern bis ich den Puffer auslese?
Würde doch exakt das Selbe machen, wie der Thread, den ich dann stattdessen starten würde.
Aber irgendwo wird da ja der Haken dran sein
Aber gibt's nicht irgendwas ohne Massenweise Endlosschleifen? Irgendwie hasse ich die Dinger

Wie machen denn professionelle Chat-Programme das? Die haben ja alle im Prinzip dieselben Probleme wie ich, egal ob IRC, ICQ, MSN, etc.
Auch Threads?
Gibt's vielleicht auch irgendwelche Demo-Messenger die nur die Grundfunktionen beinhalten damit man sich die Funktionsweise anschauen kann? Es gibt natürlich Opensource IRC-Clients die ich mir anschauen könnte aber 1. sind die nicht alle in Python und 2. blicke ich da eh nicht durch
Verfasst: Freitag 4. Januar 2008, 23:18
von Nicht_zu_definieren
Statt Grundmessenger anzuschauen: einfach Python IRC Bibliotheken.
Du kannst dir ja z.B. die python-irclib (
http://python-irclib.sourceforge.net/) und andere IRC-Implementationen für Python anschauen (eine Liste ist unter
http://mail.python.org/pipermail/python ... 13359.html erhätlich).
Nonblocking sockets funktionieren so, dass du bei socket.recv() immer sofort den aktuellen Puffer erhältst, auch wenn er leer ist.
Also: Ja, nonblocking-sockets puffern solange bis du es ausliest, das tun aber blocking-sockets auch...nur dass die halt beim auslesen immer solange warten bis sie dir was zurückliefern.
Wenn du nonblocking-sockets verwendest machst du statt zwei threads die zwei verschiedene Aufgaben haben nur einen Thread mit zwei Aufgaben und du musst selber immer hin- und herwechseln zwischen senden und empfangen.
Ohne Endlosschleifen wird nichts funktionieren, du du musst ja "für immer" senden und empfangen , ausser der User beendet die Verbindung (aber wann das ist kannst du ja nicht im vorhinein wisen).
Edit: (grml, schon wieder was vergessen): Ja, im RFC steht PING, PONG und alle anderen commands auch immer GROSS, aber in section 2.3.1 ("Message format in 'pseudo' BNF"), ist <letter> wie folgt definiert: 'a' ... 'z' | 'A' ... 'Z'. Also ist es laut RFC immer 'PING' oder 'ping' (aber niemals 'Ping' oder 'PiNg'.
Verfasst: Freitag 4. Januar 2008, 23:24
von Nocta
Dann hät ich mir sozusagen alles bis jetzt sparen können und gleich die Python-Lib benutzen können? Okay, ich schau mal

Danke
Nicht_zu_definieren hat geschrieben:Nonblocking sockets funktionieren so, dass du bei socket.recv() immer sofort den aktuellen Puffer erhältst, auch wenn er leer ist.
Also: Ja, nonblocking-sockets puffern solange bis du es ausliest, das tun aber blocking-sockets auch...nur dass die halt beim auslesen immer solange warten bis sie dir was zurückliefern.
Wenn du nonblocking-sockets verwendest machst du statt zwei threads die zwei verschiedene Aufgaben haben nur einen Thread mit zwei Aufgaben und du musst selber immer hin- und herwechseln zwischen senden und empfangen.
Ohne Endlosschleifen wird nichts funktionieren, du du musst ja "für immer" senden und empfangen , ausser der User beendet die Verbindung (aber wann das ist kannst du ja nicht im vorhinein wisen).
Dass ich Endlosschleifen brauche, ist mir schon klar, aber ich hasse sie trotzdem :p
Okay, nonblocking Sockets scheinen hier wohl nicht wirklich was zu ändern. Dann werd ich mir jetzt wohl mal die irclib und threads anschauen.
Ich meld mich dann bald wieder

Verfasst: Samstag 5. Januar 2008, 00:00
von Leonidas
Threads bringen für die Kommunikation nichts, weil es ja nur ein Socket gibt, über das geschrieben wird. Threads werden erst wichtig, wenn es eine UI gäbe. Diese UI würde in einem Thread laufen, die Netzwerkkommunikation in einem anderen.
Was ich aber nutzen würde, wenn ich IRC-Clients implementieren wollte, wären die Module ``asyncore`` und insbesondere für IRC ``asynchat``. Oder wenn es etwas größer sein darf, auch
Twisted.
Verfasst: Samstag 5. Januar 2008, 00:06
von Nocta
Hm ich dachte ich könnte in einem Thread den Socket lesen lassen und im anderem vielleicht schreiben lassen.
Dann könnte ich lesen und schreiben gleichzeitig :p Aber vielleicht denke ich da auch vollkommen falsch, ich hab noch nie Threads benutzt.
Was ich aber nutzen würde, wenn ich IRC-Clients implementieren wollte, wären die Module ``asyncore`` und insbesondere für IRC ``asynchat``. Oder wenn es etwas größer sein darf, auch Twisted.
Die Module müsste ich mir mal anschauen. In Kombination mit der irclib (die anscheinend noch ziemlich unfertig ist, version 0,4.irgendwas) oder ohne?
Twisted hab ich schonmal gehört aber mehr auch nicht

Größer darf's von mir aus sein ... Soll halt funktionieren

Verfasst: Samstag 5. Januar 2008, 11:28
von BlackJack
Woraus schliesst Du das die IRC-lib unfertig ist? An Versionsnummern kannst Du Dich da nicht orientieren.
Mit Socketprogrammierung + Threads hast Du Dir gleich zwei ziemlich verzwickte Themen ausgesucht.
Zum Beispiel musst darfst Du nicht einfach so `recv(1024)` schreiben und davon ausgehen, dass ein komplettes Kommando zurückgegeben wird, oder das es nur eines ist und nicht zwei, oder auch anderthalb.
Du musst solange lesen bis Du das erste '\r\n' gelesen hast, dann alles davor verarbeiten und alles danach als Anfang für das nächste Kommando betrachten. Bis zum nächsten '\r\n'.
Verfasst: Samstag 5. Januar 2008, 17:19
von Leonidas
BlackJack hat geschrieben:Woraus schliesst Du das die IRC-lib unfertig ist? An Versionsnummern kannst Du Dich da nicht orientieren.
Sie funktioniert eigentlich ganz ok, aber ich habe vor Jahren auch einige nicht beim Autor reproduzierbare Probleme gehabt. Für einen simplen IRC-Client reicht sie aber ganz sicher.
Und Twisted Words enthält eine IRC-Implementation. Habe ich mal in einem unfertigen IRC nach Jabber-MUC-Gateway verwendet, aber die Dokumentation ist teilweise echt spärlich.
Verfasst: Sonntag 6. Januar 2008, 16:32
von Nocta
BlackJack hat geschrieben:Woraus schliesst Du das die IRC-lib unfertig ist? An Versionsnummern kannst Du Dich da nicht orientieren.
Naja ich dachte eben, dass alle Versionen unter 1.0 unfertig (was nicht unbedingt verbuggt oder unbrauchbar heißt) sind, und wenn die IRC-lib grad mal bei 0,4 oder 0,5 ist, schließe ich daraus, dass sie noch bei weitem nicht alles implementiert hat, was vorgesehen ist. Okay, du hast recht, das war ein voreiliger Schluss, damit ist ja nicht gesagt, dass die IRC-lib nicht bereits alles hat, was ICH brauche.
Aber auf der Seite selbst steht ja auch
Current limitations:
* The IRC protocol shines through the abstraction a bit too much.
* Data is not written asynchronously to the server (and DCC peers), i.e. the write() may block if the TCP buffers are stuffed.
* Like most projects, documentation is lacking...
Aber ist ja jetzt auch genug zu dem Thema

Wenn Leonidas sagt, es reicht für einen simplen IRC-Client, glaube ich ihm einfach mal. Ich hoff mal das ganze bleibt nicht an der Dokumentation hängen, ich bin eben kein 1337-h4xX0r der ohne vernünftige Erklärungen auskommt.
Mit Socketprogrammierung + Threads hast Du Dir gleich zwei ziemlich verzwickte Themen ausgesucht.
Zum Beispiel musst darfst Du nicht einfach so `recv(1024)` schreiben und davon ausgehen, dass ein komplettes Kommando zurückgegeben wird, oder das es nur eines ist und nicht zwei, oder auch anderthalb.
Du musst solange lesen bis Du das erste '\r\n' gelesen hast, dann alles davor verarbeiten und alles danach als Anfang für das nächste Kommando betrachten. Bis zum nächsten '\r\n'.
Brauch ich denn noch Threads wenn ich die IRC-lib verwende?
Ich weiß ja noch nichtmal genau was die IRC-lib so alles für mich erledigt

Mit dem `recv(1024)` hast du wohl auch recht, bis zum nächstem `\r\n` zu lesen wäre wohl am sinnvollsten.
Ich hab das Gefühl ich laber hier einfach zu viel, sorry

Verfasst: Sonntag 6. Januar 2008, 18:01
von BlackJack
Versionsnummern sind keine Dezimalbrüche. Das sind eigentständige Komponenten die durch einen Punkt getrennt sind. Zum Beispiel ist eine 1.20 grösser als eine 1.1, das wäre bei Dezimalbrüchen nicht der Fall.
In dem oben verlinkten Artikel steht auch noch einmal, dass bei "Open Source"-Projekten Versionen unter 1.0 nicht zwangsläufig "unfertig" sind. Mein `bitrate.py` ist zum Beispiel bei Version 0.3 und, soweit man das bei Software überhaupt sagen kann, ist es fertig. Soll ich jetzt eine neue Version zusammen stellen bei der sich nichts geändert hat, ausser der Versionsnummer? Erscheint mir etwas sinnfrei.
Verfasst: Sonntag 6. Januar 2008, 18:17
von Hyperion
BlackJack hat geschrieben:Zum Beispiel ist eine 1.20 grösser als eine 1.1, das wäre bei Dezimalbrüchen nicht der Fall.
Das sollte sicher "1.2" heißen statt "1.1"? Sonst passt es nicht

Verfasst: Sonntag 6. Januar 2008, 19:08
von Nocta
Hyperion hat geschrieben:BlackJack hat geschrieben:Zum Beispiel ist eine 1.20 grösser als eine 1.1, das wäre bei Dezimalbrüchen nicht der Fall.
Das sollte sicher "1.2" heißen statt "1.1"? Sonst passt es nicht

Wahrscheinlich.
Aber ich hab's ja eingesehen

Eine Version die noch nicht die Versionsnummer 1.0xxxxx hat, kann trotzdem fertig sein und selbst, wenn's das noch nicht ist, trotzdem fertig genug
Ist auch OT irgendwie? Hätten wir ja jetzt dann geklärt.
Verfasst: Sonntag 6. Januar 2008, 21:13
von BlackJack
Ups, ja eigentlich sollte es sogar eine 1.3 sein.

Verfasst: Montag 7. Januar 2008, 10:29
von Y0Gi
In Versionsnummern ist auch 1.20, nicht aber 1.2.0, größer als 1.3

Verfasst: Montag 7. Januar 2008, 13:48
von Hyperion
Y0Gi hat geschrieben:In Versionsnummern ist auch 1.20, nicht aber 1.2.0, größer als 1.3

Ja genau darum ging es ja!!! Aber als Dezimalzahl wäre eben 1.20 eben nicht > 1.3! Das war gemeint!
Verfasst: Montag 7. Januar 2008, 14:23
von mkesper
Y0Gi hat geschrieben:In Versionsnummern ist auch 1.20, nicht aber 1.2.0, größer als 1.3

Da das aber sehr verwirrend ist, sollte man keine Unterversionen größer 9 verwenden.
Verfasst: Montag 7. Januar 2008, 14:32
von BlackJack
Sehe ich nicht so. Besser noch eine .0 anhängen, also 1.20.0, damit die Leute nicht so leicht auf die Idee kommen es wäre ein Dezimalbruch. Das würde ja sonst bedeuten man müsste nach einer 0.9 zwangsläufig eine 1.0 veröffentlichen, auch wenn noch gar nicht alle Ziele erreicht wurden, die man sich dafür gesteckt hat.