Script mit Tastatur-/Bildschirm Ein-/Ausgaben über Telnet Server 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 ein kleines, simples Script in Python3 geschrieben, das mit Bildschirm und Tastatur interagiert und je nach Taste, die man drückt ein anderes Textfile am Bildschirm anzeigt. (Übungsprojekt für mich)

Nun möchte ich, dass dieses Script auf Verbindungen an Port 23 (Telnet) wartet und sobald ein Client sich verbindet, das Script abläuft. Beim Beenden des Scripts, soll die Verbindung getrennt werden. Optimal wäre natürlich ein multithreaded Serverdienst dafür. Ich habe schon ein bisschen rumprobiert mit Socketserver, das ist ja relativ gut verständlich. Aber wie kombiniere ich nun die beiden Teilaufgaben? Achja, ich bin übrigens kein geübter Coder und mache das aus Spass an der Freude mal eben zwischendurch, daher meine vielleicht etwas holprige Fragestellung.

Hier mein Code, der auf der Konsole soweit wie gewünscht funktioniert:

Code: Alles auswählen

import sys

print("\n" * 100)
fobj = open("txt/welcome.txt", "r")
for line in fobj:
    print(line.rstrip())
fobj.close()

schleife = 1

while schleife == 1:

    main = ""
    while main not in ("x", "i", "n"):
        main = input("<Hauptmenü> e(x)it, (i)nfo, (n)eue Nachricht :")
        if main == "x":
            fobj = open("txt/goodbye.txt", "r")
            print("\n" * 100)
            for line in fobj:
                print(line.rstrip())
            fobj.close()
            schleife += 1
            
        elif main == "i":
            fobj = open("txt/info.txt", "r")
            print("\n" * 100)
            for line in fobj:
                print(line.rstrip())
            fobj.close()

        elif main == "n":
            fobj = open("txt/nachricht.txt", "r")
            print("\n" * 100)
            for line in fobj:
                print(line.rstrip())
            fobj.close()
Im Voraus danke für eure Hilfe,
Marius
Zuletzt geändert von Anonymous am Montag 23. Januar 2017, 22:53, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
AttributeError: 'power74' object has no attribute 'nervenbehalten'
BlackJack

@power74: Wenn man etwas wiederverwenden möchte, dann muss man es so schreiben, das man auch tatsächlich etwas woanders verwenden kann. So wie es jetzt da steht kann man das Modul als Programm laufen lassen, und das war es dann auch schon.

Um das Programm in einem anderen Programm verwenden zu können, muss man es so umschreiben, das man das Modul importieren kann, und dann beispielsweise eine Funktion darin definiert ist, die man aufrufen kann damit das Programm abläuft. Diese Hauptfunktion eines Programms wird üblicherweise `main()` genannt und am Ende des Quelltextes nur aufgerufen wenn das Modul als Programm gestartet wurde, aber *nicht* wenn es als Modul importiert wurde. Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert.

`sys` wird importiert aber nicht verwendet.

Im Programm selber ist schon ein eindeutiger Fall für eine wiederverwenbare Funktion, nämlich die Ausgabe einer Datei. Der gleiche Code steht dort mehrfach mit dem einzigen Unterschied eines Dateinamens. Wenn Du Code kopierst, einfügst, und leicht veränderst, machst Du in 99% der Fälle etwas falsch! Programmierer sind faul und wiederholen weder Code noch Daten. Das ist fehleranfällig wenn man Veränderungen an dem Code vornehmen will oder muss, weil man dann immer an alle Kopien des Code denken und alle gleichartig ändern muss.

Zum Beispiel wäre es besser die Dateien mit der ``with``-Anweisung zu öffnen. Wenn die Ausgabe einer Datei in einer Funktion steht, muss man das *einmal* in dieser Funktion ändern. Ansonsten für jede Stelle im Programm wo eine Datei ausgegeben wird.

`schleife` ist kein wirklich guter Name für einen Wert. Daran wird ja keine Schleife gebunden sondern eine Zahl. Wobei auch das nicht wirklich gut ist da 1 oder 2 dran zu binden, wenn man eigentlich einen Wahrheitswert haben möchte. Dafür gibt es in Python den Typ `bool` mit den Werten `True` und `False`. Allerdings würde ich `schleife` komplett eliminieren und eine ”Endlosschleife” schreiben, die bei Bedarf mit ``break`` verlassen werden kann.

Dazu muss man die sinnfreie innere ``while``-Schleife entfernen.

Ich lande dann als Zwischenergebnis ungefähr hier (ungetestet):

Code: Alles auswählen

def print_file(filename):
    print('\n' * 100)
    with open(filename, 'r') as lines:
        for line in lines:
            print(line.rstrip())


def main():
    print_file('txt/welcome.txt')
    while True:
        choice = input('<Hauptmenue> e(x)it, (i)nfo, (n)eue Nachricht: ')
        if choice == 'x':
            print_file('txt/goodbye.txt')
            break
        elif choice == 'i':
            print_file('txt/info.txt')
        elif choice == 'n':
            print_file('txt/nachricht.txt')


if __name__ == '__main__':
    main()
Was man jetzt noch ändern müsste wäre die hart verdrahtete Ein- und Ausgabe über die Standardein- und ausgaben. Man müsste `main()` zumindest optional als Argumente übergeben können aus welcher Datei gelesen und in welche Datei geschrieben werden soll. Dann könnte man es entweder mit der Standardein- und Ausgabe aufrufen oder beispielsweise mit einem Dateiobjekt das man mit `socket.makefile()` aus einem Socket-Objekt in einem Server erstellt hat.
Benutzeravatar
power74
User
Beiträge: 14
Registriert: Donnerstag 3. September 2015, 12:25
Wohnort: in der Nähe von Zürich

@BlackJack
Vielen Dank, ich werde voraussichtlich nächste Woche weiter an diesem Übungsprojekt arbeiten können. Ich habe bereits jetzt wichtige Erkenntnisse gewonnen durch Deine Hinweise. Gerne werde ich dann hier weiter dazu dokumentieren.
AttributeError: 'power74' object has no attribute 'nervenbehalten'
BlackJack

Mal spasseshalber eine Lösung in Hy die sowohl über die Standardein-/ausgabe kommunizieren kann als auch über die Dateiobjekte die ein `SocketServer.StreamRequestHandler` bietet.

Code: Alles auswählen

#!/usr/bin/env hy
(require [hy.contrib.loop [loop]])
(import
  os
  [SocketServer [StreamRequestHandler ThreadingTCPServer]]
  [sys [stdin stdout]])


(def *text-path* "txt")


(defn make-connection [in-file out-file]
  {:in-file in-file
   :out-file out-file})


(defn send [connection data]
  (doto (:out-file connection)
    (.write data)
    (.flush)))


(defn ask [connection prompt]
  (send connection prompt)
  (.rstrip (.readline (:in-file connection))))


(defn send-file [connection filename]
  (send connection (* "\n" 100))
  (with [message-file (open (os.path.join *text-path* filename) "r")]
    (send connection (.read message-file))))


(defn communicate [in-file out-file]
  (let [prompt "<Hauptmenue> e(x)it, (i)nfo, (n)eue Nachricht: "
        choice->filename {"x" "goodbye.txt"
                          "i" "info.txt"
                          "n" "nachricht.txt"}
        connection (make-connection in-file out-file)]
    (send-file connection "welcome.txt")
    (loop []
      (let [choice (ask connection prompt)
            filename (.get choice->filename choice)]
        (unless (none? filename) (send-file connection filename))
        (unless (= choice "x") (recur))))))


(defclass RequestHandler [StreamRequestHandler]
  (defn handle [self]
    (communicate self.rfile self.wfile)))


(defn run-server [host port]
  (print (.format "Listening on {} port {}. Ctrl+C to stop..." host port))
  (try
    (.serve-forever (ThreadingTCPServer (, host port) RequestHandler))
    (except [KeyboardInterrupt] (print "\nServer stopped."))))


(defmain [&rest args]
  (let [arg-count (len args)]
    (cond
      [(= arg-count 1) (communicate stdin stdout)]
      [(= arg-count 3) (run-server (get args 1) (integer (get args 2)))]
      [True (print (get args 0) "[host port]")])))
In Python würde es sich wohl anbieten aus der Verbindung eine Klasse zu machen.
Benutzeravatar
power74
User
Beiträge: 14
Registriert: Donnerstag 3. September 2015, 12:25
Wohnort: in der Nähe von Zürich

OK, ich danke dir BlackJack. Im Moment muss ich es auf der Seite liegen lassen, werde aber hier weiter schreiben, wenn ich wieder daran tüftle.
Ich hoffe, das ist so erlaubt im Forum aber ich habe derzeit anderes woran ich zuerst weite machen möchte.
AttributeError: 'power74' object has no attribute 'nervenbehalten'
Antworten