Sean
Sean

Reputation: 119

OSError: [Errno 9] Bad file descriptor with socket.shutdown()

I'm currently trying to get a hang of how sockets work.

I've followed an online tutorial & the python HOWTO

I have a server:

#!/usr/bin/env python3
    
import socket

HOST = '127.0.0.1' #standard loopback interface address (localhost)
PORT = 9999

serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind((HOST, PORT))
serversocket.listen()
(clientsocket, address) = serversocket.accept()
with clientsocket:
    print('Connected by', address)
    while True:
        data = clientsocket.recv(1024)
        if not data:
            break
        clientsocket.sendall(data)
clientsocket.shutdown(socket.SHUT_RDWR)
clientsocket.close()

and a client:

#!/usr/bin/env python3

import socket

HOST = '127.0.0.1' #server hostname or IP address
PORT = 9999

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello world!')
data = s.recv(1024)
s.shutdown(socket.SHUT_RDWR)
s.close

print('Received', repr(data))

The server listens for a connection, accepts it and echos whatever it receives.

In the python HOWTO it was emphasized to always close a socket.

Now when I run both scripts in seperate terminals the client does as expected:

Received b'Hello world!'

The terminal with the server gives me the following error:

Connected by ('127.0.0.1, 42780')
Traceback (most recent call last):
    File "echo-server.py", line 19, in <module>
        clientsocket.shutdown(socket.SHUT_RDWR)
OSError: [Errno 9] Bad file descriptor

From looking around stackeoverflow I learned that it was because of closing the Python file from "the outside", i.e. not from the file object's close() and I assume this means the socket I am trying to call socket.shutdown() on has already been shutdown.

Now when I comment line 19 out in the server script

#clientsocket.shutdown(socket.SHUT_RWDR)

everything works as expected without any errors.

The terminals show

Connected by ('127.0.0.1, 42780')

and

Received b'Hello world!'

From my understanding this behaviour is caused by the with statement.

Now to my actual questions:

  1. Does the with statement make calling socket.shutdown() unnecessary by completing that task itself?
  2. If so, is calling socket.close() still necessary or is that done by the with statement aswell? (Commenting that line out didn't give me any different results)

In the end I want to make sure how to close a socket properly from the start since the HOWTO put much emphasis on proper form.

Upvotes: 0

Views: 3892

Answers (2)

Mark Tolonen
Mark Tolonen

Reputation: 178125

close is unnecessary in a with since it calls close automatically when exiting the with block, but shutdown can still be called inside the with before it exits:

with clientsocket:
    print('Connected by', address)
    while True:
        data = clientsocket.recv(1024)
        if not data:
            clientsocket.shutdown(socket.SHUT_RDWR)
            break
        clientsocket.sendall(data)

Upvotes: 1

Sean
Sean

Reputation: 119

As @Peter Wood pointed out the documentation does contain the answer to my question:

Changed in version 3.2: Support for the context manager protocol was added. Exiting the context manager is equivalent to calling close().

Furthermore:

Sockets are automatically closed when they are garbage-collected, but it is recommended to close() them explicitly, or to use a with statement around them.

Since calling fileObject.close() on a fileObject that has already been closed does not produce an error commenting that specific line out did not lead to any different results.

The way I interpret that:

It is not intended to explicitly call socket.shutdown() on a socket within a with statement.

Calling socket.close() on a socket in a with statement is unnecessary.

Upvotes: 1

Related Questions