Search code examples
pythonsocketstcp

Sending to a disconnected socket succeeds the first time


In a TCP client-server communication, when the client attempts to send to the server after the latter has disconnected, the first attempt to send() (allegedly) succeeds and all subsequent attempts fail with [Errno 32] Broken pipe. Minimal example:

import socket
import sys
import time

SERVER_ADDR = ("127.0.0.1", 65432)


def server(s):
    s.bind(SERVER_ADDR)
    s.listen()
    print(f"Server listening for clients at {SERVER_ADDR}")
    conn, addr = s.accept()
    with conn:
        print("Connected by", addr)


def client(s, msg):
    s.connect(SERVER_ADDR)
    time.sleep(1)
    for i in range(1, 10):
        print(f"Sending {msg} - attempt {i}")
        try:
            nbytes = s.send(msg)
            print(f"{nbytes} bytes sent out of {len(msg)} in total")
        except OSError as ex:
            print(ex)


if __name__ == "__main__":
    msg = " ".join(sys.argv[1:]).encode("utf8")
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        if msg:
            client(s, msg)
        else:
            server(s)
  1. Start the server:
python run.py 
Server listening for clients at ('127.0.0.1', 65432)
  1. Start the client:
python run.py hello world
Sending b'hello world' - attempt 1
11 bytes sent out of 11 in total
Sending b'hello world' - attempt 2
[Errno 32] Broken pipe
Sending b'hello world' - attempt 3
[Errno 32] Broken pipe
Sending b'hello world' - attempt 4
[Errno 32] Broken pipe
Sending b'hello world' - attempt 5
[Errno 32] Broken pipe
Sending b'hello world' - attempt 6
[Errno 32] Broken pipe
Sending b'hello world' - attempt 7
[Errno 32] Broken pipe
Sending b'hello world' - attempt 8
[Errno 32] Broken pipe
Sending b'hello world' - attempt 9
[Errno 32] Broken pipe
  • Why does this happen?
  • How can I either (a) make the first attempt fail too, or (b) detect if the connection is alive before sending?

Solution

  • Why does this happen?

    There is a detailed explanation in this answer to Writing to a closed, local TCP socket not failing.

    How can I either (a) make the first attempt fail too, or (b) detect if the connection is alive before sending?

    You can make the first attempt fail by forcing the server to close the socket immediately, with the SO_LINGER socket option, after the s.accept line:

    conn.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))