Externe Übergabe von Parametern an mein Python-Skript

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
BlackJack

@Alfons Mittelmeyer: Wie schon gesagt: UDP-Pakete müssen nicht beim Empfänger ankommen und das ist nicht nur theoretisch so, sondern kommt in der Praxis durchaus vor. Ein schöner Grund etwas vorhandenes zu nehmen: Das funktioniert zuverlässig und ist schon in der Praxis getestet. Und man müsste sich das nicht aus verschiedenen Beiträgen in einem Forum zusammenkopieren wenn man es benutzen möchte.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack Das mit UDP war ja nur mal zum Ausprobieren und sollte auf dem eigenen PC über localhost wohl auch sicher sein. Und für Anderes würde man dann etwas Bewährtes nehme. Da bräuchte man ja nur das Empfangsteil austauschen und ein paar Zeilen im Sendeteil. Hier dieses Sende und Empfangsteil:

Code: Alles auswählen

import socket
import socketserver
import threading
import json
import extern_proxy
import proxy as myproxy

HOST, PORT = "localhost", 8888
MYPORT = 0
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# Sender gets port for receiver ==================================

sock.sendto(bytes("PORT?", "utf-8"), (HOST, PORT))
MYPORT = int.from_bytes(sock.recv(16), byteorder='big')


# RECEIVER ===== receives from router =========================

class MyUDPHandler(socketserver.BaseRequestHandler):
 
	def handle(self):
	   
		message = json.loads(str(self.request[0], "utf-8"))
		extern_proxy.proxy.receive_extern(message)
		socket = self.request[1]
		socket.sendto(bytes('OK', "utf-8"), self.client_address)



class ServerThread(threading.Thread):

    def run(self):
        print("Receiver",HOST,MYPORT)
        server = socketserver.UDPServer((HOST, MYPORT), MyUDPHandler)
        server.serve_forever()

receiverthread=ServerThread()
receiverthread.daemon = True
receiverthread.start()


# SENDER ===== sends to router - own thread, because shouldn't block tranmissions between threads ==


proxy = None

class MyThread(threading.Thread):

    def send_to_router(self,message):
        data=json.dumps(message)
        sock.sendto(bytes(data, "utf-8"), (HOST, PORT))
        sock.recv(16)

    def do_send_to_router(self,message_ids):
        proxy.do_receive_extern(message_ids)
        for mid in message_ids: proxy.do_receive(None,mid,self.send_to_router,True)

    def do_receive_from_router(self,message_ids):
        message = ("ROUTE",(MYPORT,message_ids))
        self.send_to_router(message)

    def run(self):
        global proxy
        self.proxy = myproxy.Proxy(extern_proxy.proxy)
        proxy = self.proxy
        self.proxy.loop()

mythread = MyThread()
mythread.daemon = True
mythread.start()

def do_send_to_router(message_ids):
	proxy.send_extern_highprio("execute_function",lambda: mythread.do_send_to_router(message_ids))
	
def do_receive_from_router(message_ids):
	proxy.send_extern_highprio("execute_function",lambda: mythread.do_receive_from_router(message_ids))
Auszutauschen wäre das, nämlich dass man eine Port und/oder IP Adresse erhält - im Falle eines eigenen Routers.

Wenn man etwas anderes nimmt, geschieht das wohl von selbst. Nur man braucht diese Identifikation, damit der Router die Messages für diese Anwendung dieser Anwendung schicken kann.

Code: Alles auswählen

# Sender gets port for receiver ==================================
sock.sendto(bytes("PORT?", "utf-8"), (HOST, PORT))
MYPORT = int.from_bytes(sock.recv(16), byteorder='big')
Auszutauschen wäre der Empfang. Wichtig ist nur der Ersatz für:

Code: Alles auswählen

message = json.loads(str(self.request[0], "utf-8"))
extern_proxy.proxy.receive_extern(message)
Und auszutauschen im Sendeteil wäre:

Code: Alles auswählen

sock.sendto(bytes(data, "utf-8"), (HOST, PORT))
sock.recv(16)
Erläuterungen:

do_send_to_router erhält eine Liste der Message IDs die zum Router gehen sollen. Diese Message IDs werden eingetragen bei der Zentralen Routing Task für Messageaustausch zwischen Treads, damit diese diese Messages der Queue des Sendeteils übergibt: proxy.do_receive_extern(message_ids)

Dann werden diese Message IDs im Sendeteil eingetragen, damit diese Messages an den Router geschickt werden: for mid in message_ids: proxy.do_receive(None,mid,self.send_to_router,True)

do_receive_from_router sendet an den Router eine Message mit der ID "ROUTE", die Port Adresse des Empfangsteil und eine Liste der Message IDs, die der Router an das Empfangsteil senden soll.
Das Empfangsteil übergibt empfangene Messages der Queue der zentralen Message Routing Task (zwischen Threads) - extern_proxy.proxy

Vielleicht sollte ich bei do_send_to_router noch einen zusätzlichen Parameter machen - den owner (self des Threads) - jetzt auf None gesetzt, für den Fall, dass man einen Thread beendet. Dann kann dieser Thread die im zugeordneten Messages zum Router wieder austragen.

do_receive_from_router gilt allerdings applikationsweit, da der Router nur den Port der Anwendung hat und keine Referenz auf einen Thread. Aber da kann auch nichts passieren. Wenn man diese Messages nicht austrägt, werden sie sowieso intern nicht weiter geleitet.

Implementieren muss ich noch eine Funktion, nämlich die Abmeldung der Applikation beim Router mittels "UNROUTE". Das sollte bei einer GUI Anwendung nach der mainloop geschehen und sollte auf Rückmeldung warten. Also eine Pollschleife für die Rückmeldung. Die Frage ist allerdings, was bei gewaltsamem Abbruch geschieht, etwa Stoppen des Prozesses.

Naja, dann kann der Router eben nicht mehr an diese Port Adresse senden - und könnte in diesem Falle selbst die Message IDs austragen, falls diese noch von einer anderen Anwendung gesendet werden.

Also try für senden und timeout für warten auf Bestätigung und bei den eingetragenen Messages muss man auch den Owner mit berücksichtigen, sonst löscht man diese Messages für alle - zur Zeit werden nur die Callbacks berücksichtigt. Das ist noch ein Fehler - für das externe Routing.

Also beim Router ist das falsch: self.proxy.do_receive(None,"UNROUTE",self.proxy.undo_receiveAll)
Und Messages zum Router müssen auch pro Owner eingetragen werden aber nur einmal aufgerufen werden - damit ein sich abmeldender Thread nicht diese Message auch für andere Thread abmeldet. Aber man kann sie auch drin lassen, stört ja nicht - ist wohl das Einfachste.

Die Lösung wäre statt Callback auch ein Tuple aus Callback und Owner zulassen. Nein, jetzt habe ich es: Ownersdictionary pro Callback mit Referenzzähler auf die Owner bzw. Dictionary empty
Das heißt, dass dann der optionale Parameter auch ein Tuple sein kann, welches zusätzlich ein Owners Dictionary hat.

Das kann ich dann auch mal testen. Zur Zeit kommt ein und dieselbe Message wohl nur bei einer Applikation an, weil die Applikationen die MessageID beim Router überschreiben Ja, ist so!

Also, mir geht es nicht um TCP, UDP oder sonst etwas, sondern um die Signal Framework Logik.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Fall gelöst mithilfe der Funktion do_receive_options: self.proxy.do_receive_options(port,mid,self.do_send,port)
Die option ist hier der letzte Parameter port. Dabei wird der optionale Parameter hier nicht überschrieben, sondern in ein Dictionary für den optionalen Parameter eingetragen.
Der Router kann somit an alle ports für diese Message senden.

Code: Alles auswählen

    # register message id, callback and optional_parameter in the message id dictionary
    def _register_options(self,msgid,callback,optional_parameter=False):
        if msgid not in self._Dictionary: self._Dictionary[msgid] = {}
        if callback not in self._Dictionary[msgid]: self._Dictionary[msgid][callback] = {}
        self._Dictionary[msgid][callback][optional_parameter] = None
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@allrounder93 Hi, hab das Ganze wohl ein wenig übertrieben. Hast Du es jetzt mit TCP gemacht? Und wie sieht Deine Lösung aus?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:Wenn Du jetzt noch einen Router implementierst dann sind wir aber wirklich bei einem System angelangt wo man etwas bereits vorhandenes verwenden kann. Also eine der vielen Message-Queues.
@BlackJack Was wäre denn zu empfehlen, wenn man über TCP/IP, Intranet und Internet gehen will?

Oder reicht es, über TCP zu gehen? Statt Portadresse könnte man IP Adresse + Port nehmen. Dann müßte das auch zwischen verschiedenen Rechnern funktionieren. Hab aber nur eine einzige LAN Verbindung. Oder gibt es eine Smartphone Applikation, zu der man zu Testzwecken etwas übertragen kann?

Gut wäre so etwas wie ein Android Message Router.
Antworten