Using Tornado To Send Intermittent Updates to Client - python

I've written a simple tornado application using web-sockets to manage daemons (just like transmission web-interface). One thing that bothers me is the implementation of status update.
My WebSocketHandler just recieves messages from client, performs neccessary actions, checks the status and sends it to client. It does not send status to client without request.
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print('connected')
self.daemon = Daemon()
self.write_message(self.daemon.get_status())
def on_message(self, message):
if message == 'start':
self.daemon.start()
elif message == 'stop':
self.daemon.stop()
self.write_message(self.daemon.get_status())
def on_close(self):
print('disconnected')
I'm using setInterval function of javascript on client side to request for status update, like this:
ws = new WebSocket("ws://localhost:8080/websocket");
ws.onopen = function () {
setInterval(function() {
if (ws.bufferedAmount == 0)
ws.send('status');
}, 1000);
};
}
How can the same result be achieved on server side, so that tornado sends current status to client without blocking on_message method?

You can use tornado.ioloop.IOLoop.add_timeout to call a method every X number of seconds to update the client:
from tornado.ioloop import IOLoop
from datetime import timedelta
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print('connected')
self.daemon = Daemon()
self.write_message(self.daemon.get_status())
self.schedule_update()
def schedule_update(self):
self.timeout = IOLoop.instance().add_timeout(timedelta(seconds=5),
self.update_client)
def update_client(self):
try:
self.write_message(self.daemon.get_status())
finally:
self.schedule_update()
def on_close(self):
IOLoop.instance().remove_timeout(self.timeout)
print('disconnected')

Related

Is it possible to call a method to send messages while a WebsocketApp is running from the same class?

I am working on a websocket application that utilizes WebSocketApp from websocket-client. As my websocket is running, and listening for messages I would like to be able to call a method to send a message to the server as needed.
WebSocket
import websocket
class WebSocketTest():
wsapp = None
print("starting websocket app")
ws_is_open = False
def on_message(self, wsapp, message):
print("ON_MESSAGE")
print(message)
def on_open(self, wsapp):
print("SOCKET OPENING")
self.ws_is_open = True
def on_close(self):
print("SOCKET CLOSING")
async def start(self):
self.wsapp = websocket.WebSocketApp("ws://localhost:5001",
on_message=self.on_message,
on_close=self.on_close,
on_open=self.on_open)
self.wsapp.run_forever()
def send(self, msg):
if self.ws_is_open:
print(f"SENDING: {msg}")
self.wsapp.send(msg)
Test Setup File
from websocketapp import WebSocketTest
import asyncio
ws = WebSocketTest()
asyncio.run(ws.start())
ws.send("Hello!")
Output
some_user python % python test.py
starting websocket app
SOCKET OPENING
I have created a setup file that initializes the class, starts the server and then attempts to send a message using the send method. It looks like it never gets to the line that calls send. The connection to the server works, and if I broadcast a message from my websocket server to the client on_message is triggered and I see the message printed out.

How to handle a bidirectional grpc stream asynchronously

I have a game or for that matter any remote user interface with a server and multiple clients which should communicate via network.
Both client and server should be able to send updates asynchronously.
This seems to be a very natural service definition, which let's grpc manage sessions.
syntax = "proto3";
package mygame;
service Game {
rpc participate(stream ClientRequest) returns (ServerResponse);
}
message ClientRequest {
// Fields for the initial request and further updates
}
message ServerResponse {
// Game updates
}
Implementing the client is trivial (although the following code is obviously incomplete and simplified).
class Client:
def __init__(self):
self.channel = grpc.insecure_channel("localhost:50051")
self.stub = game_pb2_grpc.GameStub(channel)
self.output_queue = queue.Queue()
def output_iter(self):
while True:
client_output_msg = self.output_queue.get()
self.output_queue.task_done()
yield client_output_msg
def do_work(self):
for response in self.stub.participate(self.output_iter()):
print(response) # handle update
with grpc.insecure_channel("localhost:50051") as channel:
client = Client()
client.do_work()
What seems hard is implementing the server without blocking.
class Game(game_pb2_grpc.GameServicer):
def __init__(self):
self.pending_events = queue.Queue()
def participate(self, request_iter, context):
for client_update in request_iter:
print(client_update)
# !!!
# The next bit won't happen if the client has no updates
# !!!
try:
while True:
server_update = self.pending_events.get_nowait()
yield server_update
except queue.Empty:
pass
server = grpc.server(ThreadPoolExecutor(max_workers=100))
game_pb2_grpc.add_GameServicer_to_server(Game(), server)
server.add_insecure_port("[::]:50051")
server.start()
server.wait_for_termination()
As commented in the code, the client won't receive updates if it doesn't constantly send requests.
Maybe a async approach would be better, which might also solve other problems in this design.
PS: This issue has been solved with grpc in go here, however i don't see how to translate this to pythons grpc implementations.
I would be very happy about any help!
I was finally able to get it working using the python asynio api.
The basic idea is to decouple read and write into two coroutines using asyncio.create_task.
For anybody interested, here is a solution.
class Game(game_pb2_grpc.GameServicer):
async def read_client_requests(self, request_iter):
async for client_update in request_iter:
print("Recieved message from client:", client_update, end="")
async def write_server_responses(self, context):
for i in range(15):
await context.write(game_pb2.ServerResponse(dummy_value=str(i)))
await asyncio.sleep(0.5)
async def participate(self, request_iter, context):
read_task = asyncio.create_task(self.read_client_requests(request_iter))
write_task = asyncio.create_task(self.write_server_responses(context))
await read_task
await write_task
async def serve():
server = grpc.aio.server()
game_pb2_grpc.add_GameServicer_to_server(Game(), server)
server.add_insecure_port("[::]:50051")
await server.start()
await server.wait_for_termination()
if __name__ == "__main__":
asyncio.run(serve())
Note that instead of the write coroutine, a yield would also be sufficient.

mqtt client does not receive message in case of thread and rest-api

I have a python script, which based on flask and mqtt. The use case is to receive a request via rest-api then to create a new thread which publishes some messages on mosquitto mqtt and expects a response (see subscribe). My problem is that I don't receive any messages. I think it has something to do with the thread, because without the thread it's working very fine..
Do you know what can be the problem?
Thank you in anticipation!
here the code:
from flask import Flask, Response
import paho.mqtt.client as mqtt
from threading import Thread
import threading
app = Flask(__name__)
lock = threading.Lock()
def on_connect(client, userdata, flags, rc): # The callback for when the client connects to the broker
print("Connected with result code {0}".format(str(rc))) # Print result of connection attempt
client.subscribe("/mytopic")
def on_message(client, userdata, msg): # The callback for when a PUBLISH message is received from the server.
print(msg.topic)
client = mqtt.Client(client_id=client_name, clean_session=True)
client.on_connect = on_connect # Define callback function for successful connection
client.on_message = on_message # Define callback function for receipt of a message
client.username_pw_set(mqtt_user, mqtt_password)
client.loop_start()
client.connect(mqtt_host)
def test(param1, param2):
lock.acquire()
try:
ret = client.publish("/mytopic", "")
while True:
check the response from mqtt => but i don't get the response anymore
....
break
finally:
lock.release()
return result
#app.route('/test/check', methods=['POST'])
def check():
global sessionId
sessionId = sessionId + 1
t = Thread(target=test, args=(sessionId,None))
t.start()
return {'id': sessionId, 'eta': 0}
if __name__ == '__main__':
app.run(debug=True)
There are a couple of problems with this.
Both the client.connect() and the client.subscribe() calls need iterations of the client network loop to run in order to complete properly.
The network loop needs to run at least once every keep alive period the time after the connection has been made in order to stop the broker disconnecting the client as dead. This means if there is a delay between starting the code and the first REST request then the client will be disconnected.
Better to use the client.start_loop() function to run MQTT client network loop continuously in the background on it's own.
You should also remove the call to client.subscribe() that is outside the on_connect() callback.
EDIT:
As hashed out in the comments/chat the following works. It looks like running the flask app in debug mode does some strange things and creates multiple MQTT clients over and over again with the same client id. This leads to the broker constantly kicking the old ones off so messages never get delivered.
from flask import Flask, Response
import paho.mqtt.client as mqtt
import time
from threading import Thread
import threading
app = Flask(__name__)
lock = threading.Lock()
sessionId=0
cont=True
def on_connect(client, userdata, flags, rc): # The callback for when the client connects to the broker
print("Connected with result code {0}".format(str(rc))) # Print result of connection attempt
client.subscribe("mytopic")
def on_message(client, userdata, msg): # The callback for when a PUBLISH message is received from the server.
global cont
print(msg.topic)
cont=False
client = mqtt.Client(client_id="foo", clean_session=True)
client.on_connect = on_connect # Define callback function for successful connection
client.on_message = on_message # Define callback function for receipt of a message
#client.username_pw_set(mqtt_user, mqtt_password)
client.connect("localhost", port=1884)
client.loop_start()
def test(param1, param2):
lock.acquire()
try:
ret = client.publish("mytopic", "foo")
while cont:
time.sleep(5)
print("loop")
finally:
lock.release()
result = "foo"
return result
#app.route('/test/check', methods=['POST'])
def check():
global sessionId
sessionId = sessionId + 1
t = Thread(target=test, args=(sessionId,None))
t.start()
return {'id': sessionId, 'eta': 0}
if __name__ == '__main__':
print("started")
app.run()

multi tornado websocket block one another

all.
In my project,I built two websocket connections. one is used for sending pictures from server to client, and the other is used for sending some control data from client to server. now I use only one client.
var ws2 = new WebSocket('ws://xx.xx.xx.xx:9002/control')
var ws1 = new WebSocket('ws://xx.xx.xx.xx:9002/data')
ws.binaryType = 'blob'
ws2.onopen =()=> console.log('ws2 connected.')
On the server side, once ws1 is open, it constently send data to the client. q is a global queue from which I get the data. This part works fine.
The problem is that the program can not run into on_message(self, message) in function in ws2 after I send message from the client side.
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render("index.html")
class myWebSocket(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True;
def open(self):
print "websocket1 open."
while self.ws_connection is not None:
print "send..."
try:
self.write_message(q.get(),True)
except tornado.websocket.WebSocketClosedError:
self.close()
def on_message(self, message):
print("websocket1 received message.")
print message
def on_close(self):
print("websocket1 closed.")
class controlWebSocket(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
return True;
def open(self):
print "websocket2 open"
def on_message(self, message):
print("websocket2 received message.")
print message
def on_close(self):
print("websocket2 closed.")
and I am sure the client successfully send the control data by click a button.
<script>
function test(){
ws2.send("this is from ws2.")
alert("home clicked.")
}
</script>
and I also found that once the open function in ws1 stopped, the ws2 can receive the message. I don't understand why ws1 sending data caused ws2 unable to receive data.
I'm new in python programming and tornado websocket. anyone can help on this problem, thanks a lot~!
Tornado is built on non-blocking concurrency. This means you must generally avoid blocking methods (or run them in a thread pool). q.get() is a blocking method, so nothing else in Tornado can run while it is waiting for a message. You should probably replace this queue with a tornado.queues.Queue.

Unhandled error in Deferred? Using UNIX socket with Twisted

I'm completely new to network programming and event driven events. However I was able to successfully implement a pub-sub scheme using a TCP connection between my machine (server) and client machines for testing (command line). However, I need to actually use a UNIX socket with Twisted.
I am getting the following error when running the code:
Unhandled error in Deferred
Here is my code for pub_sub.py:
"""
a networking implementation of PubSub using Twisted.
=============
PubSub Server
=============
A PubSub server listens for subscription requests and publish commands, and, when
published to, sends data to subscribers. All incoming and outgoing requests are
encoded in JSON.
A Subscribe request looks like this:
{
"command": "subscribe",
"topic": "hello"
}
A Publish request looks like this:
{
"command": "publish",
"topic": "hello",
"data": {
"world": "WORLD"
}
}
When the server receives a Publish request, it will send the 'data' object to all
subscribers of 'topic'.
"""
import argparse
import json
import logging
from collections import defaultdict
from twisted.internet import reactor
from twisted.python import log
from twisted.python.filepath import FilePath
from twisted.internet.endpoints import UNIXClientEndpoint, UNIXServerEndpoint, \
connectProtocol
from twisted.internet.protocol import Protocol, Factory
class PubSubProtocol(Protocol):
def __init__(self, topics):
self.topics = topics
self.subscribed_topic = None
def connectionLost(self, reason):
print("Connection lost: {}".format(reason))
if self.subscribed_topic:
self.topics[self.subscribed_topic].remove(self)
def dataReceived(self, data):
print("Data received: {}".format(data))
try:
request = json.loads(data)
except ValueError:
logging.debug("ValueError on deconding incoming data. "
"Data: {}".format(data), exc_info=True)
self.transport.loseConnection()
return
if request['command'] == 'subscribe':
self.handle_subscribe(request['topic'])
elif request['command'] == 'publish':
self.handle_publish(request['topic'], request['data'])
def handle_subscribe(self, topic):
print("Subscribed to topic: {}".format(topic))
self.topics[topic].add(self)
self.subscribed_topic = topic
def handle_publish(self, topic, data):
request = json.dumps(data)
for protocol in self.topics[topic]:
protocol.transport.write(request)
print("Publish sent for topic: {}".format(topic))
class PubSubFactory(Factory):
def __init__(self):
self.topics = defaultdict(set)
def buildProtocol(self, addr):
return PubSubProtocol(self.topics)
class PublisherProtocol(Protocol):
"""
Publish protocol for sending data to client, i.e. front-end web GUI.
"""
def __init__(self, topic, **kwargs):
self.topic = topic
self.kwargs = kwargs
def connectionMade(self):
request = json.dumps({
'command': 'publish',
'topic': self.topic,
'data': self.kwargs,
})
self.transport.write(request)
self.transport.loseConnection()
class SubscriberProtocol(Protocol):
"""
Subscriber protocol for client sending a request to subscribe to a specific
topic.
"""
def __init__(self, topic, callback):
self.topic = topic
self.callback = callback
def connectionMade(self):
request = json.dumps({
'command': 'subscribe',
'topic': self.topic,
})
self.transport.write(request)
def dataReceived(self, data):
kwargs = json.loads(data)
self.callback(**kwargs)
class PubSub(object):
def __init__(self, path='./.sock'):
self.path = FilePath(path)
self.reactor = reactor
def _make_connection(self, protocol):
endpoint = UNIXClientEndpoint(reactor, self.path)
connection = connectProtocol(endpoint, protocol)
def subscribe(self, topic, callback):
"""
Subscribe 'callback' callable to 'topic'.
"""
sub = SubscriberProtocol(topic, callback)
self._make_connection(sub)
def publish(self, topic, **kwargs):
"""
Publish 'kwargs' to 'topic', calling all callables subscribed to 'topic'
with the arguments specified in '**kwargs'.
"""
pub = PublisherProtocol(topic, **kwargs)
self._make_connection(pub)
def run(self):
"""
Convenience method to start the Twisted event loop.
"""
self.reactor.run()
def main():
path = FilePath("./.sock")
endpoint = UNIXServerEndpoint(reactor, path)
endpoint.listen(PubSubFactory())
reactor.run()
if __name__ == '__main__':
main()
Any help would be greatly appreciated on what I am doing wrong.
Thank you,
Brian
You appear to be running your software on Windows. Alas, UNIX sockets are not available on Windows. If you want to use UNIX sockets, you need to use a more POSIX-ish environment - Linux, *BSD, OS X, etc.

Categories