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)
python run.py
Server listening for clients at ('127.0.0.1', 65432)
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?
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))