I am writing a connector using UDP in Python 3.3
When I am sending data to the UDP port, everything works fine. The problem is that when I am not sending any data, I get an error generated by the receiving port once per minute that says "timed out". While debugging, I used the socket.gettimeout() function and it returned 'None'.
Why am I getting this timeout error? Any help would be greatly appreciated!
import socket
from EventArgs import EventArgs
import logging
class UDPServer(object):
"""description of class"""
def __init__(self, onMessageReceivedEvent = '\x00'):
self.__onMessageReceivedEvent = onMessageReceivedEvent
self.__s = '\x00'
self.__r = '\x00'
def openReceivePort(self,port):
try:
self.__r = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.__r.bind(("",port))
print ("opening port: ", port)
except socket.error as e:
logging.getLogger("ConnectorLogger").critical(e)
def openBroadcastPort(self):
try:
self.__s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.__s.bind(("",2101))
print ("opening port: ", 2101)
except socket.error as e:
logging.getLogger("ConnectorLogger").critical(e)
def closePorts():
if self.__r != '\x00':
self.__r.close()
if self.__s != '\x00':
self.__s.close()
def getUDPData(self):
try:
data, addr = self.__r.recvfrom(1024)
if self.__onMessageReceivedEvent != '\x00':
eventArgs = EventArgs()
eventArgs.Addr = addr
eventArgs.Data = data
self.__onMessageReceivedEvent.fire(self, eventArgs)
except socket.error as e:
logging.getLogger("ConnectorLogger").critical(e)
def send(self,ipAddress,port,message):
try:
self.__s.sendto(message.encode(),(ipAddress,23456))
except socket.error as e:
logging.getLogger("ConnectorLogger").critical(e)
I figured out the answer to my own problem. I was using the default configuration for socket.setblocking which is 0 (non-blocking). The documentation says that using this configuration is the equivalent of using a settimeout value of 0. If I use a blocking socket, it is the equivalent of using a settimeout value of 'None'. Once I changed to a blocking socket I no longer saw this error.
socket.setblocking(flag)-Set blocking or non-blocking mode of the socket: if flag is 0, the socket is set to non- blocking, else to blocking mode. Initially all sockets are in blocking mode. In non-blocking mode, if a recv() call doesn’t find any data, or if a send() call can’t immediately dispose of the data, a error exception is raised; in blocking mode, the calls block until they can proceed. s.setblocking(0) is equivalent to s.settimeout(0.0); s.setblocking(1) is equivalent to s.settimeout(None)*
Related
My problem is like this. I would need to create a client program for quick trial that can send out some commands and listen to data(after which I would need to parse it) from the same socket. So I have created two threads(one to issue command (not shown here), the other to listen to data) to handle this after I created the sockets and connect out to the server. Server is written in other langauges.
As the same socket is to be used, I thought that the socket should be set to be unblocking
the socket after creation is send to the thread as an arguement.
I tried to run the python program. And there is a problem of OSError: [Errno 9] Bad file descriptor. I have narrowed down the problem. It got to do with the recv function.
Most of the solution down in the forum seem to point to socket closure as the main problem but i really could not see how since the socket closure was placed out of while loop.
So need somebody help to point to the problem. And here is my code (as below)
import socket
import errno
import sys
import threading
HOST = "192.168.50.35"
PORT = 2356
def listener(sock, q):
print("status thread created")
while q != True:
try:
data = sock.recv(1024)
except socket.error as socketerr:
if socketerr == errno.EAGAIN or socketerr == errno.EWOULDBLOCK:
sleep(1)
print('Data is unavailable')
continue
else:
print(socketerr)
sys.exit(1)
else:
print(f" new {data!r} recieved")
### processed data
### some processing set but not shown here
continue
sock.close()
def connect():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try:
s.connect((HOST, PORT))
s.setblocking(0)
s.settimeout(1)
except socket.error as socketerr:
print("Error: ", socketerr)
return s
if __name__ == '__main__':
print_machine_info()
q = False
s = connect()
print("socket created")
t1 = threading.Thread(target=status, args=(s,q))
t1.daemon = False
t1.start()
I'm trying to write my own TCP Non-Blocking Server to handle multiple long lasting socket connections rather than opening many threads to handle them.
I've written my over-complicated, hard to use syntax but have the issue forms when I'm trying to detect a closed socket.
In a normal threaded TCP Socket Server I would use detected a b'' from the socket.read(size) function, However this is not possible with a nonblocking socket as it will always return a BlockingIOError
I have also tried catching theese following events
except BrokenPipeError:
conn.abort()
except ConnectionResetError:
conn.abort()
except ConnectionAbortedError:
conn.abort()
except socket.error:
conn.abort()
(conn is a class that houses the client socket and address from socket.accept())
I'm unsure what to do, but here is a deeply simplified extract from my code:
def loop_listen(self):
while self.running == True:
cr, addr = self.server.accept()
crs = SocketHandler(self, cr, addr)
self.client_handler(crs)
self.connections.append(crs)
crs.events["open"]()
crs.cr.setblocking(0)
def loop_recv(self):
while self.running == True:
time.sleep(self.poll_time)
for conn in self.connections:
try:
data = conn.cr.recv(self.poll_size)
print(data)
if (data == b''):
conn.abort()
except BlockingIOError:
data = None
except BrokenPipeError:
conn.abort()
except ConnectionResetError:
conn.abort()
except ConnectionAbortedError:
conn.abort()
except socket.error:
conn.abort()
if (data != None):
conn.events["msg"](data)
(Both loops are separate threads)
And incase you wanted it, here is the conn class
class SocketHandler:
def __init__(self, server, cr, addr):
self.server = server
self.cr = cr
self.addr = addr
self.events = {"msg": emptyCallback, "close": "emptyCallback","open":emptyCallback}
self.cache = b""
def message(self, func):
self.events["msg"] = func
def close(self, func):
self.events["close"] = func
def open(self, func):
self.events["open"] = func
def send(self, data):
self.cr.send(data)
def abort(self):
self.cr.close()
self.events["close"]()
self.server.connections.remove(conn)
This works fine on Windows but on Ubuntu it does not call the conn.abort().
Any help would be greatly appreciated.
Thanks, Sam.
The official way to detect a closed connection on a non-blocking socket is exactly the same as blocking sockets. They return empty data from recv().
Example:
# Server
import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 12345))
s.listen(1)
while True:
conn, addr = s.accept()
conn.setblocking(0)
print("New connection from " + str(addr) + ".")
while True:
try:
data = conn.recv(1024)
if not data:
break
print("Received:", data)
except BlockingIOError:
time.sleep(0.001)
print("Closed.")
# Client
import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 12345))
for i in range(5):
time.sleep(0.3)
s.send(str(i).encode('utf-8'))
s.close()
There is one special case where this won't work, as described in the official docs, section When Sockets Die. It happens, when sockets don't shut down gracefully. There basically is no way for recv() to detect when a socket is dead without a graceful shutdown. It might be that this is what you are seeing.
There are multiple ways to resolve that. For one, create some kind of timeout that closes and discards a socket if it didn't receive a message for a sensible amount of time. Secondly, you could actively send messages. Detecting a dead socket is much easier for send() than for recv().
Further, this works on Linux. I didn't test it on Windows. The internal implementation of the sockets class is very platform dependent, so it might be a Windows bug.
I've implemented some code that allows a client to connect to a socket server, introduces itself and the server then goes into an infinite loop which sends "commands" (strings) to the client from a Redis list. The server uses the Redis 'blpop' method to block until a string arrives which is then sent off to the client and the response awaited.
However, in testing (with a python client socket script on another local workstation) I find that if I break the client connection (Ctrl+c) to simulate an interruption in the connectivity, the server happily writes the next received string to the client, reports an empty response but ONLY throws the broken pipe exception when a second string is written :/ Thus, two writes are "lost" before anything is caught. Here's my code:
# Create global Redis resource
rds_cnx = redis.StrictRedis(host='localhost', port=6379, db=6)
def initialise_server():
""" Setup server socket """
try:
srv_skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv_skt.bind((IP, PORT))
srv_skt.listen(1)
print("Listening on:[{}]".format(IP, PORT))
return srv_skt
except socket.error as skt_err: # e.g. port in use
print("Could not initialise tcp server:[{}]".format(skt_err))
sys.exit(1)
except Exception as exp:
print("Unable to setup server socket:[{}]".format(exp))
sys.exit(1)
def main():
server_socket = initialise_server()
while True:
client_socket, remote_address = server_socket.accept()
try:
# Block and wait for connection and data
initial_data = client_socket.recv(1024).decode()
print("Connection from [{}] - Data:[{}]".format(remote_address, initial_data))
while True:
wait_for_queue_command(client_socket)
except (BrokenPipeError, socket.error, Exception) as sck_exp:
print("Exception in client loop:[{}]".format(sck_exp))
continue
except KeyboardInterrupt:
# Close client socket
client_socket.shutdown(2)
client_socket.close()
print('Caught Ctrl+c ... Shutting down.')
break
# Tear down context
server_socket.shutdown(2) # Param ref: 0 = done receiving, 1 = done sending, 2 = both
server_socket.close()
def wait_for_queue_command(client_skt):
""" Blocking while waiting for command for Redis list
:param client_skt: socket
:return: None
"""
print('Waiting for command...')
queue_cmd = rds_cnx.blpop('queuetest', 0)
print("Received something from the queue:")
pprint(queue_cmd)
try:
#client_skt.settimeout(15)
client_skt.send(queue_cmd[1])
# Block for response
response_data = client_skt.recv(1024).decode()
print("Response:[{}]".format(response_data))
except BrokenPipeError as brkn_p:
print('Outbound write detected "Broken Pipe":[{}]'.format(brkn_p))
''' Here one would decide to either re-schedule the command or
ignore the error and move on to the next command. A "pause"
(sleep) could also useful?
'''
raise
except socket.timeout as sck_tmo:
print('Socket timed out:[{}]'.format(sck_tmo))
except socket.error as sck_err:
print('Socket timed out:[{}]'.format(sck_err))
raise
print('Command handling complete.')
Is there any better way to handle such a situation? I've had a cursory look at Twisted but it seems very difficult to achieve the specific blocking behavior and other code that might be implemented to handle specific responses from the client.
I'm trying to get a small socket communication set up on my own machine for testing purposes, but I keep getting errors like "[Errno 10053] An established connection was aborted by the software in your host machine" and "[Errno 10054] An existing connection was forcibly closed by the remote host"
The code for the server is
import socket, threading, Queue
class PiConn(threading.Thread, object):
def __init__(self, input_queue, output_queue):
threading.Thread.__init__(self)
self.input_queue = input_queue
self.output_queue = output_queue
self.HOST = ''
self.PORT = 8888
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
self.s.bind((self.HOST, self.PORT))
except socket.error, msg:
print "Binding socket failed, error message: " + msg[1]
def run(self):
self.s.listen(5)
while True:
try:
#trying to accept data
conn, addr = self.s.accept()
print "Connected to", addr
data = conn.recv(4096)
self.input_queue.put(data)
except Exception as e:
print e, "when trying to accept data"
break
try:
output = self.output_queue.get(False)
self.s.sendall(output)
print "Sent", output
except Queue.Empty:
pass
except socket.error as e:
print e, "when trying to send data"
input_queue = Queue.Queue()
output_queue = Queue.Queue()
conn = PiConn(input_queue, output_queue)
conn.start()
while True:
output_queue.put("This is sent by server")
try:
print input_queue.get(False)
except Queue.Empty:
pass
The code for the client is
import socket, threading, Queue
class GUIConn(threading.Thread, object):
def __init__(self, input_queue, output_queue):
threading.Thread.__init__(self)
self.input_queue = input_queue
self.output_queue = output_queue
self.PORT = 8888
self.PI_IP = "127.0.0.1"
try:
#Creates a socket
self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error, msg:
print 'Socket creating failed, error message:' + str(msg[1])
self.s.connect((self.PI_IP, self.PORT))
def run(self):
while True:
try:
#Trying to send data
output = self.output_queue.get(False)
self.s.sendall(output)
except Queue.Empty:
pass
except socket.error as e:
print e
try:
#trying to accept data
data = self.s.recv(4096)
self.input_queue.put(data)
except Exception as e:
print e
break
input_queue = Queue.Queue()
output_queue = Queue.Queue()
conn = GUIConn(input_queue, output_queue)
conn.start()
while True:
output_queue.put("This is sent by client")
try:
print input_queue.get(False)
except Queue.Empty:
pass
To test it, I start 2 IDLE shells, run the server, and then the client.
Any clue as to what I'm doing wrong? I'm fairly new at sockets, and I've been struggling with this all day.
Thanks in advance!
Your initial problem is caused by known issues IDLE has when working with threads.
See here and here for example.
I'm not aware of any workaround. Try running your code from terminal instead.
As to the other errors you're getting, if you post them, we can try and assist.
warning, big wall of text, read all of it before commenting
there is a huge number of problem with this small amount of code
first, the most obvious is the 'busy' loops that will use up all 100% of the cpu, not only that, it will also slowly use up all the ram as well cause you set the blocking for the queue.get to be False
you could have set it to True and it would have waited until there something and once it get that, it would loop back to the top and put another one of "This is sent by client" thus solving both the busy loop and ram usage problem
while True:
output_queue.put("This is sent by client")
try:
print input_queue.get(False) # here
except Queue.Empty:
pass
second, the way you reply/send data from the server to the client isn't through the main listening socket but the socket that is return from the self.s.accept()
so self.s.sendall(output) in the server should have been conn.sendall(output)
third, in the client code, there a chance that self.output_queue.get(False) would error with Queue.Empty and thus pass using the try and except and ended up in the blocking recv
and both the server and client would both be listening and waiting for each other to send something
fourth, self.s.accept() is blocking, after one loop in the server, it would be stuck waiting for another client while the client would send the data then end up waiting for some data
lastly, about those error you said, i can't reproduce them at all, if i have to guess, i say those error are cause by your firewall or the server isn't running (fail to bind) or something else, see here: No connection could be made because the target machine actively refused it
also, you could try a different port and maybe the first two example on this site to check if there is something weird causing problem, if those example doesn't work then there is a problem with your computer, https://docs.python.org/release/2.5.2/lib/socket-example.html
I'm trying to translate this from Ruby to Python.
Ruby code:
def read_byte
begin
Timeout.timeout(0.5) do
b = socket.read 1
end
rescue Timeout::Error => e
socket.write("\n")
socket.flush
retry
end
end
def socket
#socket ||= TCPSocket.open #host, #port
rescue SocketError
# TODO: raise a specific error
raise "Unable to open connection to #{#host} with given parameters"
end
My mean problem here is with
socket.flush
I can't find a way to do flush. what other way can I do this?
I wrote this.
Python code:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.host, self.port))
s.settimeout(0.5)
while True:
try:
print s.recv(1)
except socket.timeout:
s.sendall("\n")
I doubt that flushing the socket will make a difference, but here is a way to "flush" the socket by first creating a file-like object.
def flush_socket(s):
f = s.makefile()
f.flush()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.host, self.port))
s.settimeout(0.5)
while True:
try:
print s.recv(1)
except socket.timeout:
s.sendall("\n")
flush_socket(s)
The stream is kinda hanging with my code.
Of course it is, since it is an endless loop unless some exception other than socket.timeout occurs.
maybe it's another part of the ruby code
It must be ... Inspect the Ruby loop where read_byte is called and compare that to your Python while True.