Programm doppelt öffnen verhindern

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.
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Das bedeutet, dass der Thread in einer Endlosschleife steckt und beendet werden muss. Ich würde das ganz einfach so lösen, dass sich das Hauptprogramm mit sich selbst verbindet und dem Thread einen speziell definierten String schickt, auf den er sich dann beendet. Versuch (ungetestet):

Code: Alles auswählen

class Miniserver(threading.Thread): 
    def __init__(self,sock): 
        threading.Thread.__init__(self) 
        self.s=sock 
    def run(self): 
        while 1: # Empfange Anfragen vom 2. Programm 
            conn, addr = self.s.accept() 
            print 'Connected by', addr 
            while 1: 
                data = conn.recv(1024) 
                if not data: break 
            conn.close()
        if data == "zu Ende" and self.quit:
            break
            Funktion(data)
Das self.quit in der Abfrage hat den Zweck, dass der Thread nicht von außen beendet werden kann. So musst du es im Hauptprogramm vor der Verbindung setzen und das kann nur das Hauptprogramm, nicht aber ein anders Pythonscript, was ja auch "zu Ende" an den Socket schicken könnte...
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hi. Aber dann habe ich wieder das Problem, das bei einem gewaltsamen Abschuss des Programms, der Thread nicht beendet wird, oder?
Gruß, Harry
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Das ist kein Problem, das wird er. Ein Thread ist ja ein Leichtgewichtprozess, der als unterprozess läuft. Damit ist er zwar zeitlich unabhängig von der Ausführung des Hauptprogrammes, aber trotzdem abhängig. Wenn der Prozess beendet wird, sollte auch der Thread beendet werden. Bestimmt ist es auch möglich, ihn explizit vom Hauptprogramm aus zu beenden, aber ich hab mich da noch weiter mit beschäftigt. Vielleicht weiß jemand anderes, wie das geht...
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo,

Das Modul läuft nun schon ziemlich gut!
Hier der Code:

Code: Alles auswählen

# -*- coding: iso-8859-1 -*-
import socket 
import sys 
import threading

#Socketspeicher
_sock=[None] 

#Verbinden
def DoConnect(text="END", funktion=None, parameter=None):
    HOST = "localhost"
    PORT = 65012 
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
        s.bind((HOST, PORT)) 
        s.listen(1)
        _sock[0] = s 
        serv = Miniserver(s, funktion, parameter) 
        serv.start() 
        print "Server gestartet"
    except:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
        s.connect((HOST, PORT)) 
        s.send(text)
        s.close()
        return "CLOSE"
        
#Server
class Miniserver(threading.Thread): 
    def __init__(self, sock, funktion, parameter): 
        threading.Thread.__init__(self) 
        self.s = sock
        self.funktion = funktion
        self.parameter = parameter
    def run(self): 
        while 1:
            conn, addr = self.s.accept()
            data = conn.recv(1024)
            conn.close()
            if data == "END":
                print "Server beendet"
                break
            print "Server hat folgendes Signal erhalten: %s"%data
            if self.funktion:
                self.funktion(self.parameter)
                print "Übergebene Funktion wurde gestartet"

if __name__=="__main__": 
    DoConnect()
Durch die Parameter 'funktion' und 'parameter' in 'DoConnect' kann man eine Funktion übergeben, welche nach doppeltem Aufruf des Programms gestartet werden soll.
Bei Beendigung des Programms, genügt es 'DoConnect' ohne Paramter aufzurufen. Dadurch wird "END" an den Miniserver übertragen und der Thread beendet.
Wer hat noch eine Idee zur Optimierung?
Was ist wenn die Portnummer schon von einem anderen unabhängigen Programm belegt ist? Dann startet mein Programm nicht mehr, oder? Wie kann ich das verhindern?
Gruß, Harry
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo,

Habe nun auch den Rest hinbekommen. Dieses Modul kann nun auch für mehrere Programme verwendet werden. Jedes dieser Programme erhält dann eine eigene noch nicht existierende Hostnummer.

Code: Alles auswählen

# -*- coding: iso-8859-1 -*-
import socket 
import sys 
import threading

#Socket- und Portspeicher
_sock=[None]
_port=[None]

#Verbinden
def DoConnect(progName, funktion=None, parameter=None):
    if not progName or type(progName) != str:        
        raise ValueError, 'Value must be a string and may be not empty'
    HOST = socket.getfqdn()
    for newport in xrange(65000, 65536):
        if not _port[0]:
            port = newport
        else:
            port = _port[0]
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind((HOST, port))
            s.listen(1)
            _sock[0] = s
            _port[0] = port
            serv = Miniserver(s, progName, port, funktion, parameter)
            serv.start() 
            print "Port %s gestartet"% port
            break
        except:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect((HOST, port))
            s.send(progName)
            data=s.recv(1024)
            s.close()
            if data != progName and progName != "!%END%!":
                continue
            else:
                return "LOCK"

#Verbindung trennen
def DoDisconnect():
    DoConnect("!%END%!")

#Server
class Miniserver(threading.Thread): 
    def __init__(self, sock, progName, port, funktion, parameter): 
        threading.Thread.__init__(self)
        self.s = sock
        self.progName= progName
        self.port= port
        self.funktion = funktion
        self.parameter = parameter        
        
    def run(self): 
        while 1:            
            client, addr = self.s.accept()
            port = client.getsockname()[1]
            client.send(self.progName)
            progName = client.recv(1024)
            client.close()
            if self.port == port and self.progName != progName and progName != "!%END%!":
                continue
            if self.port == port and progName == "!%END%!":
                print "Port %s beendet"% port
                break
            if self.port == port and self.funktion:
                self.funktion(self.parameter)
                print "Übergebene Funktion des Ports %s wurde gestartet"% port

if __name__=="__main__": 
    pass
Mit der an die Funtion 'DoConnect' zu übergebenden Variable 'progname' wird der Name des Programms übergeben. Für jeden neuen Namen wird dann eine neue Host-Nummer vergeben.
Mit der Funktion 'DoDisconnect' wird dann das Socket für das jeweilige Programm entfernt.
Der Bereich der zu vergebenden Hostnummern kann nartürlich vergrößert oder verkleinert werden.
Gruß, Harry
pSy
User
Beiträge: 44
Registriert: Montag 4. Oktober 2004, 17:58
Kontaktdaten:

ist ja wirklich n tolles modul... aber wie benutze ich die funktion DoConnect, ohne das sich das Script ewig daran aufhält?

Wenn ich

Code: Alles auswählen

servermodul.DoConnect('myCoolScript')
benutze, hängt das Programm an dieser Stelle fest.

Gruß
pSy
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hi pSy,

wie ich das sehe, musst du der Funktion DoConnect noch deine Main-Funktion als Parameter mitgeben, Diese wird dann ausgeführt.


Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo zusammen

Ich habe das Modul nocht etwas ausdokumentiert und eine Kleinigkeit verbessert.

Code: Alles auswählen

# -*- coding: iso-8859-1 -*-

import socket
import sys
import threading

#Socket- und Portspeicher
_sock=[None]
_port=[None]

#Verbinden
def DoConnect(progName, funktion=None, parameter=None):
    """
    progname =      The identified name for the programm which connect you. Must be a name,
                    that will really differ beetwen programs.
    funktion =      The funktion which will be started, when the programm will double opened
    parameter =     The parameter for the funktion
    """
    if not progName or type(progName) != str:
        raise ValueError, 'Value must be a string and may be not empty'
    HOST = "localhost"
    for newport in xrange(65000, 65536):
        if not _port[0]:
            port = newport
        else:
            port = _port[0]
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind((HOST, port))
            s.listen(1)
            _sock[0] = s
            _port[0] = port
            #start serverthread
            serv = Miniserver(s, progName, port, funktion, parameter)
            serv.start()
            print "Port %s gestartet"% port
            break
        except:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.connect((HOST, port))
                s.send(progName)
                data=s.recv(1024)
                s.close()
                if data != progName and progName != "!%END%!":
                    continue
                else:
                    return "LOCK"
            except:
                continue


#Verbindung trennen
def DoDisconnect():
    """ Break up the connected socket"""
    DoConnect("!%END%!")

#Server
class Miniserver(threading.Thread):
    def __init__(self, sock, progName, port, funktion, parameter):
        """
        sock =          The new socket
        progname =      The identified name for the programm which connect you. Must be a name,
                        that will really differ beetwen programs.
        port =          The reserved port for the socket
        funktion =      The funktion which will be started, when the programm will double opened
        parameter =     The parameter for the funktion

        """
        threading.Thread.__init__(self)
        self.s = sock
        self.progName= progName
        self.port= port
        self.funktion = funktion
        self.parameter = parameter

    def run(self):
        """Let run the thread to observe the started programs"""
        while 1:
            client, addr = self.s.accept()
            port = client.getsockname()[1]
            client.send(self.progName)
            progName = client.recv(1024)
            client.close()
            if self.port == port and self.progName != progName and progName != "!%END%!":
                continue
            if self.port == port and progName == "!%END%!":
                print "Port %s beendet"% port
                break
            if self.port == port and self.funktion:
                self.funktion(self.parameter)
                print "Übergebene Funktion des Ports %s wurde gestartet"% port

if __name__ == "__main__":
    pass
Gestartet wird mit:

Code: Alles auswählen

server=modul.DoConnect("Programmname")
if server == "LOCK":
      sys.exit()
Beendet wir mit:

Code: Alles auswählen

modul.DoDisconnect()
Wird eine Funktion mit übergeben wird folgendermaßen gestartet:

Code: Alles auswählen

server=modul.DoConnect("Programmname", funktion, parameter)
if server == "LOCK":
      sys.exit()
Beendet wird genauso wie oben.

Der Sinn dieses Moduls ist, das doppelt öffnen von Programmen zu verhindern. Wird ein Programm doppelt geöffnet, gibt das Modul "LOCK" zurück. Das kann man dann auswerten.
Zusätzlich erhält man die Möglichkeit bei doppelten Aufruf eine Funktion ausführen zu lassen. Funktion und Parameter sind aber nicht unbedingt erforderlich.
Gruß, Harry
Gast

danke... du bist zu gut zu uns :lol:
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo zusammen

Bin heute auf diesen Thread gestoßen und habe den Code von HarryH, laut meinen Fehlermeldungen angepasst.
So läuft dieser bei mir bisher problemlos.

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x

import sys
import socket
import threading

# Socket- und Portspeicher
_sock=[None]
_port=[None]

# Verbinden
def DoConnect(progName, funktion=None, parameter=None):
    """
    progname =      The identified name for the programm which connect you. Must be a name,
                    that will really differ beetwen programs.
    funktion =      The funktion which will be started, when the programm will double opened
    parameter =     The parameter for the funktion
    """
    if not progName or type(progName) != str:
        raise ValueError (e, 'Value must be a string and may be not empty')
    HOST = "localhost"
    for newport in range(65000, 65536):
        if not _port[0]:
            port = newport
        else:
            port = _port[0]
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind((HOST, port))
            s.listen(1)
            _sock[0] = s
            _port[0] = port
            # start serverthread
            serv = Miniserver(s, progName, port, funktion, parameter)
            serv.start()
            print('Port %s gestartet' % port)
            break
        except:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    s.connect((HOST, port))
                except socket.error:
                    return
                s.send(progName)
                data=s.recv(1024)
                s.close()
                print(progName, data)
                if data != progName and progName != "!%END%!":
                    continue
                else:
                    return "LOCK"
            except TypeError:
                return "LOCK"


# Verbindung trennen
def DoDisconnect():
    """ Break up the connected socket"""
    DoConnect("!%END%!")

# Server
class Miniserver(threading.Thread):
    def __init__(self, sock, progName, port, funktion, parameter):
        """
        sock =          The new socket
        progname =      The identified name for the programm which connect you. Must be a name,
                        that will really differ beetwen programs.
        port =          The reserved port for the socket
        funktion =      The funktion which will be started, when the programm will double opened
        parameter =     The parameter for the funktion

        """
        threading.Thread.__init__(self)
        self.s = sock
        self.progName = progName
        self.port = port
        self.funktion = funktion
        self.parameter = parameter

    def run(self):
        """Let run the thread to observe the started programs"""
        while 1:
            client, addr = self.s.accept()
            port = client.getsockname()[1]
            try:
                client.send(self.progName)
            except TypeError:
                break
            progName = client.recv(1024)
            client.close()
            if self.port == port and self.progName != progName and progName != "!%END%!":
                continue
            if self.port == port and progName == "!%END%!":
                print('Port %s beendet' % port)
                break
            if self.port == port and self.funktion:
                self.funktion(self.parameter)
                print('Übergebene Funktion des Ports %s wurde gestartet' % port)

if __name__ == "__main__":
    pass
In dem zu startenden Programm, habe das am Anfang des Programmes so eingebunden:

Code: Alles auswählen

# Kontrollfunktion gegen doppeltes Starten des Programmes
import gui_sock_control as modul

gui_work = os.path.join(os.path.dirname(__file__), 'gui_work.py')

server = modul.DoConnect(gui_work)
if server == "LOCK":
      sys.exit()
Was mich interessiert:
Wie kann ich sicherstellen , daß wenn mein Programm mit dem X-Button am Fenster beendet wird, daß

Code: Alles auswählen

modul.DoConnect()
ausgeführt wird?

Grüße Nobuddy
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

So z.B.:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# For Python3.x

import sys
import socket
import threading
import atexit

# Socket- und Portspeicher
_sock=[None]
_port=[None]

# Exit abfangen
def OnClose(port):
    if port == _port[0]:
        DoDisconnect()

# Verbinden
def DoConnect(progName, funktion=None, parameter=None):
    """
    progname =      The identified name for the programm which connect you. Must be a name,
                    that will really differ beetwen programs.
    funktion =      The funktion which will be started, when the programm will double opened
    parameter =     The parameter for the funktion
    """
    if not progName or type(progName) != str:
        raise ValueError (e, 'Value must be a string and may be not empty')
    HOST = "localhost"
    for newport in range(65000, 65536):
        if not _port[0]:
            port = newport
        else:
            port = _port[0]
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.bind((HOST, port))
            s.listen(1)
            _sock[0] = s
            _port[0] = port
            # start serverthread
            serv = Miniserver(s, progName, port, funktion, parameter)
            serv.start()            
            atexit.register(OnClose, port)
            print('Port %s gestartet' % port)
            break
        except:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                try:
                    s.connect((HOST, port))
                except socket.error:
                    return
                s.send(progName)
                data=s.recv(1024)
                s.close()
                print(progName, data)
                if data != progName and progName != "!%END%!":
                    continue
                else:
                    return "LOCK"
            except TypeError:
                return "LOCK"


# Verbindung trennen
def DoDisconnect():
    """ Break up the connected socket"""
    DoConnect("!%END%!")

# Server
class Miniserver(threading.Thread):
    def __init__(self, sock, progName, port, funktion, parameter):
        """
        sock =          The new socket
        progname =      The identified name for the programm which connect you. Must be a name,
                        that will really differ beetwen programs.
        port =          The reserved port for the socket
        funktion =      The funktion which will be started, when the programm will double opened
        parameter =     The parameter for the funktion

        """
        threading.Thread.__init__(self)
        self.s = sock
        self.progName = progName
        self.port = port
        self.funktion = funktion
        self.parameter = parameter

    def run(self):
        """Let run the thread to observe the started programs"""
        while 1:
            client, addr = self.s.accept()
            port = client.getsockname()[1]
            try:
                client.send(self.progName)
            except TypeError:
                break
            progName = client.recv(1024)
            client.close()
            if self.port == port and self.progName != progName and progName != "!%END%!":
                continue
            if self.port == port and progName == "!%END%!":
                print('Port %s beendet' % port)
                break
            if self.port == port and self.funktion:
                self.funktion(self.parameter)
                print('Übergebene Funktion des Ports %s wurde gestartet' % port)

if __name__ == "__main__":
    pass
Gruß, Harry
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo HarryH

Danke für Deine schnelle Antwort! :wink:

Der neu hinzugefügte Code, funktioniert bei mir aber nicht.
Nach einigem Herumtesten, ist ein Doppelstart so trotzdem möglich.

Ich dachte eigentlich das direkte Abfangen des Programmendes in dem gestarteten Programm selbst.
Mein Programm beinhaltet eine grafische Oberfläche (Tkinter).
Dort besteht die Möglichkeit, mit einem Beenden-Button sowie auch dem X-Button das Programm zu beenden.

Sinn Deines Programmes, ist ja für das Starten mehrere verschiedener Python-Programme gedacht, wenn ich das richtig verstanden habe.
Ich habe mir gedacht, den beim Programmstart erhaltenen Port, beim Programmende wieder an dein Programm zurück zu übergeben, damit dann 'progName' und der dazugehörige Port wieder freigegeben werden.

Wünsche schöne Weihnachten
Nobuddy

Nachtrag:
Sorry, funktioniert alles! :wink:
Hatte zuvor 'OnClose(port)' in meinem Programm nicht integriert gehabt.
Eine Frage bleibt noch.
Wie kann ich das abfangen, wenn man mit dem X-Button das Programm zu beendet?
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hi,

In den python manuals wird schön beschrieben unter welchen Umständen das modul "atexit" funktioniert und unter welchen nicht.
Note: the functions registered via this module are not called when the program is killed by a signal not handled by Python, when a Python fatal internal error is detected, or when os._exit() is called.
Habe mit Tkinter kaum Erfahrungen; arbeite mit wxPython. Dort ist es wichtig das Close-Event anzufangen und das Programm dann mit sys.exit() zu beenden. Dann werden auch die in atexit.register(...) registrierten Funktionen gestartet.
Gruß, Harry
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo HarryH

Danke für Deine Info, werde mal im Tkinter-Forum nachfragen.
Dein Programm funktioniert prima! :wink:

Grüße Nobuddy
Antworten