Klassen, Vererbung und Multihtreaded TCP-Server

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
JohnDoe

Hi, ich hab da ein *kleines* Problemchen, unter Umständen ist auch mein Lösungsansatz falsch.

Vorweg, was ich mir eigentlich basteln wollte:
- Multithreaded Server, wie hier gezeigt.
- Dabei wäre es schön, wenn MyTCPHandler von socketserver.StreamRequestHandler erbt, damit man dateiähnliche Objekte erhält (Wie hier gezeigt)
- Zudem sollte das Programm solange warten bis ein Thread über Netzwerk den Befehl stop() erhält.
- Und das ganze in eine Klasse verpackt mit ein paar weiteren Funktionen :)

Ich habe hier ein Minimalbeispiel, welches natürlich nicht funktioniert:

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-


## Imports
import time
import threading
import socketserver


## Classes
class Test():
    def __init__(self):
        self.running = False
        self.requestStop = False

    def update(self):
        # update...
        pass

    def wait(self):
        while self.running and not self.requestStop:
            time.sleep(5)
        if self.requestStop:
            self.stopNode()

    def startServer(self):
        self.running = True
        self.server = ThreadedTCPServer(('', 0), ThreadedTCPRequestHandler)
        self.serverThread = threading.Thread(target=self.server.serve_forever)
        self.serverThread.daemon = True
        self.serverThread.start()
        print('Port: {}'.format(self.server.server_address[1]))

    def stopServer(self):
        if self.running == True:
            self.server.shutdown()
            self.running = False


class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        data = str(self.request.recv(1024), 'utf-8')
        if data == 'stop':
            # stop server
            pass
        print(data)
        response = bytes('{}'.format(data.upper()), 'utf-8')
        self.request.sendall(response)


class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass


## Application
def main():
    t = Test()
    t.startServer()
    t.update()
    t.wait()
    return 0

if __name__ == '__main__':
    main()
Zum Senden noch die Datei:

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-


## Imports
import sys
import socket


## Application
def main():
    port = int(sys.argv[1])
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect(('', port))
        sock.sendall(bytes('stop', 'utf-8'))
        fsock = sock.makefile()
        msgLine = fsock.readline()
        print(msgLine)
    finally:
        sock.close()
    return 0

if __name__ == '__main__':
    main()
Im Moment ist mein größtes Problem eigentlich, dass der Thread, der "stop" übers Netzwerk erhält, dies an die Klasse Test weitergeben soll. Nur wie?

Hat jemand ne Idee?

mfg
BlackJack

@JohnDoe: Der `BaseRequestHandler` kennt den Server. Es mag total abgefahren erscheinen, aber in der Dokumentation zur RequestHandler.handle()-Methode steht tatsächlich wie man an das `SocketServer`-Exemplar heran kommt, für das der Handler aufgerufen wurde. ;-)

Jetzt musst Du nur noch einen eigenen `SocketServer` ableiten oder vielleicht noch besser die `Test`-Klasse dazu umbauen.
JohnDoe

BlackJack hat geschrieben:Es mag total abgefahren erscheinen, aber in der Dokumentation zur RequestHandler.handle()-Methode steht tatsächlich wie man an das `SocketServer`-Exemplar heran kommt, für das der Handler aufgerufen wurde. ;-)
1+ für den genialen Kommentar :)
BlackJack hat geschrieben:Jetzt musst Du nur noch einen eigenen `SocketServer` ableiten oder vielleicht noch besser die `Test`-Klasse dazu umbauen.
Würde es nicht ausreichen, wenn ich nur den BaseRequestHandler ableite? Müsste auch gehen. Ich probier mal etwas rum

(Btw: Wie werden hier im Forum gelöste Themen für gewöhnlich markiert?)

mfg
BlackJack

@JohnDoe: Den `BaseRequestHandler` hast Du doch schon abgeleitet. Da kannst Du aber nichts dauerhaftes dran binden, weil Exemplare davon nicht von Deinem Code erstellt werden, sondern vom `SocketServer` jedes mal wenn eine Anfrage abgearbeitet werden muss. Der `SocketServer` existiert dabei während der ganzen Laufzeit des Servers. Da müsste man dann also auch Daten und Methoden verfügbar machen, die von allen Anfragen verwendet werden können.
JohnDoe

BlackJack hat geschrieben:@JohnDoe: Den `BaseRequestHandler` hast Du doch schon abgeleitet. Da kannst Du aber nichts dauerhaftes dran binden, weil Exemplare davon nicht von Deinem Code erstellt werden, sondern vom `SocketServer` jedes mal wenn eine Anfrage abgearbeitet werden muss.
Das hab ich auch gerade gemerkt. Muss ich wohl doch vom socketserver ableiten.

EDIT:

Code: Alles auswählen

$ python
Python 3.3.1 (default, Apr  6 2013, 19:03:55) 
[GCC 4.8.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socketserver
>>> class newsocketserver(socketserver):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: module.__init__() takes at most 2 arguments (3 given)
>>> 
Irgendwas mach ich falsch :|
JohnDoe

Hehe :mrgreen:

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-


## Imports
import os
import time
import queue
import threading
import socketserver


## Classes
class Test:
    def __init__(self):
        self.running = False
        self.requestStop = False
        self.queue = queue.Queue()

    def update(self):
        # update...
        pass

    def wait(self):
        data = None
        while not self.requestStop:
            time.sleep(5)
            if self.queue.qsize() > 0:
                print('Queuesize: {}'.format(self.queue.qsize()))
                print('  `-Items: {}'.format(self.queue.get()))
                self.requestStop = True
        self.stopServer()


    def startServer(self):
        self.running = True
        self.server = ThreadedTCPServer(('', 0), ThreadedTCPRequestHandler)
        self.server.setQueue(self.queue)
        self.serverThread = threading.Thread(target=self.server.serve_forever)
        self.serverThread.daemon = True
        self.serverThread.start()
        print('Port: {}'.format(self.server.server_address[1]))

    def stopServer(self):
        if self.running == True:
            self.server.shutdown()
            self.running = False


class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
    def handle(self):
        currentThread = threading.current_thread()
        self.data = self.rfile.readline().strip()
        print("{} :{} wrote: {}".format(currentThread.name, self.client_address[0], self.data))
        if self.data == b'stop':
            print('Stop request')
            self.server.queue.put('Stop')
        self.wfile.write(self.data.upper())


class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    def setQueue(self, queue):
        self.queue = queue


## Application
def main():
    t = Test()
    t.startServer()
    t.update()
    t.wait()
    return 0

if __name__ == '__main__':
    main()
Funktioniert, auch wenn ich die Queue im Moment nur als einfache Variable missbrauche :)

Kritik, Anregungen, Sonstiges?

mfg
Antworten