IRC-Client

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

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.
Nicht_zu_definieren
User
Beiträge: 21
Registriert: Freitag 21. April 2006, 17:01
Kontaktdaten:

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 ;)
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

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.
Zuletzt geändert von Nocta am Freitag 4. Januar 2008, 23:06, insgesamt 1-mal geändert.
Nicht_zu_definieren
User
Beiträge: 21
Registriert: Freitag 21. April 2006, 17:01
Kontaktdaten:

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...)
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

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 :D
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
Nicht_zu_definieren
User
Beiträge: 21
Registriert: Freitag 21. April 2006, 17:01
Kontaktdaten:

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'.
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

Nicht_zu_definieren hat geschrieben: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).
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 ;) :twisted:
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

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 :D
Größer darf's von mir aus sein ... Soll halt funktionieren ;)
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'.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

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 :D
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 :o
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 :D
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.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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 :D
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

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 :D
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 :o

Ist auch OT irgendwie? Hätten wir ja jetzt dann geklärt.
BlackJack

Ups, ja eigentlich sollte es sogar eine 1.3 sein. :oops:
Y0Gi
User
Beiträge: 1454
Registriert: Freitag 22. September 2006, 23:05
Wohnort: ja

In Versionsnummern ist auch 1.20, nicht aber 1.2.0, größer als 1.3 ;)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

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!
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

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.
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.
Antworten