def sendData(sock, data): """ Send some data over a socket. Some systems have problems with ``sendall()`` when the socket is in non-blocking mode. For instance, Mac OS X seems to be happy to throw EAGAIN errors too often. This function falls back to using a regular send loop if needed. """ if sock.gettimeout() is None: # socket is in blocking mode, we can use sendall normally. try: sock.sendall(data) return except socket.timeout: raise TimeoutError("sending: timeout") except socket.error as x: raise ConnectionClosedError("sending: connection lost: " + str(x)) else: # Socket is in non-blocking mode, use regular send loop. retrydelay = 0.0 while data: try: sent = sock.send(data) data = data[sent:] except socket.timeout: raise TimeoutError("sending: timeout") except socket.error as x: err = getattr(x, "errno", x.args[0]) if err not in ERRNO_RETRIES: raise ConnectionClosedError("sending: connection lost: " + str(x)) time.sleep( 0.00001 + retrydelay) # a slight delay to wait before retrying retrydelay = __nextRetrydelay(retrydelay)
def receiveData(sock, size): """Retrieve a given number of bytes from a socket. It is expected the socket is able to supply that number of bytes. If it isn't, an exception is raised (you will not get a zero length result or a result that is smaller than what you asked for). The partial data that has been received however is stored in the 'partialData' attribute of the exception object.""" try: retrydelay = 0.0 msglen = 0 chunks = [] if config.USE_MSG_WAITALL and not hasattr(sock, "getpeercert"): # waitall is very convenient and if a socket error occurs, # we can assume the receive has failed. No need for a loop, # unless it is a retryable error. # Some systems have an erratic MSG_WAITALL and sometimes still return # less bytes than asked. In that case, we drop down into the normal # receive loop to finish the task. # Also note that on SSL sockets, you cannot use MSG_WAITALL (or any other flag) while True: try: data = sock.recv(size, socket.MSG_WAITALL) if len(data) == size: return data # less data than asked, drop down into normal receive loop to finish msglen = len(data) chunks = [data] break except socket.timeout: raise TimeoutError("receiving: timeout") except socket.error as x: err = getattr(x, "errno", x.args[0]) if err not in ERRNO_RETRIES: raise ConnectionClosedError( "receiving: connection lost: " + str(x)) time.sleep( 0.00001 + retrydelay) # a slight delay to wait before retrying retrydelay = __nextRetrydelay(retrydelay) # old fashioned recv loop, we gather chunks until the message is complete while True: try: while msglen < size: # 60k buffer limit avoids problems on certain OSes like VMS, Windows chunk = sock.recv(min(60000, size - msglen)) if not chunk: break chunks.append(chunk) msglen += len(chunk) data = b"".join(chunks) del chunks if len(data) != size: err = ConnectionClosedError("receiving: not enough data") err.partialData = data # store the message that was received until now raise err return data # yay, complete except socket.timeout: raise TimeoutError("receiving: timeout") except socket.error: x = sys.exc_info()[1] err = getattr(x, "errno", x.args[0]) if err not in ERRNO_RETRIES: raise ConnectionClosedError( "receiving: connection lost: " + str(x)) time.sleep( 0.00001 + retrydelay) # a slight delay to wait before retrying retrydelay = __nextRetrydelay(retrydelay) except socket.timeout: raise TimeoutError("receiving: timeout")