[Zope] Re: Running more than one instance on windows often block each other

Sune B. Woeller sune at syntetisk.dk
Tue Jul 26 07:23:04 EDT 2005


Tim Peters wrote:
> [Sune B. Woeller]
> 
>>>...
>>>I can see (with the excellent (and free) 'Process
>>>Explorer' from sysinternals) that the python
>>>processes always opens port 19999, and connects by
>>>that port to themselves on another port (for
>>>instance 2550).
> 
>  
> [Dieter Maurer]
> 
>>You can find the relevant code in
>>"ZServer.medusa.thread.select_trigger.trigger.__init__"
>>
>>In principle, the code should try all sockets between
>>"19999" down to "19950" and fail only when none of them
>>could be bound to...
> 
Thanks for the pointer. I have been debugging
select_trigger.py, and has some more info:

The problem is that the call a.accept() sometimes hangs.
Apparently a.bind(self.address) allows us to bind to
a port that another zope instance already is bound to.

The code creates the server socket a, and the client socket w,
and gets the client socket r by connecting w to a. Then it closes a.
a goes out of scope when __init__ terminates, and is probably garbage collected 
at some point.

I tried moving the code to the following standalone script, and I can reproduce 
the error with that. In the original code w is kept as an instance variable, and 
r is passed to asyncore.dispatcher.__init__  and probably kept there. I simulate 
that by returning them, then the caller of socktest can keep them around.

I try to call socktest from different processes A and B (two pythons):
(w,r = socktest())
The call in A gets port 19999. The second call, in B, either blocks, or takes 
over port 19999 (I see the second process taking over the port in a port scanner.)

a.bind in B does not raise socket.error: (10048, 'Address already in use') as 
expected, when the server socket in A is closed, even though the port is used by 
the client socket r in A.

If I remove a.close(), and keep a around (by passing it to the caller), a.bind 
works as expected - it raises socket.error: (10048, 'Address already in use').

But in the litterature on sockets, I read it should be okay to close the server 
socket and keep using the client sockets.

So, is this a possible bug in bind() ?


I have tested the new code from Tim Peters, it apparently works, ports are given 
out by windows.
But could the same problem with bind occur here, since a is closed (and garbage 
collected) ? (far less chance for that since we do not specify port numbers, I 
know).

I tried getting a pair of sockets with Tim's code, and then trying to bind a 
third socket to the same port as a/r. And I got the same problem as above.


Sune

+++++++++++++++++++++
import socket, errno

class BindError(Exception):
     pass


def socktest():
     """blabla
     """

     address = ('127.9.9.9', 19999)

     a = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
     w = socket.socket (socket.AF_INET, socket.SOCK_STREAM)

     # set TCP_NODELAY to true to avoid buffering
     w.setsockopt(socket.IPPROTO_TCP, 1, 1)

     # tricky: get a pair of connected sockets
     host='127.0.0.1'
     port=19999

     while 1:
         print port
         try:
             a.bind((host, port))
             break
         except:
             if port <= 19950:
                 raise BindError, 'Cannot bind trigger!'
             port=port - 1

     a.listen (1)
     w.setblocking (0)
     try:
         w.connect ((host, port))
     except:
         pass
     r, addr = a.accept()
     a.close()
     w.setblocking (1)

     #return (a, w, r)
     return (w, r)
     #return w

+++++++++++++++++++++



> 
> Yup.  ZODB has what looks like a copy/paste of this code, in
> ZEO/zrpc/trigger.py.  I didn't realize where it came from originally
> until you pointed out the Medusa code here.
> 
> Anyway, it so happens I rewrote ZEO's copy a few weeks ago, in ZODB
> 3.4.  The Windows part is much simpler there now.  I don't know why
> the original might fail in the way Sune reported, but perhaps the
> rewritten version would not.
> 
> Before:
> 
>             # tricky: get a pair of connected sockets
>             host='127.0.0.1'
>             port=19999
>             while 1:
>                 try:
>                     self.address=(host, port)
>                     a.bind(self.address)
>                     break
>                 except:
>                     if port <= 19950:
>                         raise BindError, 'Cannot bind trigger!'
>                     port=port - 1
>                     
>             a.listen (1)
>             w.setblocking (0)
>             try:
>                 w.connect (self.address)
>             except:
>                 pass
>             r, addr = a.accept()
>             a.close()
>             w.setblocking (1)
>             self.trigger = w
> 
> After:
> 
>             # Specifying port 0 tells Windows to pick a port for us.
>             a.bind(("127.0.0.1", 0))
>             connect_address = a.getsockname()  # assigned (host, port) pair
>             a.listen(1)
>             w.connect(connect_address)
>             r, addr = a.accept()  # r becomes asyncore's (self.)socket
>             a.close()
>             self.trigger = w
> _______________________________________________
> Zope maillist  -  Zope at zope.org
> http://mail.zope.org/mailman/listinfo/zope
> **   No cross posts or HTML encoding!  **
> (Related lists - 
>  http://mail.zope.org/mailman/listinfo/zope-announce
>  http://mail.zope.org/mailman/listinfo/zope-dev )
> 



More information about the Zope mailing list