Seite 1 von 1

Klassen, Vererbung und Multihtreaded TCP-Server

Verfasst: Sonntag 19. Mai 2013, 19:45
von 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

Re: Klassen, Vererbung und Multihtreaded TCP-Server

Verfasst: Sonntag 19. Mai 2013, 19:59
von 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.

Re: Klassen, Vererbung und Multihtreaded TCP-Server

Verfasst: Sonntag 19. Mai 2013, 22:43
von 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

Re: Klassen, Vererbung und Multihtreaded TCP-Server

Verfasst: Sonntag 19. Mai 2013, 22:58
von 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.

Re: Klassen, Vererbung und Multihtreaded TCP-Server

Verfasst: Sonntag 19. Mai 2013, 23:06
von 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 :|

Re: Klassen, Vererbung und Multihtreaded TCP-Server

Verfasst: Sonntag 19. Mai 2013, 23:36
von 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