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.
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo,

Ich möchte verhindern das ein Programm dopppelt geöffnet werden kann, wenn es bereits läuft. Ich arbeite auf Windows. Hat jemand eine Idee :?:

Weiter möchte ich , falls das Programm bereits läuft, in dieses eingreifen können. Das heißt eine Funktion in diesem aufrufen.

Könnt ihr mir bitte weiterhelfen.
Gruß, Harry
fs111
User
Beiträge: 170
Registriert: Samstag 15. November 2003, 11:42
Kontaktdaten:

Ich denke in beiden Fällen sind Sockets die richtige Lösung. Binde einfach beim Programmstart Dein Programm an einen TCP-Port. Wenn dann die zweite Instanz startet, kannst Du das abfangen, und mit einer Fehlermeldung beenden. Für zweiteres kannst Du auch Sockets sehr gut benutzen, einfach Kommunikation via TCP.

HTH

fs111
Pydoc-Integration in vim - Feedback willkommen: http://www.vim.org/scripts/script.php?script_id=910
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo fs111,

Danke für deine sehr schnelle Antwort. Ich habe leider überhaupt keine Ahnung von Sockets. Ich weiss nicht mal was damit gemeint ist. Könntest du mir das bitte ein wenig erklären und mir vielleicht ein Beispiel für die Lösung meines Problems zeigen.
Das wäre super. Danke :D
Gruß, Harry
Sorgenkind
User
Beiträge: 34
Registriert: Samstag 24. Juli 2004, 19:25
Kontaktdaten:

unter windows macht man das eigentlich mit DDE ... geht auch recht einfach.
bei win32all ist ein beispiel für DDE server und client dabei....

sockets wären natürlich OS unabhängiger
fs111
User
Beiträge: 170
Registriert: Samstag 15. November 2003, 11:42
Kontaktdaten:

Sockets sind die Endpunkte der Datenübertragung bei Netzwerkkommunikation (Unix-Domain-Sockets lasse ich mal außen vor) via TCP und UDP, also die Technik, die es Dir ermöglichen über Netwerke hinweg zu kommunizieren. Genauso wie Dein Browser und der Server dieses Forums es tun. Die beiden Protokolle, die dafür in zuständig sind, sind TCP (Transmission Control Protocol) und UDP (User Datagram Protocol), welche beide über Sockets kommunizieren (dafür braucht man auch die Port-Nummern). Du solltest hierbei TCP verwenden, da es eine zuverlässige Datenübertragung bereit stellt (reliable). Du würdest für das Locking einfach einen Socket an einer bestimmten Portnummmer öffnen (am besten möglichst hoch), und damit wäre schon alles getan, da immer nur ein Dienst/Programm einen Portnummer zur gleichen Zeit belegen kann, würde die zweite Instanz den Socket nicht öffnen können, und genau dieses könntest Du abfangen. Da Du ja nun schon eine Applikation hast, die auf Anfrage wartet, kannst Du darum noch ein Commando-Interface basteln, welches Kommandos von einem Client, der sich zur Anwendung verbindet entgegen nimmt.

Da ich jetzt nicht weiß, wie bewandert Du mit Netwerken, TCP/IP etc bist, kann ich hier auch noch sehr viel mehr schreiben, oder Du sagst, wo Du dann Probleme hast.

Als Python-Einstieg: http://www.amk.ca/python/howto/sockets/

und falls Du noch Links zu TCP/IP-Themen brauchst, sag einfach Bescheid.

fs111
Pydoc-Integration in vim - Feedback willkommen: http://www.vim.org/scripts/script.php?script_id=910
Dookie
Python-Forum Veteran
Beiträge: 2010
Registriert: Freitag 11. Oktober 2002, 18:00
Wohnort: Salzburg
Kontaktdaten:

Hallo zusammen,

warum mit Kanonen auf Spatzen schießen?

Erstell doch beim Programmstart einfach eine Datei, die beim Programmende wieder gelöscht wird. Unter windows gibts ja sicher auch ein C:\\tmp Verzeichnis oder so wo das einfach möglich ist. Bevor die Datei beim Programmstart erstellt wird einfach testen ob sie schon existiert, dann läuft das Programm schon und es kann eine entsprechende Meldung ausgegeben werden.

Code: Alles auswählen

import os, sys

def main():
    pass # hier ist das Hauptprogramm

tmplockname = "C:\\tmp\testprog.lock"

if os.path.exists(tmplockname):
    print "Testprog läuft schon"
    sys.exit()

f = file(tmplockname,'w')
f.write(" ")
f.close()

main()

os.remove(tmplockname)

Gruß

Dookie
[code]#!/usr/bin/env python
import this[/code]
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. So kann aber keine Funktion im anderem Programm gerufen werden... Ich würde das mit den Sockets auch so handhaben, da dann beim gewaltsamen Abschuss des Programmes auch der Port wieder geschlossen wird. Die Datei würde weiter bestehen bleiben...
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo,

Schon mal vielen Dank für alle eure Gedanken.

Ich werde die von fs111 empfohlene Doku zur Socketprogrammierung mal unter die Lupe nehmen. Das ist genau das was ich brauche. Ich werde mich wieder rühren wenn ich Probleme damit habe. :lol:
Gruß, Harry
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo,

Ich habe folgendes ausprobiert:

Code: Alles auswählen

try:
    HOST = ''                 # Symbolic name meaning the local host
    PORT = 1234567890         # Arbitrary non-privileged port
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(1)    
except:
    sys.exit()
Diesen Code habe ich am Anfang meines Programms gehängt. Starte ich es doppelt, bricht er wie erwünscht ab. Gut so.

Nun habe ich gleichen Code in ein Modul geschrieben und dieses Modul dann am Anfang meines Programms gestartet. Dann bricht er nicht wie gewünscht ab. Warum?
Gruß, Harry
fs111
User
Beiträge: 170
Registriert: Samstag 15. November 2003, 11:42
Kontaktdaten:

Die Portnummer ist viel zu groß! TCP und UDP-Ports gehen von 1-65536, die von Dir angegebenen Zahl sprengt alle Grenzen, ich wundere mich, dass es überhaupt funktioniert hat...


Hier mal ein paar Hintergründe zu TCP/IP:

http://www.rvs.uni-bielefeld.de/~heiko/ ... nhalt.html
http://de.wikipedia.org/wiki/TCP/IP-Referenzmodell
http://de.wikipedia.org/wiki/TCP
http://www.redbooks.ibm.com/pubs/pdfs/r ... 243376.pdf


fs111
Pydoc-Integration in vim - Feedback willkommen: http://www.vim.org/scripts/script.php?script_id=910
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hallo,

Ich habe es auch mit einem innerhalb des Bereichs liegenden Wert versucht (65007 - so wie in der Python-Doku). Der gewünschte Erfolg bleibt jedoch aus, sobald ich über ein Modul arbeite.
Das Modul sieht so aus:

Code: Alles auswählen

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

def DoConnect():
    try:
        HOST = ''            # Symbolic name meaning the local host
        PORT = 65007         # Arbitrary non-privileged port
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind((HOST, PORT))
        s.listen(1)
    except:
        sys.exit()
Ich rufe das Modul aus meinem Hauptprogramm auf und starte die Funktion 'DoConnect'.
Gruß, Harry
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. Wahrscheinlich wird der Socket sofort wieder gelöscht, da er ja nur in einer Funktion ohne Rückgabe steht. Probier mal das hier:

Code: Alles auswählen

# -*- coding: iso-8859-1 -*- 
import socket 
import sys 
_sock=[None]

def DoConnect():
    try: 
        HOST = ''            # Symbolic name meaning the local host 
        PORT = 65007         # Arbitrary non-privileged port 
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
        s.bind((HOST, PORT)) 
        s.listen(1)
        _sock[0]=s
    except: 
        sys.exit()
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Hi,

Ja, super, es geht!
Aber ich verstehe noch nicht so genau was da passiert. könntest su mir das bitte erklären :oops:

Jetzt muss ich es nur noch schaffen in dem Hauptprogramm eine Funktion aufzurufen. Könntest du mir dabei bitte auch einen Lösungsansatz schenken?

Jetzt ist mir noch eine Frage eingefallen. Was ist wenn die Portnummer schon von einem anderem unabhängigen Programm belegt ist? Dann startet mein Programm nicht mehr, oder? Wie kann ich das verhindern?
Gruß, Harry
Milan
User
Beiträge: 1078
Registriert: Mittwoch 16. Oktober 2002, 20:52

Hi. In einer Funktion sind alle Variablen lokal, d.h. du kannst von außen nicht auf HOST oder PORT in DoConnect zugreifen. Sie existieren also nur während des Funktionsaufrufes und werden danach vom Python Garbage Collector wieder gelöscht (es existiert kein Verweis mehr auf sie, also werden sie nicht mehr gebraucht). Wenn nun aber s gelöscht wird, wird der Socket gleich wieder geschlossen. Das soll verhindert werden. Also hab ich eine Liste außerhalb der Funktion definiert, in die s dann eingetragen wird. So existiert der Socket zwar nicht mehr als s, aber als Teil der Liste _sock und wird nicht gelöscht. Es ist egal, ob du eine Liste oder etwas anderes nimmst, nur bei einer Liste brauch ich kein global einzusetzen, damit ich auch aus der Funktion heraus schreibend zugreifen kann.
HarryH hat geschrieben:Jetzt muss ich es nur noch schaffen in dem Hauptprogramm eine Funktion aufzurufen. Könntest du mir dabei bitte auch einen Lösungsansatz schenken?
Dazu ist es unter anderem ganz nützlich, dass du den Socket noch weiter speicherst. So kannst du Daten darüber empfangen. Hier mal ein längeres Codebeispiel, was dein Modul schon mal in die Richtung ausbaut:

Code: Alles auswählen

# -*- coding: UTF-8 -*-
import socket
import sys
import threading
_sock=[None]
HOST = 'localhost'
PORT = 65007


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()
			Funktion(data)

def Funktion(text):
	print "Habe Nachricht empfangen: %s"%text

def DoConnect():
	try:
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		s.bind((HOST, PORT))
		s.listen(1)
		_sock[0]=s
		serv=Miniserver(s)
		serv.start()
		print "Server gestartet"
	except: #Hauptprogramm existiert, verbinde mit Programm
		s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		s.connect((HOST, PORT))
		s.send('Hello, world')
		s.close()
		print "Funktion gestartet, beende mich..."

if __name__=="__main__":
	DoConnect()
HarryH
User
Beiträge: 266
Registriert: Freitag 23. Mai 2003, 09:08
Wohnort: Deutschland

Danke! Funktioniert schon recht gut.

Nur habe ich nun das Problem, das mein Programm nicht beendet werden kann, da er bei 'self.s.accept()' wartet. Also auch wenn ich in meinem Hauptprogramm sys.exit() aufrufe läuft es weiter.

Was hat das schon wieder zu bedeuten?
Gruß, Harry
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
Antworten