Simpler Chat (Server-Client) Multithreading

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
onur22
User
Beiträge: 19
Registriert: Freitag 28. Dezember 2018, 21:25

Freitag 28. Dezember 2018, 21:34

Hallo,

ich bin aktuell dabei ein ganz einfaches Chat Programm zu schreiben. Das hat auch alles soweit gut geklappt, nun versuche ich gerade gewisse Funktionen, konkret das senden und empfangen von Nachrichten, zu parallelisieren. Damit man quasi eine Nachricht tippen kann und gleichzeitig etwas empfangen kann(recv und send). Dazu habe ich mich nun in Threading und Multiprocessing eingelesen. Beides sollte eig. funktionieren für mein Tool. Leider kriege ich beides nicht zum laufen hin...

In den folgenden Codes könnt ihr ganz unten meine Versuche sehen, multiprocessing oder threading einzusetzen. Das hat leider so nicht geklappt.
Mein Server Code :

Code: Alles auswählen

import socket
import multiprocessing
import easygui
import time
from threading import Thread


port = 
IP
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_socket.bind(('IP', port))


hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
print("Server connects with your IP: " + IPAddr)

print("Server running")
server_socket.listen(10)
(print("Wait for a Message"))
(client_socket, addr) = server_socket.accept()
print(addr)




def ResvMessage():
 while socket.error != None:
  time.sleep(0.2)
  print("Waiting for an answer")
  b = "stop"
  msg2 = client_socket.recv(1024)
  k = (str(msg2, "utf8"))
  print(k)
  if k == b:
   socket.close(server_socket)
   sys.exit()
   print("server close")
  else:
   print("Waiting for more messages")



def Senden():
 while socket.error != None:
  time.sleep(0.2)
  client_socket.send((bytes(input("Type in your Message"), "utf8")))
  msg2 = client_socket.recv(1024)
  k = (str(msg2, "utf8"))
  print(k)

ResvMessage()
Senden()





t = Thread(target=Senden(), args=())
t2 = Thread(target=ResvMessage(), args=())
t.start()
t2.start()


Client:
[code]import socket
import multiprocessing
import easygui
import time
from threading import Thread
 
IP =
port =
 
 
print("Clientporgramm started")
 
 
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
 
hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
print("Server Connects with your IP: " + IPAddr)
 
 
x = client_socket.connect(('IP', port))
 
 
def sendmessages():
    while socket.error != None:
        time.sleep(0.2)
        Nachricht = input("Your Message:")
        if Nachricht != "":
            b = client_socket.send(bytes(Nachricht, "utf8"))
            print(b)
        else:
            print("Type in your Message")
 
 
 
def Recvmessages():
 while socket.error != None:
    time.sleep(0.2)
    msg = client_socket.recv(1024)
    print(str(msg, "utf8"))
    print("Got a Message")
 

 
 

 
t = Thread(target=Recvmessages(), args=())
t2 = Thread(target=sendmessages(), args=())
t.start()
t2.start()
 
Danke schonmal, falls mir jemand helfen kann
alles ist als Einsteiger möglich. Es ist nur die Frage, wie lange es dauert, bis man die nötigen Vorkenntnisse erworben hat.
__deets__
User
Beiträge: 5356
Registriert: Mittwoch 14. Oktober 2015, 14:29

Freitag 28. Dezember 2018, 22:50

multiprocessing geht hier nicht. Ein socket zwischen Prozessen zu teilen ist nicht so ohne weiteres drin. Multithreading geht, aber ich würde dir stattdessen zu select.select oder am besten gleich dem asyncio Framework ab Python 3.5 raten.
Sirius3
User
Beiträge: 9702
Registriert: Sonntag 21. Oktober 2012, 17:20

Freitag 28. Dezember 2018, 23:42

@onur22: als erstes solltest Du Code ohne SyntaxFehler posten. `IP` ist nicht definiert und 'IP' bei bind nicht sinnvoll. Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht einem. socket.error ist niemals gleich None, weil das eine Klasse ist. sys.exit sollte in einem sauberen Programm nicht vorkommen. Dein Socket-Code ist wie 99.999% der Beispiele, die Du im Netz findest, totaler Schrott. Das ist einfach nur kaputt, und ich kann es nicht mehr sehen. `recv` muß damit zurechtkommen, dass pro Aufruf nur 1 Byte zurückgeliefert wird, und dann ist Dein UTF8 kaputt. Und send sendet im Zweifel nur einen Teil Deiner Nachricht, der Rest ist futsch. Und wenn Du dann noch zwei Threads hast, die gleichzeitig Lesen, dann ist auch nicht klar, wer was bekommt.
Sockets sind nicht einfach und Threads auch nicht. Warum glaubt alle Welt, damit ein EINFACHES Chatprogramm schreiben zu können.
onur22
User
Beiträge: 19
Registriert: Freitag 28. Dezember 2018, 21:25

Samstag 19. Januar 2019, 15:15

Hier der fertige Chat(Client, Server ist ähnlich aufgebaut), funktioniert nun so, wie es sein soll. Also falls mal jmd über Google die gleiche Fragen hat, wie ich...

Code: Alles auswählen

def Tkiinter():
    master = Tk()
    Label(master, text="Client").grid()


    Button(master, text='Send', command=Start,).grid(row=7, column=1, sticky=W, pady=4)
    Button(master, text='Baue Verbindung mit dem Chatserver auf', command=Startconn,).grid(row=8, column=1, sticky=W, pady=4)
    #Button(text='Beende das Program', command=exit1,).grid(row=9, column=1, sticky=W, pady=4)
    global e1
    e1 = Entry(master)

    e1.grid()
    e1.insert(index=0 ,string="Type in here your Message")
    global T
    T = Text( height=10, width=50)
    T.grid()
    T.insert(END, "Chat Verlauf")



    mainloop()







def Start():
    first = threading.Thread(target=Send, args=(), name=Send)
    first.start()

def Send():
        Message = e1.get()
        client_socket.send((bytes(" \n" "Client:  " + Message, "utf8")))
        client_socket.send((bytes(" \n" + "Client:  " + Message, "utf8")))
        time.sleep(0.2)
        T.insert(END, "\n" + "Client :" + e1.get())




def Recive():
    while True:
        Message = client_socket.recv(1024)
        print(Message)
        time.sleep(0.2)

def recvmessageprint():
    time.sleep(0.5)
    while True:
        T.insert(END, client_socket.recv(1024))


def Startconn():
    global client_socket
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    hostname = socket.gethostname()
    IP = "''
    try:
     x = client_socket.connect((IP, 2422))
     T.insert(END, "Using Port 1")
    except:
     client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     b = client_socket.connect((IP, 2433))
     T.insert(END, "Using Port 2")
    second = threading.Thread(target=Recive, args=(), name=Recive)
    forth = threading.Thread(target=recvmessageprint, args=(), name=recvmessageprint)

    second.start()
    forth.start()

Tkiinter()
second = threading.Thread(target=Recive, args=(), name=Recive)
forth = threading.Thread(target=recvmessageprint, args=(), name=recvmessageprint)

second.start()
forth.start()
alles ist als Einsteiger möglich. Es ist nur die Frage, wie lange es dauert, bis man die nötigen Vorkenntnisse erworben hat.
Sirius3
User
Beiträge: 9702
Registriert: Sonntag 21. Oktober 2012, 17:20

Samstag 19. Januar 2019, 15:44

@onur22: um ehrlich zu sein, ich hoffe, dass das niemand per Google findet.
Es fehlen die Zeilen mit den *-Importen, die man nicht benutzen sollte. Vergiß dass es »global« gibt, das macht nur Probleme, und löst keine; vor allem nicht in Zusammenhang mit Threads.

Wenn man keinen guten Namen für eine Funktion hat, dann schreibt man am besten einen mit Schreibfehler; genauso gut sind Variablennamen die nur ausgeschriebene Zahlen sind.
Sarkasmus-Ende.

GUIs dürfen nur vom Hauptthread aus verändert werden, nicht in Threads, dafür muß man über Queues kommunizieren.
Eingerückt wird immer mit 4 Leerzeichen pro Ebene, keine nackten Excepts.

Wie schon vermutet, ist der ganze Socket-Code kaputt. Nimm wenigstens eine zeilenbasiertes Protokoll. Dass es zwei Threads gibt, die sich gegenseitig die Bytes wegnehmen und zweimal gesendet wird, hab ich auch noch nie gesehen.

Wenn Du ernsthaft lernen willst, wie man richtig programmiert, kann man Dir hier helfen, bisher hast Du aber nicht den Eindruck erweckt, dass Du Hilfe willst. Falls ich falsch liege, korrigier mich bitte.
onur22
User
Beiträge: 19
Registriert: Freitag 28. Dezember 2018, 21:25

Samstag 19. Januar 2019, 17:29

Sirius3 hat geschrieben:
Samstag 19. Januar 2019, 15:44
@onur22: um ehrlich zu sein, ich hoffe, dass das niemand per Google findet.
Es fehlen die Zeilen mit den *-Importen, die man nicht benutzen sollte. Vergiß dass es »global« gibt, das macht nur Probleme, und löst keine; vor allem nicht in Zusammenhang mit Threads.

Wenn man keinen guten Namen für eine Funktion hat, dann schreibt man am besten einen mit Schreibfehler; genauso gut sind Variablennamen die nur ausgeschriebene Zahlen sind.
Sarkasmus-Ende.

GUIs dürfen nur vom Hauptthread aus verändert werden, nicht in Threads, dafür muß man über Queues kommunizieren.
Eingerückt wird immer mit 4 Leerzeichen pro Ebene, keine nackten Excepts.

Wie schon vermutet, ist der ganze Socket-Code kaputt. Nimm wenigstens eine zeilenbasiertes Protokoll. Dass es zwei Threads gibt, die sich gegenseitig die Bytes wegnehmen und zweimal gesendet wird, hab ich auch noch nie gesehen.

Wenn Du ernsthaft lernen willst, wie man richtig programmiert, kann man Dir hier helfen, bisher hast Du aber nicht den Eindruck erweckt, dass Du Hilfe willst. Falls ich falsch liege, korrigier mich bitte.
Ich versuche meine Ergebnisse nur zu teilen, vielleicht helfen sie ja jemandem. Ich meine in der Coding welt findet man ja so gut wie nie perfekte Lösungen, sondern muss selbst immer was optimieren :) Den Code habe ich ja für niemanden außer mich geschrieben(der code ist auch nur paar Zeilen lang, aber wenn jemand mal sowas sehen will, dann findet mans vielleicht ganz hilfreich. Sicher ist da viel Mist drin, kann das auch löschen, wenn du mir sagst, wie man das hier macht

"GUIs dürfen nur vom Hauptthread aus verändert werden, nicht in Threads, dafür muß man über Queues kommunizieren."
Wird doch bei mir im Hauptthread ausgeführt, oder?

"Wie schon vermutet, ist der ganze Socket-Code kaputt. Nimm wenigstens eine zeilenbasiertes Protokoll. Dass es zwei Threads gibt, die sich gegenseitig die Bytes wegnehmen und zweimal gesendet wird, hab ich auch noch nie gesehen."
Was meinst du mit kaputt? Es funktioniert nach meinem Empfinden genau so, wie es soll

"Wenn Du ernsthaft lernen willst, wie man richtig programmiert, kann man Dir hier helfen, bisher hast Du aber nicht den Eindruck erweckt, dass Du Hilfe willst. Falls ich falsch liege, korrigier mich bitte."
Ich nehme Hilfe sehr gerne an :), dafür bin ich hier. Nur wenn du mal den anderen Beitrag von dir nochmal liest, fällt dir vielleicht auf, wieso es keine Reaktion gibt...
alles ist als Einsteiger möglich. Es ist nur die Frage, wie lange es dauert, bis man die nötigen Vorkenntnisse erworben hat.
__deets__
User
Beiträge: 5356
Registriert: Mittwoch 14. Oktober 2015, 14:29

Samstag 19. Januar 2019, 17:42

Noe, dein Send manipuliert aus dem Thread heraus "T" - das kann beliebig krachen.

Und kaputt ist der Socket-Code weil du faelschlicherweise annimmst, ein Sendevorgang korrespondiert mit einem Paket empfangener Daten. Dem ist so nicht. Sockets sind DatenSTROEME. Da kommen Bytes rausgetroepfelt, und ob die eine komplette Nachricht darstellen, dass musst du mit einem Protokoll sicherstellen. Zuzugebenermassen ist das bei deiner Chat-Implementierung akut noch nicht so relevant, weil jedes Byte das kommt auch immer dargestellt wird - wenn die Nachricht also in mehreren Happen kommt, dann werden die einfach hintereinander gepappt. Aber das ist ganz bestimmt keine Eigenschaft, die dir bewusst ist. Und beim naechsten mal geht das dann halt mal in die Hose.
Benutzeravatar
__blackjack__
User
Beiträge: 3094
Registriert: Samstag 2. Juni 2018, 10:21

Samstag 19. Januar 2019, 17:51

@onur22: Deine Ergebnisse helfen niemandem weil es nur ein weiteres kaputtes Beispiel ist, von denen man im Netz sowieso schon viel zu viele findet. Ob etwas nach Deinem ”Empfinden” funktioniert ist irrelevant wenn es grobe Fehler enthält. Es geht hier nicht um eine perfekte Lösung sondern eine die auch tatsächlich funktioniert und nicht abstürzt oder Datensalat produziert. Dein Empfinden ändert sich sicher wenn das dann mal passiert ist. Man kann auch aus dem 10 Stock springen und bei jeder Etage an der man vorbeifällt sagen ”funktioniert doch”. Bis es unten *Platsch* macht. Und das hätte man oben im 10 Stock schon sehen können, dass das keine gute Idee ist.
“In computing, invariants are ephemeral.” – Alan J. Perlis
Daniel26
User
Beiträge: 17
Registriert: Freitag 2. Juni 2017, 09:29

Mittwoch 6. Februar 2019, 10:08

Moin,

ich beschäftige mich auch gerade mit dem Thema Sockets und Multithreading.
Jetzt habt ihr dem Theradersteller zig-mal an den Kopf geworfen, dass sein Code Sch..... ist. Aber es gibt nicht einen vernünftigen Vorschlag was er besser machen könnte oder wo er nachschauen muss damit er selbst die Lösung findet.

Eigentlich wollte ich auch ein Thema mit meinem Problem eröffnen, aber das lass ich wohl besser bleiben.

Gruß
__deets__
User
Beiträge: 5356
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 6. Februar 2019, 10:27

Sicher kein brillanter Thread. Der Frust meinerseits und der anderen über den nicht enden wollenden falschen Umgang mit sockets und Threads ist natürlich keine Schuld der Anfänger. Auf der anderen Seite ist die “aber es geht für mich”-Attitüde auch nicht gerade Ausweis eines echten Bemühens, das Problem zu verstehen.

Und ganz ohne Hinweise wie man es besser macht ist der Thread nicht. Es kam sowohl der Hinweis auf asyncio zur Vermeidung von Threads, als auch auf zeilenbasierte Protokolle als zumindest bei einem Chat geeignete Methode.
Daniel26
User
Beiträge: 17
Registriert: Freitag 2. Juni 2017, 09:29

Mittwoch 6. Februar 2019, 10:31

Gibts denn irgendwo ein gutes Tutorial wie man mit Sockets arbeitet?
Ich versuche mir gerade einen "Server" zu bauen der Kommandos annimmt, verarbeitet und das Ergebnis zurück gibt. In etwa wie bei einem Telnet-Server.
Dabei sollen verbindungen von mehreren CLients möglich sein.
__deets__
User
Beiträge: 5356
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 6. Februar 2019, 10:38

Ich kenne keines. Wenn du Kontrolle über beide Seiten der Kommunikation hast, dann empfehle ich dir nachdrücklich, eine bestehende Lösung zu benutzen. ZeroMQ oder nanomsg. Letzteres ist mein aktueller Favorit. Die nehmen dir viel von der Komplexität ab, die dir überhaupt nicht bewusst ist.
Sirius3
User
Beiträge: 9702
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 6. Februar 2019, 10:44

@Daniel26: um helfen zu können, braucht man ein konkretes Problem. Es gibt etliche Varianten, wie man einen "simplen Chat" programmieren kann.
Das Beispiel (https://docs.python.org/3/library/async ... ng-streams) ist leider mit read-100Bytes statt readline zu verwenden, was bei einem Chat sinnvoller ist, aber als Einstieg zeigt es schonmal grob, wie es geht.
__deets__
User
Beiträge: 5356
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 6. Februar 2019, 10:55

@Sirius3 was macht das denn besser jenseits der Nutzung von asyncio? Meine ich ganz ernst. Die einkommenden messages sind doch genauso potentiell gestückelt. Klar, readline würde helfen. Und das Writer.drain garantiert vollständiges versenden vor dem schließen. Also 2 von 3 Punkten.

Der Stream Reader hat einiges mehr zu bieten als normale sockets.

https://docs.python.org/3/library/asyncio-stream.html

Zb feste Größen oder bis zu beliebigen trennzeichen lesen.
Sirius3
User
Beiträge: 9702
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 6. Februar 2019, 11:03

@__deets__: genau, das was ich geschrieben habe, die Klasse ist die richtige, das Beispiel zur Anwendung suboptimal.
Antworten