Seite 1 von 1

Socket + Makefile

Verfasst: Dienstag 14. August 2012, 13:47
von TagiruAkashi
Hi

Ich versuche mir gerade einen kleinen Client + Server zu schreiben zwischen dennen ich ein Objekt einer selbstgeschriebenen Klasse hin und herschiebe (ersteinmal ohne tieferen Sinn). Das funktioniert soweit auch schon ganz gut, allerdings sobald ich viele Anfragen an den Client sende, bleiben manche davon unbeantwortet...ohne Fehlermeldung bzw. Exception.

Anbei mal der Quellcode - hoffe mir kann jemand helfen....vielleicht habe ich auch einfach etwas grundlegendes Falsch gemacht - habe bisher in Python noch nicht mit Sockets programmiert.

Der Server schickt 5 Sachen an den Client...davon kommen aber meistens nur 1 und 2 zurück - manchmal auch 1,2 und 4

Gruß Tagiru

# Client

Code: Alles auswählen

class Worker(Thread):

    def __init__ (self):
        # Threading
        Thread.__init__(self)
        self.__event = Event()
                
        # Connect to Server (Sending)
        self.__scon = socket.socket(socket.AF_INET, socket.SOCK_STREAM)        
        self.__scon.connect(('127.0.0.1', 55555))
        self.__scon.send('WILL SEND')
        
        # Connect to Server (Receiving)
        self.__rcon = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__rcon.connect(('127.0.0.1', 55555))
        self.__rcon.send('WILL RECV') #

        # Something
        self.__jobs = list()
        self.__runstate = True
        
        # Start Job Listener
        listener = Thread(target=self.__receive)
        listener.start()
        

    def run(self):
        time.sleep(5)
        while self.__runstate:
            if len(self.__jobs) == 0:
                self.__event.wait()
                continue
        
            self.__send({'type' : self.__jobs[0]['type'], self.__jobs[0]['obj']})            
            self.__jobs.remove(self.__jobs[0])
        
    
    def __send(self, tmdb_obj):
        obj = self.__scon.makefile('wb')
        pickle.dump(tmdb_obj, obj, pickle.HIGHEST_PROTOCOL)
        obj.close()


    def __receive(self):
        while self.__runstate:
            obj = self.__rcon.makefile('rb')
            data = pickle.load(obj)
            obj.close()
            self.__add_job(data)
    
    
    def __add_job(self, job):        
        print "JOB: " + str(job['type'])        
        self.__jobs.append(job)

        self.__event.set()
        self.__event.clear()

w = Worker()
w.start()

# Server (leider momentan etwas unstrukturiert, da Teile aus einem Tut stammen + ich noch am probieren bin)

Code: Alles auswählen

soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.bind(('127.0.0.1',55555))
soc.listen(5)

class ABC(threading.Thread):
    def __init__(self,c):
        threading.Thread.__init__(self)
        self.conn = c
        self.stopIt=False


    def mrecv(self):
        f = self.conn.makefile('rb')
        data = pickle.load(f)
        f.close()
        return data
        

    def run(self):
        while not self.stopIt:
            msg = self.mrecv()
            print 'recieved-> ',msg

def setConn(con1,con2):
    dict={}
    state = con1.recv(9)
    con2.recv(9)
    if state =='WILL RECV':
        dict['send'] = con1 # server will send data to reciever
        dict['recv'] = con2
    else:
        dict['recv'] = con1 # server will recieve data from sender
        dict['send'] = con2
    return dict

def msend(conn,msg):        
    test_entry = obj()
    
    tmdb_obj = {'type' : 'tmdb_country', 'obj' : test_entry}    
    obj = conn.makefile('wb')
    pickle.dump(tmdb_obj, obj, pickle.HIGHEST_PROTOCOL)
    obj.close()
    
    tmdb_obj2 = {'type' : 'tmdb_release', 'obj' : test_entry}    
    obj2 = conn.makefile('wb')
    pickle.dump(tmdb_obj2, obj2, pickle.HIGHEST_PROTOCOL)
    obj2.close()

    tmdb_obj3 = {'type' : 'tmdb_alternatetitle', 'obj' : test_entry}    
    obj3 = conn.makefile('wb')
    pickle.dump(tmdb_obj3, obj3, pickle.HIGHEST_PROTOCOL)
    obj3.close()

    tmdb_obj4 = {'type' : 'tmdb_language', 'obj' : test_entry}    
    obj4 = conn.makefile('wb')
    pickle.dump(tmdb_obj4, obj4, pickle.HIGHEST_PROTOCOL)
    obj4.close()

    tmdb_obj5 = {'type' : 'tmdb_studio', 'obj' : test_entry}    
    obj5 = conn.makefile('wb')
    pickle.dump(tmdb_obj5, obj5, pickle.HIGHEST_PROTOCOL)
    obj4.close()


(c1,a1) = soc.accept()
(c2,a2) = soc.accept()
dict = setConn(c1,c2)
thr = CThread(dict['recv'])
thr.start()
try:
    while 1:
        msend(dict['send'], raw_input()) # Damit das Teil nicht beim Testen in der Dauerschleife rennt wartet es auf irgendeine Eingabe - raw_input()
except:
    print 'closing'
thr.stopIt=True
thr.conn.close()
soc.close()

Re: Socket + Makefile

Verfasst: Dienstag 14. August 2012, 13:55
von deets
Bitte gewoehn dir mal die unnoetigen __ vor allem moeglichen ab. Dieses Feature ist nicht fuer "private"-Deklarationen gemacht, sondern um Namenskollisionen zu vermeiden. Und so viel Privatheit ist eh komisch - wenn ich will, komm ich eh ran, als Nutzer deiner Library.

Zu deinem Problem: ich vermute mal, dass du darueber stolperst das sockets keine Messages kennen. Sondern nur Byte-Stroeme abstrahieren. Das heisst, dass man ein Protokoll definieren muss (wie zB HTTP das macht), um die Laenge einer versandten Nachricht zu kommunizieren.

Oft genug geht's erstmal scheinbar ohne, weil eben nur ein Paket verschickt wird mit der Anzahl der richtigen Bytes. Aber wenn dann mal mehr Daten kommen, dann werden die eben nur teilweise oder ueberlappend gelesen - und dann kracht's.

Du solltest zB ein Anfangs-token definieren, mit einer Laengenangabe in Bytes danach gefolgt zB von einem newline (das ist das, was der HTTP header auch macht), und dann eben Laenge-an-Daten schicken. Auf der client-Seite wartest du auf den Header, dekodierst die zu lesende Byte-Zahl, und liest so lange, bis du genug Bytes hast. UU mit timeout, aber das ist Sahnehaube fuer spaeter.

Re: Socket + Makefile

Verfasst: Dienstag 14. August 2012, 14:05
von TagiruAkashi
Bitte gewoehn dir mal die unnoetigen __ vor allem moeglichen ab. Dieses Feature ist nicht fuer "private"-Deklarationen gemacht, sondern um Namenskollisionen zu vermeiden. Und so viel Privatheit ist eh komisch - wenn ich will, komm ich eh ran, als Nutzer deiner Library.
Ok, da war ich wohl falsch Informiert - das man als Nutzer der Library trotzdem dran kommt ist mir klar - ich dachte aber trotzdem das man __ verwendet um (anderen) zu zeigen das die entsprechende Methode/Variable private ist und damit nicht von außen verwendet werden sollte, da es gegebenenfalls zu Problemen kommen könnte.
Zu deinem Problem: ich vermute mal, dass du darueber stolperst das sockets keine Messages kennen. Sondern nur Byte-Stroeme abstrahieren. Das heisst, dass man ein Protokoll definieren muss (wie zB HTTP das macht), um die Laenge einer versandten Nachricht zu kommunizieren.
(...)
Ok verstehe - soetwas hatte ich mir schon Gedacht. Also sollte ich vorher der Gegenstelle immer erst mitteilen wie lang das "Paket" ist das als nächstes kommt. Aber mir ist gerade nicht klar wie ich das am geschicktesten Anstelle - soweit ich weiß serialisiert pickle doch mein Objekt und gibt es gleich an den Socket weiter... um die korrekte Bytelänge zu bekommen müsste ich es also erst sealisieren, dann die Länge von dem Resultat schicken und anschließend das Teil selber - wäre das so richtig?

Danke soweit ;)

Gruß Tagiru

Re: Socket + Makefile

Verfasst: Dienstag 14. August 2012, 14:11
von deets
Privatheit bzw. Implementierungsdetails kann man kennzeichnen, dann aber mit einem einzelnen Unterstrich. Und generell neigen Leute die aus Bondage-Sprachen kommen (wie Java/C++) dazu, davon zu viel zu nuzten.

Was das Problem angeht: ja, du musst halt zB in einen StringIO-Objekt pickeln. Alternativ kannst du auch machen, was MIME macht: einen Header mit einem garantiert in den Daten nicht vorkommenden GUID schreiben, dann die Daten, und dann wieder die GUID. Damit liest der Client erst die GUID, und danach wieder bis zum 2ten auftreten. Das ist bei deinen Datengroessen Geschmackssache, bei Gigabytes von Daten uU etwas praktikabler.

Re: Socket + Makefile

Verfasst: Dienstag 14. August 2012, 14:14
von TagiruAkashi
ok danke - dann werde ich das mal probieren, werde aber vor Donnerstag leider nicht dazu kommen - melde mich dann hier auf jeden Fall nochmal ;)

Gruß Tagiru

Re: Socket + Makefile

Verfasst: Donnerstag 16. August 2012, 19:53
von TagiruAkashi
Hi

Hab das jetzt einmal auf eine simple Art umgesetzt und es scheint zu funktionieren - Danke nochmal ;)

Gruß Tagiru

P.s.: Das mit den __ muss ich noch machen :)

Senden

Code: Alles auswählen

    tmdb_obj = {'type' : 'tmdb_country', 'obj' : test_entry}
    msg = pickle.dumps(tmdb_obj, pickle.HIGHEST_PROTOCOL)
    conn.send(str(len(msg)))
    if conn.recv(2) == 'OK':
        conn.send(msg)
Empfangen

Code: Alles auswählen

        while self.__runstate:
            # length
            data = self.__rcon.recv(4)
            self.__rcon.send('OK')
            
            # obj
            obj = self.__rcon.recv(int(data))
            data = pickle.loads(obj)
            
            # store
            self.__add_job(data)

Re: Socket + Makefile

Verfasst: Donnerstag 16. August 2012, 21:02
von deets
Ist immer noch zu kompliziert finde ich - wozu der handshake? Und du bist auch nicht wirklich robust as wiederaufsetzen angeht: dadurch, dass du immer zwei Bytes einliest um "OK" zu bekommen - was passiert denn, wenn du *ein* Byte verpasst, und dann immer 'KO', 'KO' liest? Zwar ist TCP da recht sicher weil es starke garantien gibt, aber ich wuerde immer versuchen zB mittels newlines zeilen zu schicken, die du dann als ganzes analysierst. Also lesen bis zur naechsten newline, und dann schauen, ob's ein valides Kommando mit "OK<bytezahl>" ist. So macht HTTP das auch.

Re: Socket + Makefile

Verfasst: Donnerstag 16. August 2012, 21:55
von TagiruAkashi
Ich werde mich noch einmal dran setzen und es mir noch einmal in Ruhe anschauen - danke für dein Feedback - werde mich sicher die Tage noch einmal melden.

Re: Socket + Makefile

Verfasst: Freitag 17. August 2012, 08:06
von lunar
@deets: Bei einer TCP-Verbindung kann man keine Bytes „verpassen“. ".recv(2)" gibt nach dem Öffnen der Verbindung immer die ersten beiden Bytes zurück, die die Gegenstelle gesendet hat. Das System reicht immer nur vollständigen Payload an den Prozess weiter.

Re: Socket + Makefile

Verfasst: Freitag 17. August 2012, 08:29
von BlackJack
@lunar: Es kann nicht passieren das `recv(2)` nur *ein* Byte liefert weil die zwei Bytes gerade auf einer Paketgrenze waren und das zweite der beiden Pakete noch nicht angekommen ist? Soweit ich das verstanden habe gibt `recv(x)` nur die Garantie das höchstens x Bytes zurückgegeben werden, aber nicht dass es *genau* x sind. Auch wenn da noch Daten von der Gegenseite kommen.

Re: Socket + Makefile

Verfasst: Freitag 17. August 2012, 08:46
von lunar
@BlackJack: Stimmt, daran habe ich nicht gedacht. Man muss ".recv(2, socket.MSG_WAITALL)" verwenden, um sicher zu gehen, dass man auch tatsächlich zwei Bytes erhält.

Ich hatte deets' Beitrag so verstanden, dass Bytes im Netzwerk verloren gegangene Pakete auf Python-Seite nicht ankommen würden, sprich das ".recv()" beispielsweise das erste und das dritte Byte zurückgeben könne, weil das zweite Byte verloren gegangen ist. Das kann nicht passieren.

Re: Socket + Makefile

Verfasst: Sonntag 19. August 2012, 21:46
von TagiruAkashi
Hi

Kann es sein das es MSG_WAITALL nur unter Linux gibt?

Gruß Tagiru

Re: Socket + Makefile

Verfasst: Sonntag 19. August 2012, 21:59
von lunar
@TagiruAkashi Diese Option ist POSIX-Standard, mithin sollte sie auf jedem Unix-System verfügbar sein.

Re: Socket + Makefile

Verfasst: Sonntag 19. August 2012, 22:16
von TagiruAkashi
ok danke ;) - Ich hab meine Frage wohl schlecht formuliert - kann es sein das es diese Funktion nicht unter Windows gibt? - Ich arbeite mit beiden Systemen.

Gruß Tagiru

Re: Socket + Makefile

Verfasst: Sonntag 19. August 2012, 22:17
von deets
Wenn sie unter Windows nicht verfuegbar ist, dann spielt das auch keine Rolle - denn du liest halt Daten ein, bufferst sie, und schaust in dem buffer nach, ob deine start-bedingung erfuellt ist.