Error: child process close a socket inherited from parent

narke narkewoody at gmail.com
Sun May 29 23:52:43 EDT 2011


On 2011-05-29, Chris Torek <nospam at torek.net> wrote:
> In article <slrniu42cm.2s8.narkewoody at CNZUHNB904.ap.bm.net>
> narke  <narkewoody at gmail.com> wrote:
>>As illustrated in the following simple sample:
>>
>>import sys
>>import os
>>import socket
>>
>>class Server:
>>    def __init__(self):
>>        self._listen_sock = None
>>
>>    def _talk_to_client(self, conn, addr):
>>        text = 'The brown fox jumps over the lazy dog.\n'
>>        while True:
>>            conn.send(text)
>>            data = conn.recv(1024)
>>            if not data:
>>                break
>>        conn.close()
>>
>>    def listen(self, port):
>>        self._listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>        self._listen_sock.bind(('', port))
>>        self._listen_sock.listen(128)
>>        self._wait_conn()
>>
>>    def _wait_conn(self):
>>        while True:
>>            conn, addr = self._listen_sock.accept()
>>            if os.fork() == 0:
>>                self._listen_sock.close()           # line x
>>                self._talk_to_client(conn, addr)
>>            else:
>>                conn.close()
>>
>>if __name__ == '__main__':
>>    Server().listen(int(sys.argv[1]))
>>
>>Unless I comment out the line x, I will get a 'Bad file descriptor'
>>error when my tcp client program (e.g, telnet) closes the connection to
>>the server.  But as I understood, a child process can close a unused
>>socket (file descriptor).
>
> It can.
>
>>Do you know what's wrong here?
>
> The problem turns out to be fairly simple.
>
> The routine listen() forks, and the parent process (with nonzero pid)
> goes into the "else" branch of _wait_conn(), hence closes the newly
> accepted socket and goes back to waiting on the accept() call, which
> is all just fine.
>
> Meanwhile, the child (with pid == 0) calls close() on the listening
> socket and then calls self._talk_to_client().
>
> What happens when the client is done and closes his end?  Well,
> take a look at the code in _talk_to_client(): it reaches the
> "if not data" clause and breaks out of its loop, and calls close()
> on the accepted socket ... and then returns to its caller, which
> is _wait_conn().
>
> What does _wait_conn() do next?  It has finished "if" branch in
> the "while True:" loops, so it must skip the "else" branch and go
> around the loop again.  Which means its very next operation is
> to call accept() on the listening socket it closed just before
> it called self._talk_to_client().
>
> If that socket is closed, you get an EBADF error raised.  If not,
> the child and parent compete for the next incoming connection.


Chris,

 Thanks, you helped to find out a bug in my code. 



More information about the Python-list mailing list