Progress on preventing multiple instances of a program from running in windows

Alex Tweedly alex at tweedly.net
Fri Dec 3 09:27:32 EST 2004


At 11:52 03/12/2004 +0100, thierry wrote:

>Hi,
>
>LJ> Hello... the following script seems to work.
>
>LJ> Factors to consider:
>LJ> 1)The stack should be set to purge itself from memory upon closing
>LJ> 2)Some firewalls might prevent the program from accepting
>LJ> connections on a port
>LJ> 3)This prevents multiple instances, but still does not quite allow
>LJ> the two instances to communicate. Writing to a socket from one instance
>LJ> seems to steal the ability of the other instance to listen to that
>LJ> socket - I am still trying to work out how to do that.
>
>humm, with my little experience, you probably need to differenciate the 1st
>instance from the second. if the first do a accept connection ( Server mode )
>the second instance should do a connect as a client. So, if you see yourself
>in the netstat result then run a open socket on the port.

That's right. Since we're using sockets anyway, there's no need to use 
netstat at all.

Executive Summary :
the first instance goes into server mode, and accepts connections from 
subsequent instances

Technical Summary:
when an instance starts up, it attempts to open a connection to the chosen 
port.
If it succeeds, then there is already an instance running - and we have a 
connection to it.
If it fails, then there is no instance running - become the "server" and 
accept connections from others.

Exercise (left to the reader):
if the initial instance (server) is closed down, the client(s) should react 
to the "socket closed" event, such that one of them becomes a server, and 
the other clients re-connect to it.

Here's some code to do the initial problem - it has a bunch of error 
checking you could take it if you really wanted to, so it doesn't need to 
be quite this long. Note - I did this as a handler called "startUp" rather 
than in OpenStack or preOpenStack because I pulled most of this code out of 
an existing app and this was easier.

>local lConns
>
>on startUp
>   if the openSockets <> empty then
>     resetAll
>     put "reset" into field "My Status"
>     wait for 100 millisecs
>   end if
>   -- See if there is already an instance running
>   open socket to "127.0.0.1:7654" with message "setMode"
>end startUp
>
>on setMode pMode
>   if pMode = "Server" then
>     -- we should be in server mode
>     put pMode into field "My Status"
>     accept connections on port "7654" with message "connectionMade"
>   else
>     -- another instance is running as server - need to be a client
>     put "Client" into field "My Status"
>     -- send a message to confirm
>     write "this is my input line" & cr to socket "127.0.0.1:7654"
>     read from socket "127.0.0.1:7654" until CR with message 
> "receivedResponse"
>   end if
>end setMode
>
>-- Because this object does the Accepts and Opens, it gets close and error 
>notifications
>on socketClosed s
>   put lineOffset(s, lConns) into NConn
>   if NConn = 0 then
>     logWrite "Attempt to close a socket that is not open."
>   else
>     logWrite "Socket" && s && "closed."
>     delete line NConn of lConns
>     put lConns into field "Connections"
>   end if
>end socketClosed
>
>on socketError pSocket, pError
>   if offset("10061", pError) > 0 then
>     -- Connection refused - no-one is listening on this port already
>     setMode "Server"
>   else
>     logWrite "Error :" && param(1) && param(2)
>   end if
>end socketError
>
>-- client functions
>on receivedResponse
>   logWrite "Received response : " & param(2)
>end receivedResponse
>
>-- Server mode handlers
>on connectionMade pOtherOne
>   if char 1 to 9 of pOtherOne <> "127.0.0.1" then
>     logWrite "Connection attempt from another machine - reject"
>     close socket theOtherOne
>     exit connectionMade
>   end if
>   if the number of lines in lConns >= 4 then
>     logWrite "Too many connections - rejected."
>     close socket theOtherOne
>     exit connectionMade
>   end if
>   put pOtherOne & cr after lConns
>   read from socket pOtherOne until CR with message "readsome"
>end connectionMade
>
>
>on readsome fromSocket, theInput
>   put lineOffset(fromSocket, lConns) into NConn
>   if NConn = 0 then
>     logWrite "Input rejected - unknown Connection" & fromSocket
>     read from socket fromSocket until CR with message "readsome"
>     exit readsome
>   end if
>   -- echo it
>   write "echo " & theInput & cr to socket fromSocket
>   logWrite fromSocket & theInput
>   read from socket fromSocket until CR with message "readsome"
>end readsome
>
>on logWrite m
>     put m & cr after field "Connections"
>end logWrite

This works fine for me (Win2000), running one instance in the Player and 
the other within Rev itself. (And also running an echo server or clients 
written in other languages).

Note that it *should* be even easier than this - it makes no sense to have 
two applications listening on the same port; in Python or C you get an 
error (EADDRINUSE) return from the bind call (approx equivalent of Rev's 
"Accept") when another instance has already done an Accept on the port. 
Unfortunately, Rev doesn't seem to send an error in this case.  I will play 
with this a bit more to make sure that I haven't done anything stupid, and 
then Bugzilla it.

-- Alex.


More information about the use-livecode mailing list