Socketserver Funktion für Telnet Mailbox verwenden

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
Benutzeravatar
power74
User
Beiträge: 14
Registriert: Donnerstag 3. September 2015, 12:25
Wohnort: in der Nähe von Zürich

Hallo Forum

Ich habe mir (als Anfänger) vermutlich ein zu hohes Ziel gesteckt für den Anfang. Aber ich lerne am besten mit Beispielen, daher dachte ich mir, ich bastle mir einen kleinen Mailboxserver mit der Funktion Socketserver aber ich komme nicht vom Fleck.

Die Idee wäre, dass man sich mit einem Telnet Client am Server anmelden und ein paar Funktionen aus einem Menü auswählen könnte (zB Nachricht schreiben/speichern, Nachrichten lesen), wie man es aus den Mailboxen von anno 1985 kannte.

Nun habe ich mangels Kenntnissen keinen Dunst, wie ich so ein Projekt angehen soll um dabei auch möglichst viel von Python zu lernen.

Zum Nachschlagen und Studieren habe ich mir das Buch von Ernesti und Kaiser in der 4. Auflage besorgt. (eben erst erschienen)

Da ich mich vor allem für die Netzwerkfunktionen interessiere, wollte ich mich dadurch (mit diesem Projekt) motivieren.

Ich habe versucht, aus den Beispielen im Buch etwas weiterzuentwickeln, bisher ohne nennenswerten Erfolg.
Man kann zwar zum Socketserver verbinden aber mehr geht nicht - da stehe ich nun und weiss nicht weiter, da ich keine Beispiele habe um mir ein Bild zu machen, wie sowas aussehen könnte.

Was gebt ihr mir mit auf meinen Weg?

Grüsse Power 74
AttributeError: 'power74' object has no attribute 'nervenbehalten'
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

power74 hat geschrieben:Die Idee wäre, dass man sich mit einem Telnet Client am Server anmelden und ein paar Funktionen aus einem Menü auswählen könnte (zB Nachricht schreiben/speichern, Nachrichten lesen), wie man es aus den Mailboxen von anno 1985 kannte.
Cool!
power74 hat geschrieben:Zum Nachschlagen und Studieren habe ich mir das Buch von Ernesti und Kaiser in der 4. Auflage besorgt. (eben erst erschienen)
Das Buch hat einen eher schlechten Ruf. (BlackJack hat das hier schön niedergeschrieben)

Vorschlag zu deinem Projekt:

Bau das was du willst doch mal lokal. Also mit dem Lesen von der Standardeingabe (bzw. input). Anschließend kann man dann den Netzwerkpart einfach anflanschen.
Dann siehst du auch gleich, ob du Funktionalitäten schön trennst.
BlackJack

@power74: Ergänzend zu sparrows Vorschlag könntest Du Dir das `cmd`-Modul in der Standardbibliothek anschauen cmd — Support for line-oriented command interpreters. Da könntest Du dann einfach die `rfile`- und `wfile`-Attribute für `stdin` und `stdout` verwenden und bräuchtest kaum etwas über das minimale Beispiel aus der `SocketServer`-Dokumentation hinaus benötigen.
Benutzeravatar
power74
User
Beiträge: 14
Registriert: Donnerstag 3. September 2015, 12:25
Wohnort: in der Nähe von Zürich

Hallo sparrow und BlackJack

Vielen Dank für Eure Vorschläge. Da ich als Doppelpapi und Mitglied der arbeitenden Gilde meine Pythonkindheit in der Restmenge meiner Freizeit durchlebe, hoffe ich auf allgemeine Geduld hier im Forum. Ich mache mich immer dann daran, wenns mal ein bisschen Platz hat im Tagesablauf. Auch wenn ich mehrere Tage nicht "präsent" sein sollte, habe ich Euch nicht vergessen :) !

Natürlich bin ich weiterhin offen für Vorschläge aus anderen Ecken des Forumlandes aber ich werde mich baldmöglichst (und erst mal mithilfe des Forums und des Internets) daran machen, Eure Vorschläge umzusetzen. Das Python Buch nehme ich demnach erst als zweite Möglichkeit zur Hand.

power74
AttributeError: 'power74' object has no attribute 'nervenbehalten'
Benutzeravatar
power74
User
Beiträge: 14
Registriert: Donnerstag 3. September 2015, 12:25
Wohnort: in der Nähe von Zürich

Dank Euren Tipps habe ich nun mit dem cmd Modul ein kleines Programm geschrieben, welches zwar noch nicht alles macht, was ich möchte aber es funktioniert ohne Fehler.

Code: Alles auswählen

import cmd, sys

class MeineShell(cmd.Cmd):
    intro = 'Willkommen zur Mailbox. Befehle anzeigen mit help oder ?.\n'
    prompt = ('> ')
    file = None

    def do_info(self, arg):
        'Zeigt eine Info an: INFO'
        print()
        print('Das ist ein textbasiertes System um Nachrichten zu schreiben und zu lesen.')
        print()

    def do_lesen(self, arg):
        'Liest den Text aus der Nachrichtendatei aus: LESEN'
        fobj = open("nachrichten.txt")
        for line in fobj:
            print(line.rstrip())
        fobj.close()

    def do_schreiben(self, arg):
        'Schreibt Text in die Nachrichtendatei: SCHREIBEN'
        fobj_out = open("nachrichten.txt","w")
        line = input("Text eingeben: ")
        print(line.rstrip())
        fobj_out.write(line)
        fobj_out.close()

    def do_bye(self, arg):
        'Beendet das Programm und die Verbindung:  BYE'
        print('Danke, dass Du die Mailbox verwendet hast')
        raise SystemExit

if __name__ == '__main__':
    MeineShell().cmdloop()
Nun möchte ich erreichen, dass dieses Programm über eine Netzwerkschnittstelle an einem Port hört und an diesem Port auf Anrufe wartet. Wie könnte so etwas aussehen?
Wie oben erwähnt, kenne ich den Socketserver bereits, komme aber nicht drauf, wie ich die beiden nun zusammenbringe.

Zusätzlich schwebt mir vor, dass man den Nachrichtenspeicher über die Tastatureingabe beim Lesen und Schreiben via Variable wählen kann und dass man den Prompttext via Variable 1x gleich nach dem Connect wählen kann (sodass der Loginname quasi im Prompt steht). Vielleicht habt ihr dazu noch Ideen für einen Coder-Anfänger wie mich?

(Ich bin nicht nur Python Anfänger, sondern grundsätzlich noch kein Coder - mein Gebiet ist an sich die Systemtechnik/Services - aber coden macht Spass!!!)

Ich freue mich auf Vorschläge aus Eurem Kreis!
power74

ps. ein entferntes Ziel wäre noch, dass dasselbe, was über die Netzwerkschnittstelle ginge, auch über eine RS232 funktionieren würde. Nicht zwingend gleichzeitig, sondern evtl. als paralleles Projekt. Ich habe einen Rechner mit serieller Schnittstelle und Modem, über den ich das auch ausprobieren möchte. Das Modem schickt die Daten dann über Funk an die anrufende Stelle, d.h. die Modemfunktion wird hardwaremässig bereitgestellt.
AttributeError: 'power74' object has no attribute 'nervenbehalten'
BlackJack

@power74: Ein- und Ausgaben solltest Du über die Dateiobjekte `self.stdin` und `self.stdout` machen statt mit `print()` und `input()`. Da wird von `Cmd` normalerweise `sys.stdout` und `sys.stdin` verwendet, aber man kann die Objekte beim erstellen übergeben, und dann Beispielsweise auch das Dateiobjekt für die serielle Verbindung oder die Dateiobjekte die einem beim Handler beim SocketServer zur Verfügung gestellt werden, verwenden.

Das lesen kann man dann mit `shutil.copyfileobj()` machen. Dateien sollte man mit der ``with``-Anweisung öffnen. Dann werden sie auch (deterministisch) geschlossen wenn Beispielsweise eine Ausnahme auftritt.

Das man nur eine Zeile Text eingeben kann beim Schreiben einer Nachricht ist ziemlich wenig. :-)

Zum beenden der `cmdloop()` muss die `do_*`-Methode einen ”wahren” Wert zurück geben statt dem impliziten ”falschen” `None`. Also am besten das ``raise SystemExit`` durch ``return True`` ersetzen.

Ich würde die eigentliche Logik des Programms noch mal von der Benutzerinteraktion mittels `Cmd` trennen. Das geht zwar jetzt noch so ganz gut, aber wenn man anfängt das zu erweitern und Zustand bekommt der nichts mit der Benutzerinteraktion direkt zu tun hat, dann würde ich das nicht mit in die `Cmd`-Klasse packen wollen.

Das `prompt`-Attribut kannst Du ja jederzeit ändern. Das Anmelden könnte man vielleicht in der `preloop()`-Methode unterbringen. Oder man merkt sich einen Anmeldezustand in der Klasse und erlaubt bis der gesetzt ist nur ein 'login'-Kommando. Das überprüfen würde ich dann in `precmd()` machen, denn das muss ja bei allen Befehlen ausser dem Login passieren. Oder man sorgt für das Anmelden noch vor der Verwendung der `Cmd`-Klasse und übergibt den Nutzernamen als Argument beim erstellen des `Cmd`-Exemplars.
Antworten