Simple Erstellung eines Consolen-Chats funktioniert nicht

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
fuuman
User
Beiträge: 6
Registriert: Dienstag 18. März 2014, 08:39

Hallo zusammen,

kurz zu mir: Ich habe mich gerade auf dieser Seite angemeldet, da ich mich in nächster Zeit so intensiv wie möglich mit Python beschäftigen möchte und sicher immer mal wieder Hilfe gebrauchen kann.

Kenntnisse: Ich habe mich vor etwa zwei Wochen das erste Mal mit Python beschäftigt. Bisher habe ich den kompletten Kurs auf codecademy(kennen sicher viele) durchgeackert und ein paar explizite Artikel aus dem Openbook Python gelesen (http://openbook.galileocomputing.de/python/).

Da mich im speziellen Netzwerkprogrammierung sehr interessiert habe ich mich bspw. in Sockets eingelesen und auchs chon erfolgreich ein Server- und ein Client-Script geschrieben, dass es ermöglicht in der Console des Clients auf die Console des Servers über einen UDP-Socket Nachrichten zu schreiben. Nächster Schritt war nun der Gedanke an ein Chatprogramm innerhalb der Console. Ich fragte mich wie ich quasi zwei unendliche while-Schleifen für das Lauschen auf empfangene Nachrichten und das Versenden der geschrieben gleichzeitig laufen lassen könnte. Dadurch stieß ich schnell auf Threads und arbeitete mich auch in das Kapitel des Openbooks ein.

Anschließend entstand folgender Code.

Code: Alles auswählen

import socket, time, datetime, threading

target_ip = raw_input("Empf-IP: ")
port = 48899

udp_socket = socket.socket( socket.AF_INET,  socket.SOCK_DGRAM )

class SendThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        
    def run(self):
        while True:
            message = raw_input("> ")
            udp_socket.sendto(message, (target_ip,port))

class ReceiveThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        while True:
            data, addr = udp_socket.recvfrom(1024)
            print data

my_threads = []

send_thread = SendThread()
recv_thread = ReceiveThread()

my_threads.append(send_thread)
my_threads.append(recv_thread)  

for thread in my_threads:
    thread.start()

for thread in my_threads:
    thread.join()
Leider schließt sich das Fenster sofort nach Eingabe der Empfänger-IP mit folgender Exception:
> Exception in thread Thread-2:
Traceback (most recent call last):
File "C:\Python27\lib\threading.py", line 810, in __bootstrap_inner
self.run()
File "D:\python\chat.py", line 24, in run
data, addr = udp_socket.recvfrom(1024)
error: [Errno 10022] Ein ung³ltiges Argument wurde angegeben
Mir fehlt bisher einfach die Erfahrung mit Threads zu arbeiten und hoffe hier Hilfe zu bekommen.

Vielen Dank für das Lesen meines ersten Posts.

Gruß fuuman
BlackJack

@fuuman: Das hat überhaupt nichts mit den Threads zu tun sondern einzig und alleine mit dem Socket. Wenn Du `recvfrom()` aufrufst, auf welchem Port wartet das `udp_socket` denn auf ein UDP-Paket? Wo, in welcher Programmzeile legst Du das fest?

Eigene Klassen sind hier nicht wirklich notwendig, denn wenn die `__init__()` nur die Basisklasse initialisiert und die `run()`-Methode die einzige Methode ist, dann kann man auch einfach eine Funktion und eventuelle Argumente beim erstellen eines `Thread()`-Exemplars übergeben. Es sind auch keine zwei neuen Threads nötig, denn einen gibt es ja grundsätzlich immer schon. Der ist in dem Code jetzt nur noch mit sinnlosem warten ”beschäftigt”.

Das Hauptprogramm sollte in einer Funktion verschwinden. Auf Modulebene sollte man nur Definitionen von Konstanten, Funktionen, und Klassen haben. Das schliesst dann auch automatisch aus, dass man in Funktionen und Methoden auf etwas veränderliches zugreift was nicht als Argument übergeben wurde. So etwas erzeugt unübersichtliche Abhängigkeiten und verhindert das man Teile isoliert testen oder wiederverwenden kann.

Die Zeilen 26 bis 32 sind eine reichlich umständliche Umschreibung für folgenden Einzeiler:

Code: Alles auswählen

my_threads = []
 
send_thread = SendThread()
recv_thread = ReceiveThread()
 
my_threads.append(send_thread)
my_threads.append(recv_thread)

# ->

threads = [SendThread(), ReceiveThread()]
Da habe ich dann auch gleich mal den unsinnigen `my_`-Präfix entsorgt. Der sagt hier absolut nichts aus.

Edit: Ungetestet und ohne den Socket-Fehler zu beseitigen:

Code: Alles auswählen

#!/usr/bin/env python
import socket
from threading import Thread

PORT = 48899


def receive(udp_socket):
    while True:
        data, _addr = udp_socket.recvfrom(1024)
        print data


def main():
    target_ip = raw_input('Empfaenger-IP: ')

    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    recv_thread = Thread(target=receive, args=[udp_socket])
    recv_thread.start()

    while True:
        message = raw_input('> ')
        udp_socket.sendto(message, (target_ip, PORT))


if __name__ == '__main__':
    main()
fuuman
User
Beiträge: 6
Registriert: Dienstag 18. März 2014, 08:39

Danke für deine Mühe und viele neue Erkenntnisse.

Socket-Problem habe ich durch

Code: Alles auswählen

udp_socket.bind(my_ip, port)
behoben, richtig?


'Was macht dieser Code?

Code: Alles auswählen

if __name__ == '__main__':
    main()
BlackJack

@fuuman: Der Name `__name__` ist an den Modulnamen gebunden. Ausser wenn man das Modul als Programm ausführt, dann ist der Name an den Wert '__main__' gebunden. Du kannst das Modul also als Programm ausführen, oder als Modul importieren ohne das die `main()`-Funktion ausgeführt wird.
fuuman
User
Beiträge: 6
Registriert: Dienstag 18. März 2014, 08:39

Ah danke, ich glaub ich versteh's..^^

Leider bricht das Script immer noch nach Eingabe der target_ip durch einfaches Schließen der Console ab. Ohne Fehlermeldung oder so, Fenster schließt sich einfach.

Ich kann den Code komplett nachvollziehen, aber hab keine wirkliche Idee wo es noch hängen könnte..

Derzeitiger Code:

Code: Alles auswählen

import socket
from threading import Thread

def receive(udp_socket):
    while True:
        data, _addr = udp_socket.recvfrom(1024)
        print data
 
 
def main():
    my_ip = socket.gethostbyname(socket.getfqdn())
    port = 48899 
    target_ip = raw_input('Empfaenger-IP: ')
 
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_socket.bind(my_ip, port)
 
    recv_thread = Thread(target=receive, args=[udp_socket])
    recv_thread.start()
 
    while True:
        message = raw_input('> ')
        udp_socket.sendto(message, (target_ip, port))
 
 
if __name__ == '__main__':
    main()
 
BlackJack

@fuuman: Konsolenprogramme sollte man in einer bereits laufenden Konsole ausführen. Mach das mal, dann siehst Du auch die Fehlermeldung.
fuuman
User
Beiträge: 6
Registriert: Dienstag 18. März 2014, 08:39

Macht Sinn, danke!

Folgender Code klappt gerade nicht. Komplett trivial, aber es funktioniert nicht. Ich versteh es einfach nicht..

Code: Alles auswählen

name = raw_input('Nickname: ')
  
    while True:
        message = raw_input('%s: ') % (name)
Ausgabe:

Code: Alles auswählen

"%s: "
//edit: Hab's! Ich Depp!^^
diogenes
User
Beiträge: 1
Registriert: Samstag 23. August 2014, 13:47

Weil fuuman keine Lösung für seinen letzten Beitrag geschrieben hat - Wollte ich zur Vervollständigung diese schreiben, auch mitunter, weil ich den selben Fehler gemacht hatte.

Code: Alles auswählen

name = raw_input('Nickname: ')

while True:
	message = raw_input('%s: ' % name)
Antworten