Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python doesn't detect a closed socket until the second send

Tags:

python

sockets

When I close the socket on one end of a connection, the other end gets an error the second time it sends data, but not the first time:

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("localhost", 12345))
server.listen(1)

client = socket.create_connection(("localhost",12345))
sock, addr = server.accept()
sock.close()

client.sendall("Hello World!")    # no error
client.sendall("Goodbye World!")  # error happens here

I've tried setting TCP_NODELAY, using send instead of sendall, checking the fileno(), I can't find any way to get the first send to throw an error or even to detect afterwards that it failed. EDIT: calling sock.shutdown before sock.close doesn't help. EDIT #2: even adding a time.sleep after closing and before writing doesn't matter. EDIT #3: checking the byte count returned by send doesn't help, since it always returns the number of bytes in the message.

So the only solution I can come up with if I want to detect errors is to follow each sendall with a client.sendall("") which will raise an error. But this seems hackish. I'm on a Linux 2.6.x so even if a solution only worked for that OS I'd be happy.

like image 746
Eli Courtwright Avatar asked Feb 04 '11 15:02

Eli Courtwright


People also ask

How do you check if a socket is connected disconnected in Python?

Short answer: use a non-blocking recv(), or a blocking recv() / select() with a very short timeout. Long answer: The way to handle socket connections is to read or write as you need to, and be prepared to handle connection errors.

How do you know if a socket is closed?

The most obvious way to accomplish this is having that process call read on the socket for a connection and check whether read returns 0 (i.e. reads zero bytes from the socket), in which case we know that the connection has been closed.

Why does socket close?

The reason for this behavior is the socket is actually closed after the clients first recv() , your server just doesn't realize it until the third attempt at send() . Calling close() in your client didn't change anything because previously the program was terminating which closed the socket anyway.


1 Answers

This is expected, and how the TCP/IP APIs are implemented (so it's similar in pretty much all languages and on all operating systems)

The short story is, you cannot do anything to guarantee that a send() call returns an error directly if that send() call somehow cannot deliver data to the other end. send/write calls just delivers the data to the TCP stack, and it's up to the TCP stack to deliver it when it can.

TCP is also just a transport protocol, if you need to know if your application "messages" have reached the other end, you need to implement that yourself(some form of ACK), as part of your application protocol - there's no other free lunch.

However - if you read() from a socket, you can get notified immediatly when an error occurs, or when the other end closed the socket - you usually need to do this in some form of multiplexing event loop (that is, using select/poll or some other IO multiplexing facility).

Just note that you cannot read() from a socket to learn whether the most recent send/write succeded, Here's a few cases as of why (but it's the cases one doesn't think about that always get you)

  • several write() calls got buffered up due to network congestion, or because the tcp window was closed (perhaps a slow reader) and then the other end closes the socket or a hard network error occurs, thus you can't tell if if was the last write that didn't get through, or a write you did 30 seconds ago.
  • Network error, or firewall silently drops your packets (no ICMP replys are generated), You will have to wait until TCP times out the connection to get an error which can be many seconds, usually several minutes.
  • TCP is busy doing retransmission as you call send - maybe those retransmissions generate an error.(really the same as the first case)
like image 78
nos Avatar answered Oct 13 '22 21:10

nos