Immer broken pipe bei TCP?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Habe dieses Socket Server Beispiel für TCP ausprobiert: https://docs.python.org/3.3/library/socketserver.html
Hier nochmals:

Code: Alles auswählen

import socketserver

HOST, PORT = "localhost", 9999

class MyTCPHandler(socketserver.BaseRequestHandler):

    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # just send back the same data, but upper-cased
        self.request.sendall(self.data.upper())

server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
Der Client dazu wird nur einmal aufgerufen. Ich habe es aber in einer Schleife gemacht:

Code: Alles auswählen

import socket

HOST, PORT = "localhost", 9999

while True:
	sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	sock.connect((HOST, PORT))
	data = input(">")
	if len(data) == 0: break
	sock.sendall(bytes(data + "\n", "utf-8"))
	# Receive data from the server and shut down
	received = str(sock.recv(1024), "utf-8")
	print("Received: {}".format(received))
	sock.close()
sock.close()
Das Problem ist, damit ich nicht einen Abbruch wegen 'brocken pipe' bekomme, muss ich da jedesmal in der Schleife die Verbindung neu aufmachen. Aber das will ich ich nicht und darf ja wohl nicht sein. Mit UDP gab es dieses Problem nicht.

Was muss ich tun, damit ich nicht jedes mal neu verbinden muß?

Also, wenn ich es anders mache, geht es beim ersten Mal, beim zweiten Mal kommt nichts und beim dritten Mal kommt der Abbruch.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Den Fehler bekommt man wenn die andere Seite die Verbindung geschlossen hat.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@DasIch Ich sehe aber nicht, dass da der Server die Verbinding schließt. Oder macht er das automatisch in dieser Konfiguration? Und muss man da einen anderen Mode machen?
Ich möchte ja keine Zeit verplempern mit jedesmal Verbindungsaufbau.

Ich stelle mir eben vor, dass die Verbindung dauerhaft gestehen bleibt, auch wenn ich lange nichts tue. Oder stelle ich mir das falsch vor?
BlackJack

@Alfons Mittelmeyer: Du musst halt in der `handle()`-Methode auch eine Schleife schreiben und dann ein Protokoll implementieren.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Du musst halt in der `handle()`-Methode auch eine Schleife schreiben und dann ein Protokoll implementieren.
Ach ist doch wohl doch komplizierter. Und wenn dann auch andere Verbindungen von anderen Geräten kommen, bleibt das dann in dieser Schleife und die anderen kommen nicht dran?

Oder brauche ich dann pro Verbindung einen Thread?
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Alfons Mittelmeyer hat geschrieben:Oder brauche ich dann pro Verbindung einen Thread?
Man kann eine Thread pro Verbindungen nutzen, muss man aber nicht. Man könnte grundsätzlich auch etwas asynchrones mit non-blocking IO machen. Die Frage die sich dabei allerdings stellt ist inwieweit das socketserver Modul dabei mitspielt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@DasIch Meinst Du bei Thread oder asynchron? Thread müßte gehen, ist ja sowieso alles Thread auf einem System.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

In beiden Fällen. Für Netzwerk Sachen gibt es nicht ohne Grund Frameworks wie Twisted oder asyncio.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

Alfons Mittelmeyer hat geschrieben:@... ist ja sowieso alles Thread auf einem System.
Das ist nicht ganz richtig, auf modernen Betriebssystemen sind das Prozesse, was Du meinst. Threads sind eine Subgruppierung von Instruktionen eines Prozesskontextes in unabhängig ausführbare Teile. Speichermanager und Scheduler für Prozesse können relativ simpel gehalten werden, da eine strenge Trennung der Prozesse vorausgesetzt wird. Für Threads gilt das nicht mehr, was den Aufwand kernelseitig massiv in die Höhe treibt. Auf Anwendungsprogrammiererseite wird das über eine zusätzliche API realisiert, über die man an Threadprimitive wie Mutex etc. rankommt (z.B. pthreads in Linux oder GCD in OSX). Python versteckt das hinter dem thread-Modul.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Je nachdem, ob man nur eine gelegentliche Verbindung braucht, ob nur wenig Daten zu übertragen sind, oder es auf Schnelligkeit ankommt, muss man es wohl unterschiedlich handhaben. Da kann man dann vorest auch mal die Einfachste nehmen.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Alfons Mittelmeyer hat geschrieben:Je nachdem, ob man nur eine gelegentliche Verbindung braucht, ob nur wenig Daten zu übertragen sind, oder es auf Schnelligkeit ankommt, muss man es wohl unterschiedlich handhaben. Da kann man dann vorest auch mal die Einfachste nehmen.
Threads sind definitiv nicht das Einfachste. Ich persönlich bin ein Fan von gevent, das liefert dir alle Tools mit, ohne dass du auf Synchronisierungen etc. groß zu achten hast, nicht davon zu sprechen, dass es sich mit so gut wie jeder anderen Bibliothek kombinieren lässt (monkey patching, aber es geht).
the more they change the more they stay the same
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Dav1d Ich will mich nicht mit einer Menge von Tools befassen, wenn ich nur etwas einfaches will. Außerdem ist das Problem gelöst. Ich nehme Steuerzeichen für den Server:

b'\01' bedeutet, der Server soll in eine Loop gehen
b'\00' bedeutet der Server soll aus der Loop wieder heraus

Wenn der Client die nicht verwendet, funktioniert alles wie es vorher war, also nur einmal senden pro Verbindung

Außerdem kann ich mit readline beliebig viel Daten übertragen, also sogar ein mit json umgewandeltes Python Script, wenn ich das wollte. Und bin damit nicht auf feste Buffergrößen angewiesen.
Und mit socketserver.ThreadingMixIn blockiert eine Verbindung nicht die andere, während die eine auf readline wartet.

client.py:

Code: Alles auswählen

import socket

HOST, PORT = "localhost", 9999

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
sock.sendall(b'\x01'+bytes("\n", "utf-8"))
received = sock.recv(16)

while True:
    data = input(">")
    if len(data) == 0: break
    sock.sendall(bytes(data + "\n", "utf-8"))
    received = sock.recv(16)
    print("Received: {}".format(str(received,"utf-8")))

sock.sendall(b'\x00'+bytes("\n", "utf-8"))
received = sock.recv(16)
sock.close()
server.py:

Code: Alles auswählen

import socketserver

HOST, PORT = "localhost", 9999

def process_data(data):
    print(str(data,"utf-8"))

class MyTCPHandler(socketserver.StreamRequestHandler):

    def handle(self):
        self.data = self.rfile.readline()
        print(self.data[0])
        if self.data[0] == 1:
            self.wfile.write(bytes("OK\n", "utf-8"))
            print("Loop start")
            while True:
                self.data = self.rfile.readline()
                if self.data[0] == 0:
                    self.wfile.write(bytes("OK\n", "utf-8"))
                    print("Loop exit")
                    break
                else:
                    process_data(self.data)
                    self.wfile.write(bytes("OK\n", "utf-8"))
        else:
            print("No Loop")
            process_data(self.data)
            self.wfile.write(bytes("OK\n", "utf-8"))
       
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    allow_reuse_address = True

server = ThreadedTCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
TCP ist eben etwas komplizierter als UDP. Bei UDP braucht man keine Threads, weil es da auch keine Verbindungen gibt, die man aufrecht erhalten will - oder auch nicht.

Eine andere Frage: Warum nehmen sie in diesen Beispielen immer eine Membervariable self.data. Man will sie sich ja nicht lange merken und kann dann genauso gut eine lokale Variable nehmen. Was ist schneller eine lokale Variable oder ene Membervariable. Ich erkenne da keinen Nutzen, wenn man eine Membervariable nimmt.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Alfons Mittelmeyer hat geschrieben:TCP ist eben etwas komplizierter als UDP. Bei UDP braucht man keine Threads, weil es da auch keine Verbindungen gibt, die man aufrecht erhalten will - oder auch nicht.
So einen Bullshit hab' ich schon länger nicht mehr gehört.


Finde es auch irgendwie traurig wie du konsequent jeden Tipp von Leuten die offensichtlich mehr Verständnis von der Materie haben als du (z.B. BlackJack) ignorierst. Ich bin raus.
the more they change the more they stay the same
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Dav1d Ich habe genau das gemacht, was Blackjack geraten hat:
@Alfons Mittelmeyer: Du musst halt in der `handle()`-Methode auch eine Schleife schreiben und dann ein Protokoll implementieren.
Ich habe eine Schleife gemacht.

Und jetzt habe ich noch etwas dazu gemacht, nämlich einen Fast Modus:

b'\x01' - in die Loop gehen
b'\x02' - Fast Modus: von der Loop mit response in eine weitere Loop ohne Response gehen. Wenn es nur kurze Messages sind, kann ansonsten die Übertragung lange dauern
b'\x01' - den Fast Modus wieder verlassen
b'\x00' - Verbindung beenden

Und dann muss man ein Protokoll implementieren, das ganz genau zu dem paßt, was man tun will und nicht irgend etwas nehmen, das verwendbar ist.
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: was soll der Server überhaupt können? Da TCP schon von sich aus reliable ist, braucht man nicht alles mit einem sinnfreien Ok bestätigen. Was gesendet wird, kommt auch an, oder man bekommt ein broken Pipe. Genauso die Sache mit dem loop. Nromalerweise sendet man Befehle, bis man keine Lust mehr hat und sendet dann entweder ein QUIT o.ä. oder man schließt einfach die Verbindung, dann ist auch klar, dass da wohl keine weiteren Befehle mehr kommen.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich denke, Alfons möchte vom Prinzip her eigentlich Sessions implementieren. Er will hier aber vermutlich mal wieder im typischen Alfons-Stil seinen eigenen Weg gehen, anstatt sich auf bewährte Lösungen zu verlassen...
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Daran habe ich auch denken müssen, als ich heute "Fluent Python" von Luciano Ramalho gesichtet habe und dort (Thema Inheritance) die folgende Passage fand:

If, while working as an application developer, you find yourself building multi-level class hierarchies, it’s likely that one or more of the following applies:
  • • You are reinventing the wheel. Go look for a framework or library that provides components you can reuse in your application.
    • You are using a badly designed framework. Go look for an alternative.
    • You are over-engineering. Remember the KISS principle.
    • You became bored coding applications and decided to start a new framework. Congratulations and good luck!
It’s also possible that all of the above apply to your situation: you became bored and decided to reinvent the wheel by building your own over-engineered and badly designed framework which is forcing you to code class after class to solve trivial problems. Hopefully you are having fun, or at least getting paid for it.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack Also es sind zwei Server und zwei Clients. Das eine sind Sender und Empfänger in einem Message Router, das andere sind Sender und Empfänger für Anwendungen.
Bisher hatte ich nur einmal einen Response verwendet. Der Client der Anwendung hatte sich vom Router (localhost,9999) eine Portadresse geben lassen. Aber das ist Käse. Der Client kann ja überalll sein und muss dem Router seine IP und Portadresse geben. Einen Response brauche ich gar nicht. Na macht ja nichts, wenn man das im Router trotzdem drin hat und so einstelllen kann, wie man will. Dann sollte ich aber gleich erlauben in den reponslosen Modus zu gehen und nicht erst von 1 auf 2. Und auf der Anwendungsseite braucht man auch keinen Response. Allerdings ein Response ist nützlich, wenn man Bufferüberläufe vermeiden will.

Aber das wird wohl auch automatisch gehen oder? Ich meine wenn der Netzwerkrouter nicht nachkommt, dann geht einfach das sendall langsamer, oder? Aber da ich keine riesige Mengen von Daten in Höchsteschwindigkeit übertragen will, sollte das gar kein Problem sein.

Es geht nur darum Messages zu übertragen und auch zu empfangen aber nicht als Response. Wenn man etwas will, sendet man einen Request und irgenwann trifft dann eine Antwort ein.
Und diese Antwort nimmt dann der Server der Anwendung in Empfang.

Den Router, den ich bereits habe, möchte ich auch umschreiben, denn es kostet nur Zeit einen Json String mit json.loads zurückverwandeln und danach mit json.dumps wieder zu senden.

Wieviel Zeit kostet es, ein Bytearray in einen String zu verwandeln? Am Anfang des Strings steht das: ["MessageId"
Und diese MessageID muss ich mir herausgreifen, kann ich das auch mit dem Bytearray tun? In C würde ich es machen. Aber mit Python in einer Schleife wäre das dann langsamer als eine Rückverwandlung in einen String, sofern die Message nicht zu lange ist und etliche KB hat. Eventuell greift man sich ja nur maximal die ersten 32 Zeichen heraus und wandelt die dann in einen String.

Im Besten ich speichere das empfangene Bytearray samt abschließendem EOL in der Queue, damit ich es dann ohne Änderung wieder so senden kann.

Gut wäre, wenn der Router zu der Message noch 4 oder 8 Bytes mit einem Hashcode für die Message ID bekäme, dann bräuchte diese nicht in der eigentlichen Message gesucht werden. Und bei konstanter Länge bräuchte man die Kennung einfach nur heraus greifen. Diese Kennung muss einfach nur eindeutig sein und aus der Message ID auf allen Geräten in gleicher Weise gebildet werden können.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

snafu hat geschrieben:Ich denke, Alfons möchte vom Prinzip her eigentlich Sessions implementieren. Er will hier aber vermutlich mal wieder im typischen Alfons-Stil seinen eigenen Weg gehen, anstatt sich auf bewährte Lösungen zu verlassen...
Was heißt, bewährte Lösungen. Ich könnte JSON-RPC verwenden, nur da habe ich einen Response und ich brauche keinen Response. Und dann geht JSON-RCP zu einem Server und ruft da die betreffende Funktion auf. Nur ich will einen Router und ich will auch in der Anwendung zwischen Threads weiterrouten. Warum soll ich einen Riesenaufwand betreiben, um bewährte Lösungen in mein Programm reinzupfriemeln, damit es dann zwar funktioniert, aber nicht optimal.

Ich habe mir den Sourcecode von tinyrpc angesehen und will das nicht so haben.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Also, ich will einfach nur, was ich mit UPD habe auf TCP umstellen und den Router optimieren. Und dabei soll der Router die Message nicht parsen, er braucht nur eine eindeutige ID um sie dann den Empfängern zuordnen zu können.

Bei Java gibt es java.lang.String hash function: https://en.wikipedia.org/wiki/Java_hashCode%28%29

So etwas wäre optimal.

Und dann wäre noch gut, zu routende Messages von einem Router Steuerbefehl zu unterscheiden, weil diesen muss der Router dann konvertieren und mit dem Befehl zu Ausführung versehen.
Die anderen Messages werden dann mit dem Befehl zur Weiterleitung versehen.
Antworten