I'm trying to get some code to work but can't seem to get it right, The intention is that all the clients can see when a button is pushed.
At the moment I can get the client that presses the button to see the message but no other.
Py:
pushedDict = {}
#app.route('/buttons/')
def index():
return flask.render_template('index.html', port=port)
def wsgi_app(environ, start_response):
path = environ["PATH_INFO"]
if path == "/buttons/":
return app(environ, start_response)
elif path == "/websocket/":
handle_websocket(environ["wsgi.websocket"])
else:
return app(environ, start_response)
def handle_websocket(ws):
while True:
pushRecieve = ws.receive() # Receive pushed Buttons
gap = "Button" # Placeholder for later
pushedDict.update({gap:pushRecieve}) # Add to Dictionary
pushSend = json.loads(pushedDict[gap]) # Get from Dictionary
ws.send(json.dumps({'output': pushSend['output']})) # Send
pushedDict.update({gap:""}) # Clear Dictionary
JS Receive:
$(document).ready(function(){
$(function() {
if ("WebSocket" in window) {
ws = new WebSocket("ws://" + document.domain + ":{{port}}/websocket/");
ws.onmessage = function (msg) {
var getButtons = JSON.parse(msg.data);
$("p#log").html(getButtons.output );
};
};
});
JS Send:
var buttonQueue = [];
$("a.button1").mousedown(function(e){
e.preventDefault();
buttonQueue.push("button1")
ws.send(JSON.stringify({'output': buttonQueue}));
});
$("a.button1").mouseup(function(e){
e.preventDefault();
remove(buttonQueue, "button1");
ws.send(JSON.stringify({'output': buttonQueue}));
});
$("a.button2").mousedown(function(e){
e.preventDefault();
buttonQueue.push("button2")
ws.send(JSON.stringify({'output': buttonQueue}));
});
$("a.button2").mouseup(function(e){
e.preventDefault();
remove(buttonQueue, "button2");
ws.send(JSON.stringify({'output': buttonQueue}));
});
});
Appreciate a fresh point of view.
I'm no expert on WebSockets, but my impression is that the ws protocol only establishes an ongoing connection between client and server, allowing data to be sent from the server without constant requests from the client. Your Flask app doesn't know about any other clients connected; it only uses handle_websocket(ws) to talk to one client at a time. You have to tell your Flask app which clients are currently connected, then ws.send() the button press updates to all of them. I don't have any experience with this, but it looks like the most popular way to track ws-connected clients and send them updates is redis. I also found an example chat application that you could adapt for your needs. Hope this helps!
Related
Hi i am copying parts of the github project multichat from the creator of django channels.
I am making slight changes to the code like not using jquery, renaming of some consumers and such.
I have literally no errors when running the code however when i join the page and the JS creates a websocket it says simply
[2017/08/03 13:13:48] WebSocket HANDSHAKING /chat/stream [127.0.0.1:37070]
[2017/08/03 13:13:48] WebSocket CONNECT /chat/stream [127.0.0.1:37070]
Which one would think is fine ofcourse... However i'n my connect function i have a print("********CONNECTED**********"), wich is nowhere to be seen in the console. It simply doesn't run the function i have told it to when someone connects but it still says the person connected and it throws no errors.
This is the main routing:
channel_routing = [
include("crypto_chat.routing.websocket_routing", path=r"^/chat-stream/$"),
include("crypto_chat.routing.chat_routing"),
]
Routing from app:
websocket_routing = [
route("websocket.connect", ws_connect),
route("websocket.receive", ws_receive),
route("websocket.disconnect", ws_disconnect),
]
chat_routing = [
route("chat.receive", chat_send, command="^send$"),
route("chat.receive", user_online, command="^online$"),
Connect Consumer:
#channel_session_user_from_http
def ws_connect(message):
# only accept connection if you have any rooms to join
print("******************CONNECT*************************''")
message.reply_channel.send({"accept": True})
# init rooms - add user to the groups and pk num to the session
message.channel_session['rooms'] = []
for room in Room.objects.get(users=message.user):
room.websocket_group.add(message.reply_channel)
message.channel_session['rooms'].append(room.pk)
print(message.channel_session['rooms'])
Heres JS (note: i am using the JS extension that is available on the project website also):
function send_msg(){
var msg=document.getElementById('msg_input').value;
console.log("sending msg" + msg);
webSocketBridge.send({
"command": "send",
"room": "1",
"message": msg
});
}
// logging
var ws_path = "/chat/stream";
console.log("connecting to " + ws_path);
// connect
var webSocketBridge = new channels.WebSocketBridge();
webSocketBridge.connect(ws_path);
// listen loop
webSocketBridge.listen(function(data)
{
// read json file and act accordingly
if(data.error){
// post error message in chat
console.log("Error - " + data.error);
return;
}
// handle if the user comes back online
if(data.online){
console.log("User is online");
}
else if(data.offline){
console.log("User offline");
}
else if(data.message){
console.log("Got message");
}
else{ console.log("Unknown message type"); }
});
// Helpful debugging
webSocketBridge.socket.onopen = function () {
console.log("Connected to chat socket");
};
webSocketBridge.socket.onclose = function () {
console.log("Disconnected from chat socket");
}
Websocket paths should match on server and client side. On server side, you have /chat-stream/ and on client side /chat/stream. These should match. Also, make sure you don't forget the trailing slash as django explicitly requires it.
I trying to send sensor data (in python) from my raspberry pi3 to my local node server.
I found a module for python called requests to send data to a server.
Here I'm trying send the value 22 (later there will be sensor data) from my raspberry pi3 to my local node server with socket.io.The requests.get() works but the put commmand doesn't send the data.
Can you tell me where the mistake is ?
#!/usr/bin/env python
#
import requests
r = requests.get('http://XXX.XXX.XXX.XXX:8080');
print(r)
r = requests.put('http://XXX.XXX.XXX.XXX:8080', data = {'rasp_param':'22'});
In my server.js I try to get the data but somehow nothing getting received
server.js
var express = require('express')
, app = express()
, server = require('http').createServer(app)
, io = require('socket.io').listen(server)
, conf = require('./config.json');
// Webserver
server.listen(conf.port);
app.configure(function(){
app.use(express.static(__dirname + '/public'));
});
app.get('/', function (req, res) {
res.sendfile(__dirname + '/public/index.html');
});
// Websocket
io.sockets.on('connection', function (socket) {
//Here I want get the data
io.sockets.on('rasp_param', function (data){
console.log(data);
});
});
});
// Server Details
console.log('Ther server runs on http://127.0.0.1:' + conf.port + '/');
you are using HTTP PUT from Python, but you are listening with a websocket server on nodejs side.
Either have node listening for HTTP POST (I'd use POST rather than PUT):
app.post('/data', function (req, res) {
//do stuff with the data here
});
Or have a websocket client on python's side :
ws = yield from websockets.connect("ws://10.1.10.10")
ws.send(json.dumps({'param':'value'}))
A persistant websocket connection is probably the best choice.
I'm getting started with flask and SocketIO using https://github.com/miguelgrinberg/Flask-SocketIO.
I want to post a string to the flask server and then via SocketIO, emit this to the client webpage.
Normally my posting code would look like:
#app.route('/index',methods=['POST'])
def index():
token = request.form['token']
As far as I understand, something like the following is needed to emit data from the server to client page:
#socketio.on('event', namespace='/test')
def test_message(message):
emit('my response', {'data': message['data']}, broadcast=False)
It's not clear to me how to tie the 2 functions together so that on a post the value of token would be emitted to the client.
The closest I can find in the docs is:
Receiving Messages¶
When using SocketIO messages are received by both parties as events. On the client side Javascript callbacks are used. With Flask-SocketIO the server needs to register handlers for these events, similarly to how routes are handled by view functions.
How can I get this working?
You're right with your assumptions. First, POST the data to Flask:
.ajax({
url: "{{ url_for('index') }}",
method: "POST",
data: {
token: "value"
}
});
Your view would look like
#app.route('/index',methods=['POST'])
def index():
token = request.form['token']
test_message(dict(data=token))
return '1'
And your JavaScript would look something like
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
socket.on('my response', function(msg) {
// do something with msg.data
});
I'm trying to use django-websocket-redis and I didn't understand how it works even reading the doc..
The part client (javascript/template) was easy to understand but I want to send data messages from one client to other and i'm blocking here..
Connecting each client :
var ws = new WebSocket('ws://localhost:8000/ws/foobar?subscribe-group');
ws.onopen = function(e) {
console.log("websocket connected");
};
ws.onclose = function(e) {
console.log("connection closed");
};
How manage my views.py to create a link between them ?
With NodeJS I was using this code to link the clients together :
io.sockets.on('connection', function (socket) {
var data={"action": "connexion", "session_id": socket.id,};
socket.emit('message',data);
socket.on('message', function(socket){
if (socket.action == "test")
{
io.sockets.socket(socket.code).emit('message',{"action": "move"});
//the socket.code is the session_id of the client one transmitted by a form
}
});
});
Thanks you.
The link between your Django view.py and the Websocket loop is the Redis message queue. Imagine to have two separate main loops on the server: One which handles HTTP-requests using the normal Django request handler. The other loop handles the Websockets, with their long living connections. Since you can't mix both loops within the normal Django request handler, you need message queuing, so that they can communicate to each other.
Therefore, in your Django view.py, send the data to the websocket using something like:
def __init__(self):
self.redis_publisher = RedisPublisher(facility='foo', broadcast=True)
def get(self, request):
data_for_websocket = json.dumps({'some': 'data'})
self.redis_publisher.publish_message(RedisMessage(data_for_websocket))
This will publish data_for_websocket on all Websockets subscribed (=listening) using the URL:
ws://example.com/ws/foo?subscribe-broadcast
I have written a Node.js socket.io routine which will be called by a python socket io routine from my raspberry pi. It will communicate both ways. At the moment when I run these two routines on localhost it works fine. However when I deploy the server application to cloudfoundry and change the SocketIO connection link to cloudfoundry it does not work. Below is the client python
from socketIO_client import SocketIO
def on_updatepi_response(*args):
print 'updatepi'
def on_receivepi_response(*args):
print 'receiveepi'
with SocketIO('raspinode-server.cloudfoundry.com', 8080) as socketIO:
socketIO.on('receivepi', on_receivepi_response)
socketIO.on('updatepi', on_updatepi_response)
socketIO.emit('sendrabbit','testdata')
socketIO.wait(seconds=1)
I know cloudfoundry can be a bit strange as my first idea was to use rabbitmq but it is tied to the VCAP_SERVICES idea. However I did not think such a restriction would be there on a Node.js page.
Let me know if there is anything wrong with the above code and if not how can i get my external pi to send reading to my cloud app ?
Server Code is listed below though it is not relevant. It responds on localhost...I know the rabbitmq code is not hooked up yet
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var amqp = require('amqp');
var io = require('socket.io').listen(server)
function rabbitUrl() {
if (process.env.VCAP_SERVICES) {
conf = JSON.parse(process.env.VCAP_SERVICES);
return conf['rabbitmq-2.4'][0].credentials.url;
}
else {
return "amqp://localhost";
}
}
var port = process.env.VCAP_APP_PORT || 3000;
var messages = [];
function setup() {
var exchange = conn.exchange('cf-demo', {'type':'fanout', durable:false}, function(){
var queue = conn.queue('', {durable:false, exclusive:true},
function() {
queue.subscribe(function(msg) {
messages.push(htmlEscape(msg.body));
if (messages.length > 10) {
messages.shift();
}
});
queue.bind(exchange.name, '');
});
queue.on('queueBindOk', function() {httpServer(exchange);});
});
}
server.listen(8080);
io.sockets.on('connection', function(socket){
// when the client emits sendrabbit, this listens
socket.on('sendrabbit', function(data)
{
// we tell the client to execute updatepi with 2 parameters
io.sockets.emit('updatepi', socket.username, data)
});
socket.on('disconnect', function()
{
socket.broadcast.emit('updatepi', 'SERVER', socket.username + ' has disconnected');
});
});
It's my understanding that your server should listen on the port Cloud Foundry assigns it (available in an env var). You can't assume it will be 8080. Then the client talks to raspinode-server.cloudfoundry.com (no port) and Cloud Foundry routes it to the correct place.