FAQ
Hi,

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).

Do you know what's wrong here?


--
Life is the only flaw in an otherwise perfect nonexistence
-- Schopenhauer

narke

Search Discussions

  • Narke at May 29, 2011 at 1:52 pm

    On 2011-05-29, narke wrote:
    Hi,

    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).

    Do you know what's wrong here?
    I forgot to say, it's Python 2.6.4 running on linux 2.6.33


    --
    Life is the only flaw in an otherwise perfect nonexistence
    -- Schopenhauer

    narke
  • Chris Torek at May 29, 2011 at 11:37 pm
    In article <slrniu42cm.2s8.narkewoody at CNZUHNB904.ap.bm.net>
    narke 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
  • Narke at May 30, 2011 at 3:52 am

    On 2011-05-29, Chris Torek wrote:
    In article <slrniu42cm.2s8.narkewoody at CNZUHNB904.ap.bm.net>
    narke 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.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedMay 29, '11 at 8:53a
activeMay 30, '11 at 3:52a
posts4
users2
websitepython.org

2 users in discussion

Narke: 3 posts Chris Torek: 1 post

People

Translate

site design / logo © 2022 Grokbase