On 2011-05-29, Chris Torek <nos...@torek.net> wrote: > In article <slrniu42cm.2s8.narkewo...@cnzuhnb904.ap.bm.net> > narke <narkewo...@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. -- http://mail.python.org/mailman/listinfo/python-list