I am experiencing some weird behavior when calling socket.send(), which alternatively succeeds and fails with a "connection refused" error:
import socket, time
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
dest = ("127.0.0.1", 1111)
sock.connect(dest)
counter = 1
while True:
try:
sent = sock.send(b"message\n")
print("counter {}, sent {}".format(counter, sent))
except Exception as e:
print("counter {}, exception {}".format(counter, e))
finally:
counter += 1
time.sleep(0.5)
Which outputs:
counter 1, sent 8
counter 2, exception [Errno 111] Connection refused
counter 3, sent 8
counter 4, exception [Errno 111] Connection refused
counter 5, sent 8
counter 6, exception [Errno 111] Connection refused
counter 7, sent 8
counter 8, exception [Errno 111] Connection refused
counter 9, sent 8
...
I expected the send to always succeed.
I want the code to run regardless of whether there is another process listening on that port or not (if there is one, e.g. Netcat
nc -u -l 1111,send()in my example always succeeds).the intermittent failure made me think of a buffering issue, but changing the buffersize (
setsockopt) did not affect anything (but the kernel imposes a minimum buffer size, so one cannot really set it to 0). Furthermore:Using
sendto()instead ofsend()(without the initialconnect()) works as expected, i.e., sending never fails regardless of the listener being active or not.Using a plain socket in C also works as expected, with both
send()andsendto().
I am totally puzzled, what is happening here?
I could not find any documentation about this behavior in the python docs, nor on the C manual pages, nor searching the web. I am sorry if I am missing something obvious.
Python 3.10.12 on Linux (Ubuntu)
The first
sendjust puts the data into the socket buffer. This will succeed, so no error will be returned.The OS then will try to deliver the data in the socket buffer to the target. If there is no socket in the target or some firewall is blocking access, then the target mit generate an ICMP messsage "Destination unreachable". This ICMP message will result in an error stored for the socket.
This error will be delivered to the application on the next system call related to the socket, i.e. the next
sendin your application. That's why this nextsendwill return with an error. Because of the error on the socket thesendwill also not put the data in the socket buffer, so no data will be delivered to the target.Returning the error clears the error on the socket. That's why the next
sendon the socket can succeed again without error.The data will again be put in the socket buffer, the OS will try to deliver it which again will result in an ICMP unreachable which again will be delivered as error in the next
sendand so on ....