Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does Python's socket.recv() return for non-blocking sockets if no data is received until a timeout occurs?

Basically, I've read in several places that socket.recv() will return whatever it can read, or an empty string signalling that the other side has shut down (the official docs don't even mention what it returns when the connection is shut down... great!). This is all fine and dandy for blocking sockets, since we know that recv() only returns when there actually is something to receive, so when it returns an empty string, it MUST mean the other side has closed the connection, right?

Okay, fine, but what happens when my socket is non-blocking?? I have searched a bit (maybe not enough, who knows?) and can't figure out how to tell when the other side has closed the connection using a non-blocking socket. There seems to be no method or attribute that tells us this, and comparing the return value of recv() to the empty string seems absolutely useless... is it just me having this problem?

As a simple example, let's say my socket's timeout is set to 1.2342342 (whatever non-negative number you like here) seconds and I call socket.recv(1024), but the other side doesn't send anything during that 1.2342342 second period. The recv() call will return an empty string and I have no clue as to whether the connection is still standing or not...

like image 323
El Ninja Trepador Avatar asked May 25 '13 00:05

El Ninja Trepador


People also ask

What does recv () return in Python?

If no error occurs, recv returns the bytes received. If the connection has been gracefully closed, the return value is an empty byte string.

What does socket recv return?

The recv() call applies only to connected sockets. This call returns the length of the incoming message or data.

What does RECV return when there is no more data in the OS buffer and the other side has closed the socket?

Return value If no error occurs, recv returns the number of bytes received and the buffer pointed to by the buf parameter will contain this data received. If the connection has been gracefully closed, the return value is zero.

What is non-blocking socket in Python?

When you make a socket non-blocking by calling setblocking(0) , it will never wait for the operation to complete. So when you call the send() method, it will put as much data in the buffer as possible and return. As this is read by the remote connection, the data is removed from the buffer.


2 Answers

In the case of a non blocking socket that has no data available, recv will throw the socket.error exception and the value of the exception will have the errno of either EAGAIN or EWOULDBLOCK. Example:

import sys import socket import fcntl, os import errno from time import sleep  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1',9999)) fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK)  while True:     try:         msg = s.recv(4096)     except socket.error, e:         err = e.args[0]         if err == errno.EAGAIN or err == errno.EWOULDBLOCK:             sleep(1)             print 'No data available'             continue         else:             # a "real" error occurred             print e             sys.exit(1)     else:         # got a message, do something :) 

The situation is a little different in the case where you've enabled non-blocking behavior via a time out with socket.settimeout(n) or socket.setblocking(False). In this case a socket.error is stil raised, but in the case of a time out, the accompanying value of the exception is always a string set to 'timed out'. So, to handle this case you can do:

import sys import socket from time import sleep  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('127.0.0.1',9999)) s.settimeout(2)  while True:     try:         msg = s.recv(4096)     except socket.timeout, e:         err = e.args[0]         # this next if/else is a bit redundant, but illustrates how the         # timeout exception is setup         if err == 'timed out':             sleep(1)             print 'recv timed out, retry later'             continue         else:             print e             sys.exit(1)     except socket.error, e:         # Something else happened, handle error, exit, etc.         print e         sys.exit(1)     else:         if len(msg) == 0:             print 'orderly shutdown on server end'             sys.exit(0)         else:             # got a message do something :) 

As indicated in the comments, this is also a more portable solution since it doesn't depend on OS specific functionality to put the socket into non-blockng mode.

See recv(2) and python socket for more details.

like image 157
mshildt Avatar answered Sep 22 '22 22:09

mshildt


It is simple: if recv() returns 0 bytes; you will not receive any more data on this connection. Ever. You still might be able to send.

It means that your non-blocking socket have to raise an exception (it might be system-dependent) if no data is available but the connection is still alive (the other end may send).

like image 44
jfs Avatar answered Sep 20 '22 22:09

jfs