Python socket non-blocking recv() exception(s) and sendall() exception - python

The bounty expires in 5 days. Answers to this question are eligible for a +50 reputation bounty.
chakmeshma wants to draw more attention to this question.
I'm writing a simple TCP relay server, which is gonna be deployed both on a Windows and a Linux machine (same code base).
Naturally there're gonna be two sockets to work with.
I would like to know which exceptions exactly do get raised for the following cases:
recv() returns when no data is available to read.
sendall() cannot complete (dispose of the whole to-send data)
Do I have to check for both errnos (socket.EWOULDBLOCK and socket.EAGAIN) when expecting to return from a non-blocking sockets recv()?
Which errno (.args[0]) do I get when sendall() fails?
Here's my code so far:
try:
socket1.setblocking(False)
socket2.setblocking(False)
while True:
try:
sock1_data = socket1.recv(1024)
if sock1_data:
socket2.sendall(sock1_data)
except socket.error as e:
if e.args[0] != socket.EAGAIN and e.args[0] != socket.EWOULDBLOCK:
raise e
try:
sock2_data = socket2.recv(1024)
if sock2_data:
socket1.sendall(sock2_data)
except socket.error as e:
if e.args[0] != socket.EAGAIN and e.args[0] != socket.EWOULDBLOCK:
raise e
except:
pass
finally:
if socket2:
socket2.close()
if socket1:
socket1.close()
My main concern is:
What socket.error.errno do I get when sendall() fails?
Lest the socket.error.errno I get from a failing sendall() is EAGAIN or EWOULDBLOCK, in which case it'd be troubling!!
Also, do I have to check for both EAGAIN and EWOULDBLOCK when handling a non-blocking recv()?

Related

Find socket responsible for timeout?

sock1.settimeout(2)
conn.settimeout(1) #conn comes from sock1
except socket.timeout, e:
print <responsible socket>
Is there a way to distinguish the socket responsible for the timeout?
Perhaps I'm doing something wrong if I have two sockets that are timing out.
As far as I can tell, there's nothing in the socket.timeout exception object that identifies the socket. So you need to keep track of which socket you're reading from, that will be the one that timed out:
try:
cursock = sock1
data = sock1.recv(bufsize)
cursock = conn
data1 = conn.recv(bufsize)
except socket.timeout, e:
print cursock
Or you could wrap try/except around each recv call. You could put this into a helper function:
def try_recv(sock, bufsize, flags=0):
try:
return sock.recv(bufsize, flag)
except socket.timeout, e:
print sock

Python socket connection exception

I have a socket-connection going on and I wanna improve the exception handling and I'm stuck. Whenever I call socket.connect(server_address) with an invalid argument the program stops, but doesn't seem to raise an exception. Here is my code:
import socket
import sys
import struct
class ARToolkit():
def __init__(self):
self.x = 0
self.y = 0
self.z = 0
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.logging = False
def connect(self,server_address):
try:
self.sock.connect(server_address)
except socket.error, msg:
print "Couldnt connect with the socket-server: %s\n terminating program" % msg
sys.exit(1)
def initiate(self):
self.sock.send("start_logging")
def log(self):
self.logging = True
buf = self.sock.recv(6000)
if len(buf)>0:
nbuf = buf[len(buf)-12:len(buf)]
self.x, self.y, self.z = struct.unpack("<iii", nbuf)
def stop_logging(self):
print "Stopping logging"
self.logging = False
self.sock.close()
The class maybe looks a bit wierd but its used for receiving coordinates from another computer running ARToolKit. Anyway, the issue is at the function connect():
def connect(self,server_address):
try:
self.sock.connect(server_address)
except socket.error, msg:
print "Couldnt connect with the socket-server: %s\n terminating program" % msg
sys.exit(1)
If I call that function with a random IP-address and portnumber the whole program just stops up at the line:
self.sock.connect(server_address)
The documentation I've read states that in case of an error it will throw a socket.error-exception. I've also tried with just:
except Exception, msg:
This, if I'm not mistaken, will catch any exceptions, and still it yields no result. I would be very grateful for a helping hand. Also, is it okay to exit programs using sys.exit when an unwanted exception occurs?
Thank you
If you have chosen a random, but valid, IP address and port, socket.connect() will attempt to make a connection to that endpoint. By default, if no explicit timeout is set for the socket, it will block while doing so and eventually timeout, raising exception socket.error: [Errno 110] Connection timed out.
The default timeout on my machine is 120 seconds. Perhaps you are not waiting long enough for socket.connect() to return (or timeout)?
You can try reducing the timeout like this:
import socket
s = socket.socket()
s.settimeout(5) # 5 seconds
try:
s.connect(('123.123.123.123', 12345)) # "random" IP address and port
except socket.error, exc:
print "Caught exception socket.error : %s" % exc
Note that if a timeout is explicitly set for the socket, the exception will be socket.timeout which is derived from socket.error and will therefore be caught by the above except clause.
The problem with your last general exception is the colon placement. It needs to be after the entire exception, not after the except statement. Thus to capture all exceptions you would need to do:
except Exception,msg:
However from Python 2.6+ you should use the as statement instead of a comma like so:
except Exception as msg:
I was able to run the code fine (note you need to throw in a tuple to the connect method). If you want to specifically catch only socket errors then you would need to except the socket.error class. Like you have:
except socket.error as msg:
If you want to make sure that a tuple is entered simply add another exception loop:
except socket.error as msg:
print "Socket Error: %s" % msg
except TypeError as msg:
print "Type Error: %s" % msg

Python TCPServer Timout socket timeout handling

I am trying to handle a hard to duplicate error case using TCPServer. We have seen a few occurrences that when a socket timeout happens in the handler, the code never recovers and keeps returning the socket.timeout exception.
This looks to be from the following snippet in the Socket.py library code:
def readinto(self, b):
"""Read up to len(b) bytes into the writable buffer *b* and return
the number of bytes read. If the socket is non-blocking and no bytes
are available, None is returned.
If *b* is non-empty, a 0 return value indicates that the connection
was shutdown at the other end.
"""
self._checkClosed()
self._checkReadable()
if self._timeout_occurred:
raise OSError("cannot read from timed out object")
while True:
try:
return self._sock.recv_into(b)
except timeout:
self._timeout_occurred = True
raise
except InterruptedError:
continue
except error as e:
if e.args[0] in _blocking_errnos:
return None
raise
once a timeout has occurred _timeout_occurred is set to True, and the next pass into this function, the socket has the flag set, and will immediately exit with the cannot read from timed out object error.
Now the code uses TCP Server (only relevant code included) Basically it is reading stuff from the socket, and queing it to be handled separately.
def get_event(file_):
pre_package_len = 8
msg = file_.read(pre_package_len)
if len(msg) == pre_package_len:
pkg = PRE_PACKAGE_FRAME.unpack(msg)
msg = file_.read(pkg['len'])
logger.debug('recv: type: %s len: %s bytes read: %s',
pkg['type'], pkg['len'], len(msg))
if len(msg) >= pkg['len']:
if pkg['type'] == cdefs.kNotification:
e = EVENT_FRAME.unpack(msg)
return decode_event(e)
logger.warn('received unsupported package type: %s', pkg['type'])
else:
logger.error('failed to recv')
class _EventHandler(StreamRequestHandler):
def handle(self):
logger.debug("Got event from %s", self.client_address)
try:
e = get_event(self.rfile)
if e:
self.q.put(e)
except socket.timeout:
logger.error('timed out reading event')
def process_event(q, handler, shutdown_sentinel):
for e in iter(q.get, shutdown_sentinel):
try:
handler(e)
except Exception:
logger.exception('Unhandled exception handling event: %s', e)
logger.info('exiting')
def eventhandler_maker(q, timeout):
return type('NewEventHandler',
(_EventHandler, object),
dict(q=q, timeout=timeout))
def process_events(handler, address, timeout=20):
sentinel = object()
q = Queue()
eventhandler = eventhandler_maker(q, timeout)
server = TCPServer(address, eventhandler)
start_thread(server.serve_forever)
start_thread(process_event, (q, handler, sentinel))
def shutdown():
logger.info('shutting down')
q.put(sentinel)
server.shutdown()
def add_event(e):
q.put(e)
return shutdown, add_event
The symptoms are that once the timeout happens, the log keeps showing 'timed out reading event' and the code never does anything anymore. I added code to dump out the server.socket.gettimeout() and socket.getdefaulttimeout() and both return None. This application is running on an embedded Linux 3.10 kernel with python 3.4.0.
I have 2 questions here:
What is a good recovery strategy here? Shutdown() / Close() the server socket and then restart it? Or are there better strategies?
Is there a good third party tool to provoke a timeout so a recovery strategy can be proven to be correct?

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...
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.
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).
When you use recv in connection with select if the socket is ready to be read from but there is no data to read that means the client has closed the connection.
Here is some code that handles this, also note the exception that is thrown when recv is called a second time in the while loop. If there is nothing left to read this exception will be thrown it doesn't mean the client has closed the connection :
def listenToSockets(self):
while True:
changed_sockets = self.currentSockets
ready_to_read, ready_to_write, in_error = select.select(changed_sockets, [], [], 0.1)
for s in ready_to_read:
if s == self.serverSocket:
self.acceptNewConnection(s)
else:
self.readDataFromSocket(s)
And the function that receives the data :
def readDataFromSocket(self, socket):
data = ''
buffer = ''
try:
while True:
data = socket.recv(4096)
if not data:
break
buffer += data
except error, (errorCode,message):
# error 10035 is no data available, it is non-fatal
if errorCode != 10035:
print 'socket.error - ('+str(errorCode)+') ' + message
if data:
print 'received '+ buffer
else:
print 'disconnected'
Just to complete the existing answers, I'd suggest using select instead of nonblocking sockets. The point is that nonblocking sockets complicate stuff (except perhaps sending), so I'd say there is no reason to use them at all. If you regularly have the problem that your app is blocked waiting for IO, I would also consider doing the IO in a separate thread in the background.

Python: except EOFError: ... doesn't work

I have a try/except block that sends a message and waits for confirmation from client. If the client terminates, pickle raises an EOFError, but the code below does not catch the error and execute the graceful shut down. It instead prints stack trace. I assume it has to do with the line "except socket.error, EOFError:" - am I using the wrong syntax to handle both socket.error and EOFError there?
try:
msgs = [1]
self.sock.send(pickle.dumps(msgs))
rdy = pickle.loads(self.sock.recv(2097152))
except socket.error, EOFError:
print 'log socketmanager closing'
self.terminate()
break
In Python 2.x, the form except a, b catches an exception of type a and assign it to a variable called b. In your case this would result in EOFError being ignored. Try this instead:
...
except (socket.error, EOFError):
...
Edit: to elaborate, the new syntax in Python 3.0, and available, though not required, in 2.6+, for capturing the value of an exception is except a as b.
break is causing the error, it can only be used inside a for loop or a try/finally block, not try/except, see docs and more.

Categories

Resources