I'm wondering how to correctly create background thread that would be listenning some random port and pushing received object to Queue?
I want my socket wrapper to launch new thread, select some random port and start listenning on in. I have to be able to get this port number from socket wrapper.
I've come up with simple class:
class SocketWrapper(Thread):
def __init__(self, socket_type, *args, **kwargs):
super(Thread, self).__init__(*args, **kwargs)
self._ctx = zmq.Context()
self._socket = self._ctx._socket(socket_type)
self.port = self._socket.bind_to_random_port('tcp://*')
self._queue = Queue()
def run(self):
while not self.stop_requested:
try:
item = socket.recv_pyobj(flags=zmq.NOBLOCK)
self._queue.put(item)
except ZMQError:
time.sleep(0.01) # Wait a little for next item to arrive
However, zmq sockets can't be shared between threads, they are not thread-safe (http://api.zeromq.org/2-1:zmq). So socket creation and binding should be moved to run() method:
class SocketWrapper2(Thread):
def __init__(self, socket_type, *args, **kwargs):
super(Thread, self).__init__(*args, **kwargs)
self._socket_type = socket_type
self._ctx = zmq.Context()
self._queue = Queue()
self._event = Event()
def run(self):
socket = self._ctx._socket(self._socket_type)
self.port = self._socket.bind_to_random_port('tcp://*')
self._event.set()
while not self.stop_requested:
try:
item = socket.recv_pyobj(flags=zmq.NOBLOCK)
self._queue.put(item)
except ZMQError:
time.sleep(0.01) # Wait a little for next item to arrive
def get_port(self):
self._event.wait()
return self.port
I had to add event to be sure that port is already binded before I can read it but it introduces risk of deadlock, when SocketWrapper2.get_port() is called before start(). This can be avoided by using Thread's _started Event:
def get_port(self):
if not self._started.is_set():
raise RuntimeError("You can't call run_port before thread start.")
self._event.wait()
return self.port
Is this is at last thread-safe? Is there anything else to take care of?
Problem I still see here is that I want to get port right after SocketWrapper is created. Can I safely call Thread's start() in __init__?
I ended up modifying this solution a little to avoid deadlocking main thread:
def get_port(self):
if not self._started.is_set():
raise RuntimeError("You can't call run_port before thread start.")
if not self._event.wait(1):
raise RuntimeError("Couldn't get port after a while.")
return self.port
This is not perfect. Since we delay get_port but it's simple and do the job. Any suggestions how to improve it?
Related
I am writing an application in python to acquire data using a serial communication. I use the pyserial library for establishing the communication. What is the best approach to request data in an interval (eg every 2 seconds). I always have to the send a request, wait for the answer and start the process again.
if this a "slow" process, that does not accurate time precision, use a while loop and time.sleep(2) to timeout the process for 2 seconds.
I thought about using a separate thread to prevent the rest of the applicaiton from freezing. The thread takes a function which requests data from the instrument.
class ReadingThread(Thread):
'''Used to read out from the instrument, interrupted by an interval'''
def __init__(self, controller, lock, function, queue_out, interval=3 , *args, **kwargs):
Thread.__init__(self)
self.lock = lock
self.function = function
self.queue_out = queue_out
self.interval = interval
self.args = args
self.kwargs = kwargs
self.is_running = True
def run(self):
while self.is_running:
with self.lock:
try:
result = self.function(self.args, self.kwargs)
except Exception as e:
print(e)
else:
self.queue_out.put(result)
time.sleep(self.interval)
def stop(self):
self.is_running = False
I'm trying to interrupt multiprocessing.connection.Listener.accept(), but have thus far been unsuccessful. Since it doesn't provide a timeout parameter, I thought perhaps I could use socket.setdefaulttimeout() to interrupt it, as suggested in post I cannot find anymore, here on SO.
This didnt work. I then tried calling close() on the Listener() object. according to this post's answer, this should have worked.
It appears, however, that these objects to not play along with the usual socket-related solutions.
I can confirm that that the Listener is closed by the Timer object as expected, but the accept() call isn't interrupted.
The Code:
import logging
import socket
import os
from multiprocessing.connection import Listener
from queue import Queue, Empty
from threading import Thread, Event, Timer
class Node(Thread):
"""Base Class providing a AF_INET, AF_UNIX or AF_PIPE connection to its
data queue. It offers put() and get() method wrappers, and therefore
behaves like a Queue as well as a Thread.
Data from the internal queue is automatically fed to any connecting client.
"""
def __init__(self, sock_name, max_q_size=None, timeout=None,
*thread_args, **thread_kwargs):
"""Initialize class.
:param sock_name: UDS, TCP socket or pipe name
:param max_q_size: maximum queue size for self.q, default infinite
"""
self._sock_name = sock_name
self.connector = Listener(sock_name)
max_q_size = max_q_size if max_q_size else 0
self.q = Queue(maxsize=max_q_size)
self._running = Event()
self.connection_timer = Timer(timeout, self.connection_timed_out)
super(Node, self).__init__(*thread_args, **thread_kwargs)
def connection_timed_out(self):
"""Closes the Listener and shuts down Node if no Client connected.
:return:
"""
self.connector.close()
self.join()
def _start_connection_timer(self):
self.connection_timer.start()
def start(self):
self._running.set()
super(Node, self).start()
def join(self, timeout=None):
print("clearing..")
self._running.clear()
print("internal join")
super(Node, self).join(timeout=timeout)
print("Done")
def run(self):
while self._running.is_set():
print("Accepting connections..")
self._start_connection_timer()
try:
client = self.connector.accept()
self.connection_timer.cancel()
self.feed_data(client)
except (TimeoutError, socket.timeout):
continue
except Exception as e:
raise
print("Run() Terminated!")
def feed_data(self, client):
try:
while self._running.is_set():
try:
client.send(self.q.get())
except Empty:
continue
except EOFError:
return
if __name__ == '__main__':
import time
n = Node('/home/nils/git/spab2/test.uds', timeout=10)
n.start()
print("Sleeping")
time.sleep(15)
print("Manual join")
n.join()
I realize my question is a duplicate of this question - however, it is almost one year old and has not even received a comment. In addition, I'm using Unix Domain Sockets, as opposed to the linked post's TCP connection.
I managed to set the timeout in the following way in Python 2.7:
self.listener = mpc.Listener((address, port))
self.listener._listener._socket.settimeout(3)
With this, the accept() call is interrupted.
Result:
conn = self.listener.accept()
File "/usr/lib/python2.7/multiprocessing/connection.py", line 145, in accept
c = self._listener.accept()
File "/usr/lib/python2.7/multiprocessing/connection.py", line 275, in accept
s, self._last_accepted = self._socket.accept()
File "/usr/lib/python2.7/socket.py", line 202, in accept
sock, addr = self._sock.accept()
timeout: timed out
Regards,
Henri
I'm using Python 2.7.
class Client():
def __init__(self, host, server_port):
"""
This method is run when creating a new Client object
"""
self.host = 'localhost'
self.server_Port = 1337
# Set up the socket connection to the server
self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.receiver = None
self.myParser = MessageParser()
# TODO: Finish init process with necessary code
self.run()
def run(self):
self.connection.connect((self.host, self.server_Port))
self.receiver = MessageReceiver(self, self.connection) #On this line, a MessageReceiver object is instantiated.
self.take_input()
class MessageReceiver(Thread):
def __init__(self, client, connection):
super(MessageReceiver, self).__init__()
self.myClient = client
self.connection = connection
self.daemon = True
self.run()
def run(self):
self.myClient.receive_message(self.connection.recv(1024)) #This line blocks further progress in the code.
When the run-method in the Client object instantiates a MessageReceiver object, I want the next line of code in Client to be executed immediately, without waiting for an exit code from MessageReceiver. Is there a way to do this?
self.run()
Call start() instead. run() executes the run method in the current thread. start() spins up another thread and calls it there.
self.start()
I am implementing a WebSockets server in Tornado 3.2. The client connecting to the server won't be a browser.
For cases in which there is back-and-forth communication between server and client, I would like to add a max. time the server will wait for a client response before closing the connection.
This is roughly what I've been trying:
import datetime
import tornado
class WSHandler(WebSocketHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.timeout = None
def _close_on_timeout(self):
if self.ws_connection:
self.close()
def open(self):
initialize()
def on_message(self, message):
# Remove previous timeout, if one exists.
if self.timeout:
tornado.ioloop.IOLoop.instance().remove_timeout(self.timeout)
self.timeout = None
if is_last_message:
self.write_message(message)
self.close()
else:
# Add a new timeout.
self.timeout = tornado.ioloop.IOLoop.instance().add_timeout(
datetime.timedelta(milliseconds=1000), self._close_on_timeout)
self.write_message(message)
Am I being a klutz and is there a much simpler way of doing this? I can't even seem to schedule a simple print statement via add_timeout above.
I also need some help testing this. This is what I have so far:
from tornado.websocket import websocket_connect
from tornado.testing import AsyncHTTPTestCase, gen_test
import time
class WSTests(AsyncHTTPTestCase):
#gen_test
def test_long_response(self):
ws = yield websocket_connect('ws://address', io_loop=self.io_loop)
# First round trip.
ws.write_message('First message.')
result = yield ws.read_message()
self.assertEqual(result, 'First response.')
# Wait longer than the timeout.
# The test is in its own IOLoop, so a blocking sleep should be okay?
time.sleep(1.1)
# Expect either write or read to fail because of a closed socket.
ws.write_message('Second message.')
result = yield ws.read_message()
self.assertNotEqual(result, 'Second response.')
The client has no problem writing to and reading from the socket. This is presumably because the add_timeout isn't firing.
Does the test need to yield somehow to allow the timeout callback on the server to run? I would have thought not since the docs say the tests run in their own IOLoop.
Edit
This is the working version, per Ben's suggestions.
import datetime
import tornado
class WSHandler(WebSocketHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.timeout = None
def _close_on_timeout(self):
if self.ws_connection:
self.close()
def open(self):
initialize()
def on_message(self, message):
# Remove previous timeout, if one exists.
if self.timeout:
tornado.ioloop.IOLoop.current().remove_timeout(self.timeout)
self.timeout = None
if is_last_message:
self.write_message(message)
self.close()
else:
# Add a new timeout.
self.timeout = tornado.ioloop.IOLoop.current().add_timeout(
datetime.timedelta(milliseconds=1000), self._close_on_timeout)
self.write_message(message)
The test:
from tornado.websocket import websocket_connect
from tornado.testing import AsyncHTTPTestCase, gen_test
import time
class WSTests(AsyncHTTPTestCase):
#gen_test
def test_long_response(self):
ws = yield websocket_connect('ws://address', io_loop=self.io_loop)
# First round trip.
ws.write_message('First message.')
result = yield ws.read_message()
self.assertEqual(result, 'First response.')
# Wait a little more than the timeout.
yield gen.Task(self.io_loop.add_timeout, datetime.timedelta(seconds=1.1))
# Expect either write or read to fail because of a closed socket.
ws.write_message('Second message.')
result = yield ws.read_message()
self.assertEqual(result, None)
The timeout-handling code in your first example looks correct to me.
For testing, each test case gets its own IOLoop, but there is only one IOLoop for both the test and anything else it runs, so you must use add_timeout instead of time.sleep() here as well to avoid blocking the server.
Ey Ben, I know this question was long ago resolved, but I wanted to share with any user reading this the solution I made for this.
It's basically based on yours, but it solves the problem from an external Service that can be easily integrated within any websocket using composition instead of inheritance:
class TimeoutWebSocketService():
_default_timeout_delta_ms = 10 * 60 * 1000 # 10 min
def __init__(self, websocket, ioloop=None, timeout=None):
# Timeout
self.ioloop = ioloop or tornado.ioloop.IOLoop.current()
self.websocket = websocket
self._timeout = None
self._timeout_delta_ms = timeout or TimeoutWebSocketService._default_timeout_delta_ms
def _close_on_timeout(self):
self._timeout = None
if self.websocket.ws_connection:
self.websocket.close()
def refresh_timeout(self, timeout=None):
timeout = timeout or self._timeout_delta_ms
if timeout > 0:
# Clean last timeout, if one exists
self.clean_timeout()
# Add a new timeout (must be None from clean).
self._timeout = self.ioloop.add_timeout(
datetime.timedelta(milliseconds=timeout), self._close_on_timeout)
def clean_timeout(self):
if self._timeout is not None:
# Remove previous timeout, if one exists.
self.ioloop.remove_timeout(self._timeout)
self._timeout = None
In order to use the service, itÅ› as easy as create a new TimeoutWebService instance (optionally with the timeout in ms, as well as the ioloop where it should be executed) and call the method ''refresh_timeout'' to either set the timeout for the first time or reset an already existing timeout, or ''clean_timeout'' to stop the timeout service.
class BaseWebSocketHandler(WebSocketHandler):
def prepare(self):
self.timeout_service = TimeoutWebSocketService(timeout=(1000*60))
## Optionally starts the service here
self.timeout_service.refresh_timeout()
## rest of prepare method
def on_message(self):
self.timeout_service.refresh_timeout()
def on_close(self):
self.timeout_service.clean_timeout()
Thanks to this approach, you can control when exactly and under which conditions you want to restart the timeout which might differ from app to app. As an example you might only want to refresh the timeout if user acomplish X conditions, or if the message is the expected one.
I hope ppl enjoy this solution !
Short Question
Using my examples below, is there a Pythonic way to share my_object's actual instance with with the BaseRequestHandler class?
Background
By definition, the BaseRequestHandler class creates a new instance for each request. Because of this, I am struggling to try find a solution on how to get data from the handle() function back to the ProtocolInterface instance. Note that this might be the wrong approach if I am needing to do something in handle() other than print to stdout.
At this point in time, I do not believe that global variables will work because my_object is passed in and is expected to change often (this is why handle() needs to see it. To see an example client (sending bogus data) see my other SO question. I think the biggest issue I am facing is the the socketservers are running in a background thread.
Example of what I would like to do
class ProtocolHandler(SocketServer.BaseRequestHandler):
def handle(self):
while(1):
self.data = self.request.recv(1024)
if self.data == '':
break
self.request.send("Success")
self.my_object.success = True# <--- How can I share my_object's instance?
class ProtocolInterface():
def __init__(self, obj, host='127.0.0.1', port=8000, single_connection=False):
self.my_object = obj # <--- This ideally is the same instance seen in ProtocolHandler
self.host = host
self.port = port
# Create the socket server to process in coming traffic
if(single_connection):
self.server = SocketServer.TCPServer((self.host, self.port), ProtocolHandler)
else:
self.server = SocketServer.ThreadingTCPServer((self.host, self.port), ProtocolHandler)
def start(self):
print "Server Starting on HOST: " + self.host
server_thread = threading.Thread(target=self.server.serve_forever)
server_thread.daemon = True
server_thread.start()
You could pass the object through the server instance:
self.server = SocketServer.TCPServer((self.host, self.port), ProtocolHandler)
self.server.my_object = self.my_object
The documentation indicates that you can have access to the server instance in handle() as self.server.