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