server bootstrapping upon connection (WARNING: LONG)
Fortepianissimo
fortepianissimo at yahoo.com.tw
Tue Feb 10 11:54:36 EST 2004
Here is the situation: I want my server started up upon connection.
When the first connection comes in, the server is not running. The
client realizes the fact, and then starts up the server and tries to
connect again. This of course all happens on the same machine (local
connection only).
The connections can come in as fast as 30+/sec, so the server is
threaded (using SocketServer.ThreadingTCPServer). Further, the server
initialization is threaded into two: one is to do the real, lengthy
setup, the other is start lisenting ASAP.
The problem: I need to prevent multiple copies of the server being
started. I did this by using file locking (fcntl.lockf()). However,
not every time the code successfully prevented the server being
started up more than one time. Here is the relevant code:
---- CLIENT CODE SNIPPET STARTS ----
from fooConsts import *
import socket,fcntl,os,sys,time,logging
FOO_CLIENT_DEBUG=1
POLL_SERVER_INTVL=0.5
if FOO_CLIENT_DEBUG:
myPID=os.getpid()
logger = logging.getLogger('FOO Client')
hdlr = logging.FileHandler(TMP_PATH+'fooClient.log')
formatter = logging.Formatter('%(asctime)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)
def log (info):
logger.info('%d: %s'%(myPID,info))
class FooClient:
def __init__ (self, startServer=True):
"""Connects to FooServer if it exists, otherwise starts it and
connects to it"""
self.connected=True
if self.connect(): return
elif not startServer:
if FOO_CLIENT_DEBUG: log('connection failed 1')
self.connected=False
return
# try to obtain the right to start a server; if we can't,
someone else
# must be doing it - try to reconnect
try:
if FOO_CLIENT_DEBUG: log('try to get right to start
server')
serverStartLock=open(TMP_PATH+'serverStart.lock','w')
fcntl.lockf(serverStartLock.fileno(),fcntl.LOCK_EX|fcntl.LOCK_NB)
except:
if FOO_CLIENT_DEBUG: log('someone else is doing it; wait
2')
while 1:
time.sleep(POLL_SERVER_INTVL)
if self.connect(): return
# safe to start a server and connect to it
if FOO_CLIENT_DEBUG: log('start server')
exitCode=os.system('python -OO "%sfooServer.py" &'%FOO_PATH)
if exitCode==0:
if FOO_CLIENT_DEBUG: log('server is being started; wait
3')
while 1:
time.sleep(POLL_SERVER_INTVL)
if self.connect(): return
else:
if FOO_CLIENT_DEBUG: log('sever bootstrapping failed')
self.connected=False
raise "Cannot start FOOServer"
def connect (self):
"""Attempts to connect to FOOServer from PORT_MIN to
PORT_MAX"""
if FOO_CLIENT_DEBUG: log('connection attempt')
port=PORT_MIN
while port<=PORT_MAX:
try:
self.socket=socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
self.socket.connect(('',port))
break
except:
self.socket.close()
port+=1
if port>PORT_MAX:
if FOO_CLIENT_DEBUG: log('connection failed 2')
return False
if FOO_CLIENT_DEBUG: log('connection succeeded at port
%d'%port)
return True
...
FooClient()
---- CLIENT CODE SNIPPET ENDS ----
>From the log (when problem occurred) I see even *AFTER* the server was
started and accepted connections (several connections came and went
happily), a connection would come in and hit the "connection failed 1"
log line. This shouldn't have happened as the default value of
startServer for FooClient.__init__() is True. In the very same
incident, FooServer was started twice, but in the log there's no trace
of two "start server" lines, but only the "connection failed 1" line
in the very end of the log.
I realize this is a rather complex question to ask for help, but I'm
really at wits end. Feel free to skip the detailed description, and
just throw me some general tips and hints. Thank you VERY MUCH!
More information about the Python-list
mailing list