Skip to content

socket.shutdown() does not release a blocked recv() call - it's required to call socket.close() #91913

Closed
@bjoham

Description

@bjoham

Bug report

Updated: In Linux (Python 3.8.10, Ubuntu 20.04.4 LTS), it works as expected.
Maybe the difference is that in Windows recv() can return with an error (WSAESHUTDOWN) for shutdown sockets whereas in Linux recv() returns with a byte count of zero.

Original post:
In my experience, a blocked call to a connected socket's recv() should be released by issuing a socket.shutdown(socket.SHUT_RDWR) - or socket.shutdown(socket.SHUT_RD)
That's according to function in Linux as well as TCP/IP standard (not sure here).

In Python, the shutdown() call seems to be ignored all together. See example, below.

Your environment

  • CPython versions tested on: 3.9.12
  • Operating system and architecture: Windows 10 Pro

Example
import threading
import socket
import time

HOST = "127.0.0.1"
PORT = 1234

def serverThread():
print(f"Server starting for {HOST}:{PORT}")

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    conn, addr = s.accept()
    with conn:
        print(f"Connected by {addr}")
        print("Server won't send anything")
        #conn.sendall(b"Hello")
        time.sleep(10)
        print("Server terminating...")
        conn.shutdown(socket.SHUT_RDWR) # Tell the client we're shutting down the connection - not absolutely necessary
        conn.close()
        
    s.close()

server = threading.Thread(target=serverThread, name="SERVER")

clientSocket = None
def clientThread():
print(f"Connecting to server {HOST}:{PORT}")

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    global clientSocket
    clientSocket = s
    s.connect((HOST, PORT))
    print("Client is receiving...")
    try:
        data = s.recv(64)
    except OSError as e:
        print(f"Exception in client: {e}")
    else:
        print(f"Client has received: {data!r}")
        
    print("Client terminating...")
    
    if s.fileno() < 0:
        print("Someone has already closed the socket. Who is messing with my socket?!?!?")
    else:
        s.shutdown(socket.SHUT_RDWR) # Tell the server we're shutting down the connection - not absolutely necessary
        s.close()
    print("Client is done")

client = threading.Thread(target=clientThread, name="CLIENT")

print("Server and Client is set up")
server.start()
client.start()
time.sleep(2)

if clientSocket.fileno() < 0:
print("Client socket is already closed, we're done")
exit()

print("\nShutting down socket")
clientSocket.shutdown(socket.SHUT_RDWR)
print("\n *** Nothing changed, right? ***")
time.sleep(2)

print("\n\nShutting down socket using close() coming up...")
time.sleep(2)
print("Now!")
clientSocket.close()
print("\n >>> Now, we succeeded with releasing the blocked recv() call in client\n\n")

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions