How to send message from server to client using Flask-Socket IO - python

I'm trying to create a python app that can send message from server to client. Currently I'm using this sample code from here. It's a chat app and it's working fine. I tried to modified the app and add a new function in the server side python code that will print a message "Dummy" into the client but seems like it didn't work.
Here's my html code:
index.html
<body>
<ul id="messages"></ul>
<ul id="output"></ul>
<form action="">
<input id="m" autocomplete="off" /><button>Send</button>
</form>
<script src="{{url_for('static', filename='assets/vendor/socket.io.min.js')}}"></script>
<script src="{{url_for('static', filename='assets/vendor/jquery.js')}}"></script>
<script>
var socket = io.connect('http://127.0.0.1:5000/chat');
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').html($('<li>').text(msg));
});
socket.on('output', function(msg){
alert(msg)
$('#messages').html($('<li>').text(msg));
});
</script>
Here's my backend code:
web_app.py
from flask import Flask
from flask import render_template
from flask_socketio import SocketIO
from flask_socketio import emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
connected = False
def socket_onload(json):
socketio.emit('output', str(json), namespace='/chat')
print('received message: ' + str(json))
#socketio.on('chat message', namespace='/chat')
def handle_chat_message(json):
print('received message: ' + str(json))
emit('chat message', str(json), broadcast=True)
#socketio.on('connect') # global namespace
def handle_connect():
global connected
connected = True
print('Client connected')
#socketio.on('connect', namespace='/chat')
def handle_chat_connect():
print('Client connected to chat namespace')
emit('chat message', 'welcome!')
#socketio.on('disconnect', namespace='/chat')
def test_disconnect():
print('Client disconnected')
#app.route('/')
def index():
return render_template('index.html')
#app.route('/blah/')
def blah():
return render_template('blah.html')
main.py
import web_app
import threading
import time
def main():
import web_app
webapp_thread = threading.Thread(target=run_web_app)
webapp_thread.start()
# webapp_thread = threading.Thread(target=run_web_app, args=(i,))
while web_app.connected==False:
print "waiting for client to connect"
time.sleep(1)
pass
print "Connected..."
time.sleep(3)
print "Trying to print dummy message..."
web_app.socket_onload("Dummy")
def run_web_app():
web_app.socketio.run(web_app.app)
if __name__ == '__main__':
main()
I can see "received message: Dummy" in the terminal but nothing's change on the web browser.

You have two mistakes which prevent you from doing so:
First, you are trying to emit an event with socket.io outside from the socket context.
When a function is wraped with #socketio.on decorator, it becomes an Event-Handlers.
While an event is fired on the server-side it will search for the right handler to handle the event and initialize the context to the specific client that emitted the event.
Without this context initializing, your socketio.emit('output', str(json), namespace='/chat') will do nothing because the server doesn't know to whom it should emit back the response.
Anyway, there is a little trick for emitting events manually to a specific client (even if you are not in its context). Each time a socket has opened, the server assign it to a "private" room with the same name as the socket id (sid). So in order to send a message to the client outside from the client context, you can create a list of connected client's ids and call the emit function with the room=<id> argument.
For example:
web_app.py:
...
from flask import Flask, request
clients = []
#socketio.on('connect')
def handle_connect():
print('Client connected')
clients.append(request.sid)
#socketio.on('disconnect')
def handle_disconnect():
print('Client disconnected')
clients.remove(request.sid)
def send_message(client_id, data):
socketio.emit('output', data, room=client_id)
print('sending message "{}" to client "{}".'.format(data, client_id))
...
Then you would probably use this as follow:
main.py:
import web_app
import threading
import time
def main():
webapp_thread = threading.Thread(target=run_web_app)
webapp_thread.start()
while not web_app.clients:
print "waiting for client to connect"
time.sleep(1)
print "Connected..."
time.sleep(3)
print "Trying to print dummy message..."
web_app.send_message(web_app.clients[0], "Dummy")
...
But even if you try this, it will not work (which brings us to the second mistake).
Second, you are mixing eventlet with regular Python threads and it's not a good idea. the green threads that eventlet uses do not work well with regular threads. Instead, you should use green threads for all your threading needs.
One option which I found in the internet, is to monkey patch the Python standard library, so that threading, sockets, etc. are replaced with eventlet friendly versions. You can do this at the very top of your main.py script:
import eventlet
eventlet.monkey_patch()
After that it should work fine (I tried it on my own). Let me know if you have another problems...

These decorators are there for simply loading the pages index.html and blah.html these don't do anything --- don't pass any variable or don't write anything to the template they just render it, meanwhile all the things you are doing are there for the command line if you have to print or pass anything it would have to be in these functions :
#app.route('/')
def index():
return render_template('index.html')
#app.route('/blah/')
def blah():
return render_template('blah.html')

Related

How to run a coroutine and wait for its result from a sync func

I am trying to run a python socketIO client along side a server. Basically when the server gets a viewer event, the client fires another connection to an upstream server. It then tries to establish a WebRTC connection based on aiortc.
# sio client
sioClient = socketio.Client()
# sio server
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
sioServer = SocketIO(app, async_mode=async_mode)
#app.route("/")
def index():
print("Get request '/'")
return render_template('index.html')
#sioClient.on('connect')
def sio_connect():
sioClient.emit('viewer', {'foo': 'bar'})
print('proxy is connected to server, SID = ', sioClient.sid)
#sioClient.on('offer')
def sio_offer(broadcasterId,broadcasterDescription):
print("offer event")
peerConnection = RTCPeerConnection()
peerConnection.setRemoteDescription(broadcasterDescription)
print("remoteDescription is ", peerConnection.remoteDescription)
#sioServer.on('viewer')
def handle_viewer_event(arg1):
print('received args: ' + arg1["data"])
sioClient.connect('http://localhost:4000')
print('proxy sid is', sioClient.sid)
if __name__ == '__main__':
sioServer.run(app)
Currently the #sioClient.on('connect') works fine and successfully emits another 'viewer' event to the upstream server. However when an 'offer' event comes back, I get the below error:
RuntimeWarning: coroutine 'RTCPeerConnection.setRemoteDescription' was never awaited
peerConnection.setRemoteDescription(broadcasterDescription)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
remoteDescription is None
I sort of understand this is because 'RTCPeerConnection.setRemoteDescription' is an asynchronous coroutine, but I don't know how to wait for it synchronously.
I have tried to make sio_offer an asynchronous func like below:
#sioClient.on('offer')
async def sio_offer(broadcasterId,broadcasterDescription):
print("offer event")
peerConnection = RTCPeerConnection()
await peerConnection.setRemoteDescription(broadcasterDescription)
print("remoteDescription is ", peerConnection.remoteDescription)
But the whole block doesn't execute anymore upon an 'offer' event.
I've seen a few suggestions around asyncio eventloop. But I am a python beginner and got very confused...
It will be appreciated if anyone can shed a bit light! Thanks in advance!

Listening for RabbitMQ and running flask on same server

I have a Flask application as such
from flask import Flask
from flask_restful import Resource, Api
from mq_handler import MessageBroker
import pika
app = Flask(__name__)
api = Api(app)
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
mb = MessageBroker(connection)
class HelloWorld(Resource):
def get(self):
mb.run()
return {'hello': 'world'}
class LogHandler(Resource):
def get(self, table):
return {'TableName': table}
api.add_resource(HelloWorld, '/')
api.add_resource(LogHandler, '/log/<string:table>')
if __name__ == '__main__':
app.run(debug=True)
I have added a MessageBroker class to handle all my rabbitMq messages
import pika
import json
class MessageBroker:
def __init__(self, connection):
self.connection = connection
self.channel = connection.channel()
def run(self):
self.channel.start_consuming()
self.channel.basic_consume(queue='logs',
auto_ack=True,
on_message_callback=self.handle_log)
self.channel.start_consuming()
def handle_log(self, ch, method, properties, body):
decoded_content = body.decode('utf-8')
json_payload = json.loads(decoded_content)
print(" [x] Received %r" % json_payload['message'])
I have tried different solutions, but have can I get both services to run simultaneously on the same server? can somebody explain that please?
In general.. how is it possible to have several services running listening on my flask server?
I am not sure about running the consumer on an end-point will be a good idea. Because, when you start a consumer it runs an IO loop to fetch and process messages from the server continuously. The loop will not exit unless it is done externally or any exception in the message processing causing the connection to close. Can you please state your scenario for running the consumer in the end-point?

Chrome Native Messaging - Execute the host manually

i'm trying to create a web extension using native messaging that can interact with a Web API. So the idea is when someone sends a message through the API, the extension also receive it.
I follow the steps of the firefox example. So far so good, i was able to communicate between the host and the extension.
Here is my host code, using the flask framework:
import json
import sys
import struct
import threading
import os
from flask import Flask, request, jsonify, Response, abort
# Read a message from stdin and decode it.
def get_message():
raw_length = sys.stdin.read(4)
if not raw_length:
sys.exit(0)
message_length = struct.unpack('=I', raw_length)[0]
message = sys.stdin.read(message_length)
return json.loads(message)
# Encode a message for transmission, given its content.
def encode_message(message_content):
encoded_content = json.dumps(message_content)
encoded_length = struct.pack('=I', len(encoded_content))
return {'length': encoded_length, 'content': encoded_content}
# Send an encoded message to stdout.
def send_message(encoded_message):
sys.stdout.write(encoded_message['length'])
sys.stdout.write(encoded_message['content'])
sys.stdout.flush()
def get_message_background():
while True:
message = get_message()
if message == "ping":
send_message(encode_message("pong"))
thread = threading.Thread(target=get_message_background)
thread.daemon = True
thread.start()
app = Flask(__name__)
#app.route("/test", methods=['GET'])
def test():
send_message(encode_message('testing'))
return 'testing'
app.run('localhost', 5001, debug=True)
With this code i receive a ping and respond a pong.
The problem is when i try to run that code on the terminal.
The native messaging communicates over stdio and when i'm run my script on the terminal, the terminal became the standard input and output.
If i just load the extension, the flask not start.
Someone has solved this problem?
PS: Sorry for my english

Sharing a variable between different classes in Tornado

I am trying to write a Tornado TCP + HTTP Server application.
My use case is a Tornado TCP + HTTP Server application which accepts data from a TCP client and pass the data to display it on a webpage hosted on the HTTP server.
Here is my tornado server code:
#!/usr/bin/env python
import os.path
import tornado.httpserver
import tornado.web
import logging
from tornado.ioloop import IOLoop
from tornado import gen
from tornado.iostream import StreamClosedError
from tornado.tcpserver import TCPServer
from tornado.options import options, define
define("port", default=6642, help="TCP port to listen on")
logger = logging.getLogger(__name__)
test = {}
class IndexHandler(tornado.web.RequestHandler):
def get(self):
global test
self.render('index.html', test=test)
class EchoServer(TCPServer):
#gen.coroutine
def handle_stream(self, stream, address):
global test
while True:
try:
test = yield stream.read_until("\n")
logger.info("Received bytes: %s", test)
except StreamClosedError:
logger.warning("Lost client at host %s", address[0])
break
except Exception as e:
print(e)
if __name__ == "__main__":
options.parse_command_line()
app = tornado.web.Application( handlers=[
(r'/', IndexHandler)],
static_path=os.path.join(os.path.dirname(__file__), "static"),
template_path=os.path.join(os.path.dirname(__file__), "templates"))
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
server = EchoServer()
server.listen(6641)
logger.info("Listening on TCP port %d",6641)
IOLoop.current().start()
Here is the python client code :
# echo_client.py
import socket
import time
counter = 0
host = '192.168.43.59'
port = 6641 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
while True:
s.sendall("s\n")
counter = counter + 1
time.sleep(5)
I want to pass the data received from TCP client application into the variable "test" to the render template to display in index.html webpage but I am getting no data displayed.
I am using the global variable concept but no success since couldn't pass the updated "test" variable to index.html page.
If anyone could throw light on using common vaiables across different classes or handlers would help me.
Javascript file iam using is this :
/* test.js */
var test = ""
function set_test(val)
{
test=val
}
function show_test()
{
alert(test);
}
The HTML template used is this :
<!DOCTYPE html>
<html>
<meta http-equiv="refresh" content="30" />
<head>
<title>Test</title>
<script src="{{ static_url('scripts/test.js') }}"
type="application/javascript"></script>
</head>
<body>
<input type="button" onclick="show_test()" value="alert" />
<script type="application/javascript">
set_test("{{test}}");
</script>
</body>
</html>
Hi xyres,
Thank you for your spontaneous reply.I went through the link provided by you and after going through it i could understand that q.get() and q.put() can be used to store and retrieve data as you said.But i could not after modifying the tornado server code in the following manner I couldn't receive the data from TCP client , before this i could at least get data from TCP client .Can you let me know what is the mistake i have done in queue implementation
Here is my tornado server code:
#!/usr/bin/env python
import os.path
import tornado.httpserver
import tornado.web
import logging
from tornado.ioloop import IOLoop
from tornado import gen
from tornado.iostream import StreamClosedError
from tornado.tcpserver import TCPServer
from tornado.options import options, define
define("port", default=6642, help="TCP port to listen on")
logger = logging.getLogger(__name__)
#test = {}
q = Queue(maxsize=2)
class IndexHandler(tornado.web.RequestHandler):
def get(self):
#global test
test = yield q.get
self.render('index.html', test=test)
class EchoServer(TCPServer):
#gen.coroutine
def handle_stream(self, stream, address):
#global test
yield q.put(test)
yield q.join()
while True:
try:
test = yield stream.read_until("\n")
logger.info("Received bytes: %s", test)
except StreamClosedError:
logger.warning("Lost client at host %s", address[0])
break
except Exception as e:
print(e)
if __name__ == "__main__":
options.parse_command_line()
app = tornado.web.Application( handlers=[
(r'/', IndexHandler)],
static_path=os.path.join(os.path.dirname(__file__), "static"),
template_path=os.path.join(os.path.dirname(__file__), "templates"))
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
server = EchoServer()
server.listen(6641)
logger.info("Listening on TCP port %d",6641)
IOLoop.current().start()
As per the tornado documentation it seems that queue can be applied to coroutines and here iam trying to replicate the same to two different classes.Is that a mistake ..Iam new to tornado so please bear my silly questions ..
You've a multiple options:
If you want to have a long-running connection, for example, if a client sends a request to IndexHandler and you want the client to wait until a message is in the queue, you can convert your handler to a coroutine.
If you want to return the response immediately, regardless of the availability of the data in the queue, you can use a queue's get_nowait() method.
Example for case #1:
from tornado.queues import Queue
q = Queue()
class IndexHandler(tornado.web.RequestHandler):
#gen.coroutine
def get(self):
self.data_future = q.get()
data = yield self.data_future
self.render('index.html', data=data)
def on_connection_close(self):
# set an empty result on the future
# if connection is closed so that
# the messages don't get removed from
# the queue unnecessariliy for
# closed connections
self.msg_future.set_result(None)
Example for case #2:
from tornado.queues import Queue, QueueEmpty
q = Queue()
def get(self):
try:
data = q.get_nowait()
except QueueEmpty:
data = None
self.render(...)

gevent-socketio send message from thread

I would like to use gevent-socketio to send messages from a worker thread and update all connected clients on the status of the job.
I tried this:
from flask import Flask, render_template
from flask.ext.socketio import SocketIO, send, emit
import threading
import time
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#socketio.on('message')
def handle_message(message):
send(message, broadcast=True)
#app.route('/')
def index():
return render_template('index.html')
def ping_thread():
while True:
time.sleep(1)
print 'Pinging'
send('ping')
if __name__ == '__main__':
t = threading.Thread(target=ping_thread)
t.daemon = True
t.start()
socketio.run(app)
And it gives me this error:
RuntimeError: working outside of request context
How do I send messages from a function that doesn't have the #socketio.on() decorator? Can I use gevent directly to send messages to socketio?
From this section of the documentation:
Sometimes the server needs to be the originator of a message. This can be useful to send a notification to clients of an event that originated in the server. The socketio.send() and socketio.emit() methods can be used to broadcast to all connected clients:
def some_function():
socketio.emit('some event', {'data': 42})
This emit is not from from flask.ext.socketio import SocketIO, send, but instead called on your socketio variable from socketio = SocketIO(app). Had you done socketio_connection = SocketIO(app), then you'd be calling socketio_connection.emit() to broadcast your data.

Categories

Resources