closing a server socket

simon place simon_place at whsmithnet.co.uk
Sat Oct 18 12:08:26 EDT 2003


Alex Martelli wrote:

 > Peter Hansen wrote:
 >    ...
 >
 >>I guess you are basically saying that you want to use a blocking
 >>socket (because you can't get non-blocking ones to work, though they
 >>do work in general?) but you still want to be able to terminate
 >>your call from another thread by forcibly closing the socket...
 >>
 >>If that's so, give it up.  You can't do that because the
 >
 >
 > VERY good point -- and since by a weird coincidence I've had to deal
 > with EXACTLY this problem TWICE over the last few days for two
 > different clients (!), both times in the context of xml-rpc serving,
 > let me emphasize this once again: that's not the way to do it.
 >
 >
 >
 >>Use non-blocking sockets.  They do work.
 >
 >
 > Or architect a solution where the blocked thread will get out
 > from its stuck state in the blocking socket "naturally".  That's
 > quite easy in XML-RPC, e.g. as follows...:
 >
 >
 > _baseclass = SimpleXMLRPCServer.SimpleXMLRPCServer
 > class TerminatableServer(_baseclass):
 >     allow_reuse_address = True
 >
 >     def __init__(self, addr, *args, **kwds):
 >         self.myhost, self.myport = addr
 >         _baseclass.__init__(self, addr, *args, **kwds)
 >         self.register_function(self.terminate_server)
 >
 >     quit = False
 >     def serve_forever(self):
 >         while not self.quit:
 >             self.handle_request()
 >         self.server_close()
 >
 >     def terminate_server(self, authentication_token):
 >         if not check_as_valid(authentication_token):
 >             return 1, "Invalid or expired authentication token"
 >         self.quit = True
 >         return 0, "Server terminated on host %r, port %r" % (
 >             self.myhost, self.myport)
 >
 > (you'll want some way to verify authentication, of course, but
 > that's probably something you already have somewhere in such an
 > architecture, so I'm simplistically representing it by the
 > authentication_token parameter and a hypothetical check_as_valid
 > function that's able to check it).
 >
 > The point is that in this way server termination is architected
 > as just one transaction -- the self.handle_request() in the body
 > of the while will eventually hand off control to terminate_server,
 > which may set self.quit, and that in turn will terminate the
 > while loop, since as soon as handle_request is done the loop's
 > condition is checked again.  There are other ways, of course, but
 > I've found this one clean and reliable for these specific needs.
 >
 >
 > Alex
 >

Yes, no problem closing a server before it returns to accept another 
connection, but when the handler is threaded,

see below, current situation

import socket
 >>> s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 >>> s.bind(('127.0.0.1',80))
 >>> s.listen(1)
 >>> import threading
 >>> t=threading.Thread(target=s.accept)
 >>> t.start()
 >>> s.close()
 >>> import inspect
 >>> inspect.getsource(socket.socket.close)
'    def close(self):\n        self._sock = _closedsocket()\n        self.send 
= self.recv = self.sendto = self.recvfrom = self._sock._dummy\n'
 >>> t.isAlive()
True
 >>> s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 >>> s.connect(('127.0.0.1',80))
 >>> t.isAlive()
False
 >>>

i.e.  closing server socket does not unblock accept and you can still get 
connections on it!
( because it has not actually been closed!)
how is this acceptable?



with socket modified to actually close the socket,

 >>> import socket
 >>> import inspect
 >>> inspect.getsource(socket.socket.close)
'    def close(self):\n        self._sock.close()\n        self._sock = _closeds
ocket()\n        self.send = self.recv = self.sendto = self.recvfrom = self._soc
k._dummy\n'
 >>> import threading
 >>> s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 >>> s.bind(('127.0.0.1',80))
 >>> s.listen(1)
 >>> t=threading.Thread(target=s.accept)
 >>> t.start()
 >>> t.isAlive()
True
 >>> s.close()
 >>> Exception in thread Thread-1:
Traceback (most recent call last):
   File "C:\PYTHON23\lib\threading.py", line 436, in __bootstrap
     self.run()
   File "C:\PYTHON23\lib\threading.py", line 416, in run
     self.__target(*self.__args, **self.__kwargs)
   File "socket.py", line 168, in accept
     sock, addr = self._sock.accept()
error: (10038, 'Socket operation on non-socket')


 >>> t.isAlive()
False
 >>>

i.e. close socket generates an exception on accept and no more connections.

If there's some underlying difficulty that causes this to come back and bite 
the testing i've done shows this to take a very long time.

simon.






More information about the Python-list mailing list