Error: child process close a socket inherited from parent

Chris Torek nospam at torek.net
Sun May 29 19:37:17 EDT 2011


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.
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: gmail (figure it out)      http://web.torek.net/torek/index.html



More information about the Python-list mailing list