Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use python socket.settimeout() properly

As far as I know, when you call socket.settimeout(value) and you set a float value greater than 0.0, that socket will raise a scocket.timeout when a call to, for example, socket.recv has to wait longer than the value specified.

But imagine I have to receive a big amount of data, and I have to call recv() several times, then how does settimeout affect that?

Given the following code:

to_receive = # an integer representing the bytes we want to receive
socket = # a connected socket

socket.settimeout(20)
received = 0
received_data = b""

while received < to_receive:
    tmp = socket.recv(4096)
    if len(tmp) == 0:
        raise Exception()
    received += len(tmp)
    received_data += tmp

socket.settimeout(None)

The third line of the code sets the timeout of the socket to 20 seconds. Does that timeout reset every iteration? Will timeout be raised only if one of those iteration takes more than 20 seconds?

A) How can I recode it so that it raises an exception if it is taking more than 20 seconds to receive all the expected data?

B) If I don't set the timeout to None after we read all data, could anything bad happen? (the connection is keep-alive and more data could be requested in the future).

like image 646
Jorky10 Avatar asked Dec 19 '15 13:12

Jorky10


2 Answers

The timeout applies independently to each call to socket read/write operation. So next call it will be 20 seconds again.

A) To have a timeout shared by several consequential calls, you'll have to track it manually. Something along these lines:

deadline = time.time() + 20.0
while not data_received:
    if time.time() >= deadline:
        raise Exception() # ...
    socket.settimeout(deadline - time.time())
    socket.read() # ...

B) Any code that's using a socket with a timeout and isn't ready to handle socket.timeout exception will likely fail. It is more reliable to remember the socket's timeout value before you start your operation, and restore it when you are done:

def my_socket_function(socket, ...):
    # some initialization and stuff
    old_timeout = socket.gettimeout() # Save
    # do your stuff with socket
    socket.settimeout(old_timeout) # Restore
    # etc

This way, your function will not affect the functioning of the code that's calling it, no matter what either of them do with the socket's timeout.

like image 69
Lav Avatar answered Oct 12 '22 09:10

Lav


The timeout applies to each call to recv().

A) simply use your existing timeout and call recv(to_receive) - I.e. Try to receive all the data in one recv call - in fact I don't see why you shouldn't use this as the default way it works

B) No nothing bad could happen, but any other code which uses that socket needs to be aware of handling timeout.

On your existing code, shouldn't the recv() call be recv(max(4096,to_receive-received)) - that way you won't unintentionally consume any data which follows after the to_receive bytes.

like image 43
DisappointedByUnaccountableMod Avatar answered Oct 12 '22 10:10

DisappointedByUnaccountableMod