Using websocket to connect Unity(client) and Python(server) - python

I have made a program which allow me to send a message each time I pressed a space in a unity, and send it to python server using cherrpy.
The problem is though my 'websocket.py' successfully serving on localhost as followed, once i started unity player it just Close instantly with the error.Hope some one help!!
websocket.py
import cherrypy
from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool
from ws4py.websocket import WebSocket
class Root(object):
#cherrypy.expose
def index(self):
return 'some HTML with a websocket javascript connection'
#cherrypy.expose
def ws(self):
# you can access the class instance through
handler = cherrypy.request.ws_handler
class AgentServer(WebSocket):
def opened(self):
print("Opened!")
def closed(self, code, reason):
print("Closed!")
def received_message(self,m):
self.send(m.data,m.is_binary)
if __name__ == "__main__":
cherrypy.config.update({
"server.socket_host": "127.0.0.1",
"server.socket_port": 3000,
})
WebSocketPlugin(cherrypy.engine).subscribe()
cherrypy.tools.websocket = WebSocketTool()
cherrypy.quickstart(Root(), "/", config={
"/ws": {
"tools.websocket.on": True,
"tools.websocket.handler_cls": AgentServer,
}
})
Client.cs
using UnityEngine;
using System.Collections;
using WebSocketSharp;
public class Client : MonoBehaviour {
private WebSocket ws;
// Use this for initialization
void Start () {
this.ws = new WebSocket("ws://127.0.0.1:3000");
this.ws.OnOpen += (sender, e) => {
Debug.Log("Opened");
};
ws.OnMessage += (sender, e) =>
{
Debug.Log("WebSocket Message Type: " + e.Type + ", Data: " + e.Data);
};
this.ws.OnClose += (sender, e) => {
Debug.Log("Closed");
};
this.ws.Connect();
}
// Update is called once per frame
void Update () {
if (Input.GetKeyUp(KeyCode.Space))
{
Debug.Log ("Pressed");
ws.Send("Test Message");
}
}
void OnDestroy()
{
ws.Close();
ws = null;
}
}
ErrorCode in Unity
Closed
UnityEngine.Debug:Log(Object)
client:<Start>m__3E(Object, CloseEventArgs) (at Assets/client.cs:18)
WebSocketSharp.Ext:Emit(EventHandler`1, Object, CloseEventArgs) (at Assets/Packages/websocket-sharp/Ext.cs:1101)
WebSocketSharp.WebSocket:close(CloseEventArgs, Boolean, Boolean, Boolean) (at Assets/Packages/websocket-sharp/WebSocket.cs:917)
WebSocketSharp.WebSocket:fatal(String, CloseStatusCode) (at Assets/Packages/websocket-sharp/WebSocket.cs:1128)
WebSocketSharp.WebSocket:doHandshake() (at Assets/Packages/websocket-sharp/WebSocket.cs:1085)
WebSocketSharp.WebSocket:connect() (at Assets/Packages/websocket-sharp/WebSocket.cs:958)
WebSocketSharp.WebSocket:Connect() (at Assets/Packages/websocket-sharp/WebSocket.cs:2472)
client:Start() (at Assets/client.cs:20)
Anaconda Prompt -python websocket.py
[03/Oct/2016:22:11:59] ENGINE Listening for SIGTERM.
[03/Oct/2016:22:11:59] ENGINE Bus STARTING
[03/Oct/2016:22:11:59] ENGINE Set handler for console events.
[03/Oct/2016:22:11:59] ENGINE Starting WebSocket processing
[03/Oct/2016:22:11:59] ENGINE Started monitor thread '_TimeoutMonitor'.
[03/Oct/2016:22:11:59] ENGINE Started monitor thread 'Autoreloader'.
[03/Oct/2016:22:11:59] ENGINE Serving on http://127.0.0.1:3000
[03/Oct/2016:22:11:59] ENGINE Bus STARTED
127.0.0.1 - - [03/Oct/2016:22:12:03] "GET / HTTP/1.1" 200 48 "" "websocket-sharp/1.0"

Related

Django Channels dont sent message to Android App

I wanted to apply the official django channels tutorial from https://channels.readthedocs.io/en/latest/tutorial/part_2.html to a simple android app.
At https://medium.com/#ssaurel/learn-to-use-websockets-on-android-with-okhttp-ba5f00aea988 I found a simple project but that uses the Echo WebSocket Server available at http://www.websocket.org/echo.html.
I copy-pasted the same project but replaced the Echo WebSocket Server with my own websocket server using django channels.
Here is the code:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button start;
private TextView output;
private OkHttpClient client;
private final class EchoWebSocketListener extends WebSocketListener {
private static final int NORMAL_CLOSURE_STATUS = 1000;
#Override
public void onOpen(WebSocket webSocket, Response response) {
Log.d(TAG, "onOpen() is called.");
JSONObject obj = new JSONObject();
JSONObject obj2 = new JSONObject();
try {
obj.put("message" , "Hello");
obj2.put("message", "Goodbye!");
} catch (JSONException e) {
e.printStackTrace();
}
webSocket.send(obj.toString());
//webSocket.send("What's up ?");
//webSocket.send(ByteString.decodeHex("deadbeef"));
webSocket.close(NORMAL_CLOSURE_STATUS, obj2.toString());
}
#Override
public void onMessage(WebSocket webSocket, String text) {
Log.d(TAG, "onMessage() for String is called.");
output("Receiving : " + text);
}
#Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
Log.d(TAG, "onMessage() for ByteString is called.");
output("Receiving bytes : " + bytes.hex());
}
#Override
public void onClosing(WebSocket webSocket, int code, String reason) {
Log.d(TAG, "onClosing() is called.");
webSocket.close(NORMAL_CLOSURE_STATUS, null);
output("Closing : " + code + " / " + reason);
}
#Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
Log.d(TAG, "onFailure() is called.");
output("Error : " + t.getMessage());
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.start);
output = (TextView) findViewById(R.id.output);
client = new OkHttpClient();
start.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
start();
}
});
}
void start() {
Request request = new Request.Builder().url("ws://192.168.122.1:8080/ws/chat/lobby/").build();
EchoWebSocketListener listener = new EchoWebSocketListener();
WebSocket ws = client.newWebSocket(request, listener);
client.dispatcher().executorService().shutdown();
}
private void output(final String txt) {
runOnUiThread(new Runnable() {
#Override
public void run() {
output.setText(output.getText().toString() + "\n\n" + txt);
}
});
}
}
To send & receive messages to/from the server, they use WebSocketListener from the okhttp3 library.
My consumers.py file is the same as in the Django channels tutorial. More specifically, I used the same setup.
Nevertheless, to be on the same page, I post how my consumers.py file look like:
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
import json
class SignallingConsumer(WebsocketConsumer):
def connect(self):
print("connect() is called.")
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name = 'chat_%s' % self.room_name
# Join room group
async_to_sync(self.channel_layer.group_add)(
self.room_group_name,
self.channel_name
)
self.accept()
def disconnect(self, close_code):
print("disconnect() is called.")
# Leave room group
async_to_sync(self.channel_layer.group_discard)(
self.room_group_name,
self.channel_name
)
# Receive message from WebSocket
def receive(self, text_data):
print("receive() is called with " + text_data)
text_data_json = json.loads(text_data)
message = text_data_json['message']
print("message contains: " + message)
# Send message to room group
async_to_sync(self.channel_layer.group_send)(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# Receive message from room group
def chat_message(self, event):
print("the message from the event is: " + event['message'])
message = event['message']
# Send message to WebSocket
self.send(text_data=json.dumps({
'message': message
}))
I added some prints to understand better what is going under the hood and changed the name of the consumer since I want to use it later as a signalling server for another project when I understand the basics.
On the console, I get the following:
System check identified no issues (0 silenced).
July 25, 2019 - 10:34:24
Django version 2.2.3, using settings 'signalingserver.settings'
Starting ASGI/Channels version 2.2.0 development server at http://192.168.122.1:8080/
Quit the server with CONTROL-C.
WebSocket HANDSHAKING /ws/chat/lobby/ [192.168.122.1:54194]
connect() is called.
WebSocket CONNECT /ws/chat/lobby/ [192.168.122.1:54194]
receive() is called with {"message":"Hello"}
message contains: Hello
WebSocket DISCONNECT /ws/chat/lobby/ [192.168.122.1:54194]
the message from the event is: Hello
disconnect() is called.
My LogCat output is the following:
07-25 12:35:22.754 5297-5319/com.celik.abdullah.simplewebsocketproject D/MainActivity: onOpen() is called.
07-25 12:35:22.810 5297-5319/com.celik.abdullah.simplewebsocketproject D/MainActivity: onClosing() is called.
From these outputs I assume that the connection & sending part is functioning. But the receiving of data from the websocket server is not working because the onMessage() on my client side (android app) is not called.
How, I can fix that ?
Thank you in advance.
In case anyone else sees this, the issue is because you are closing the web socket in your onOpen method before you get a chance to receive anything back from the server. You should remove the line:
webSocket.close(NORMAL_CLOSURE_STATUS, obj2.toString());
And call close at a later time when you are no longer using the web socket connection.
Everything else looks great, and works as written.

Python: Push Notifications to Node.js Socket.io using Redis PubSub

Now my project use Django as API and NodeJs (SocketIO) as server which require Realtime for pushing Notifications
I try pushing Notifications to Node.js Socket.io using Redis PubSub but not success. Please check out my code error:
My Python code. I publish to myChannel sample message:
def test_vew(request):
REDIS_SERVER = redis.Redis(host='localhost', port=6379, db=0)
REDIS_SERVER.publish("myChannel", "Demo Message")
return Response({ "success": True }, status=200)
My NodeJS Code:
var app = require('http').createServer()
const PORT = process.env.PORT || 3000;
var redis = require('redis').createClient();
const server = app.listen(3000, () => console.log('listening on port 3000'))
//sockets
const io = require('socket.io').listen(app)
redis.subscribe('myChannel', (message) => {
console.log(`Got message ` + message)
})
io.sockets.on("connection", function(socket) {
console.log("A User Connected to SocketIO");
redis.on("pmessage", function(pattern, channel, message) {
console.log(channel, message);
});
});
When I run function in Django, My NodeJS Socket Subcribe can't grab message (Nothing log in console). I don't know the reason.

Flask Socket.io doesn't work when emitting from thread

Description
The client side has 2 buttons:
One makes the server to send periodic messages
The other, stops the sending of periodic messages
This problem is a proxy of the real problem I am trying to solve.
I build the app, and in the server side it seems to work, but the client doesn't receive the server push, but is able to start the push and kill it!
What I tried
Server Side
import random
from threading import Thread
from time import sleep
from flask import Flask
from flask_socketio import SocketIO
SOCKET_NAMESPACE = '/test'
is_pushing = False
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!!'
socketio = SocketIO(app)
def server_push(fps):
dt = 1 / fps
global is_pushing
while is_pushing:
with app.test_request_context('/'):
sent = f"Server Pushed!! {random.random()}"
print(sent)
socketio.emit("serverResponse", sent, namespace=SOCKET_NAMESPACE)
sleep(dt)
#socketio.on('connect', namespace=SOCKET_NAMESPACE)
def on_connect():
print("connected server!!")
socketio.emit("serverResponse", "First Push", namespace=SOCKET_NAMESPACE)
#socketio.on('disconnect', namespace=SOCKET_NAMESPACE)
def on_disconnect():
print("disconnected server!!")
#socketio.on('startServerPush', namespace=SOCKET_NAMESPACE)
def on_start_server_push(fps=1):
print("Sever push start!!")
global is_pushing
is_pushing = True
socketio.emit("serverResponse", "Start Push", namespace=SOCKET_NAMESPACE)
Thread(target=lambda: server_push(fps)).start()
#socketio.on("killServerPush", namespace=SOCKET_NAMESPACE)
def on_kill_server_push():
print("Server push stop!!")
global is_pushing
is_pushing = False
socketio.emit("serverResponse", "Kill Push", namespace=SOCKET_NAMESPACE)
def main():
socketio.run(app, port=8082, debug=True)
if __name__ == '__main__':
main()
Client Side
import openSocket from 'socket.io-client';
import React, { Component } from 'react';
class Test extends Component {
state = {
pushedFromServer: [],
socket: null
};
componentDidMount() {
const url = 'localhost:8082/test';
const socket = openSocket(url);
socket.on('connect', () => console.log('Test connected!!'));
socket.on('disconnect', () => console.log('Test disconnected!!'));
socket.on('serverResponse', response => {
console.log(response);
const pushedFromServer = [...this.state.pushedFromServer];
pushedFromServer.push(response);
this.setState({ pushedFromServer });
});
this.setState({ socket });
}
killServerPush = () => {
this.state.socket.emit('killServerPush');
};
startServerPush = () => {
this.state.socket.emit('startServerPush');
};
render() {
return (
<div>
<button onClick={this.startServerPush}>
<h3>Start push from server</h3>
</button>
<button onClick={this.killServerPush}>
<h3>Kill push from server</h3>
</button>
<ul>
{this.state.pushedFromServer.map(value => (
<li>{value}</li>
))}
</ul>
</div>
);
}
}
export default Test;
Final Notes
In the client, I could receive the First Push, and the Start Push, I am also able to stop the periodic process from the client and restart it. I am not able to receive the periodic messages on the client.
Thanks
By looking at https://github.com/miguelgrinberg/python-socketio/issues/99, I found a solution to the issue.
Just need to change the server side.
Change line:
Thread(target=lambda: server_push(fps)).start()
to
socketio.start_background_task(target=lambda: server_push(fps))
And instead of using python sleep, use:
socketio.sleep(dt)

WebSocket: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received

I'm trying to create a WS connection with my tornado server. The server code is simple:
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def open(self):
print("WebSocket opened")
def on_message(self, message):
self.write_message(u"You said: " + message)
def on_close(self):
print("WebSocket closed")
def main():
settings = {
"static_path": os.path.join(os.path.dirname(__file__), "static")
}
app = tornado.web.Application([
(r'/ws', WebSocketHandler),
(r"/()$", tornado.web.StaticFileHandler, {'path':'static/index.html'}),
], **settings)
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
I copy pasted the client code from here:
$(document).ready(function () {
if ("WebSocket" in window) {
console.log('WebSocket is supported by your browser.');
var serviceUrl = 'ws://localhost:8888/ws';
var protocol = 'Chat-1.0';
var socket = new WebSocket(serviceUrl, protocol);
socket.onopen = function () {
console.log('Connection Established!');
};
socket.onclose = function () {
console.log('Connection Closed!');
};
socket.onerror = function (error) {
console.log('Error Occured: ' + error);
};
socket.onmessage = function (e) {
if (typeof e.data === "string") {
console.log('String message received: ' + e.data);
}
else if (e.data instanceof ArrayBuffer) {
console.log('ArrayBuffer received: ' + e.data);
}
else if (e.data instanceof Blob) {
console.log('Blob received: ' + e.data);
}
};
socket.send("Hello WebSocket!");
socket.close();
}
});
When it tries to connect i get the following output on the browser's console:
WebSocket connection to 'ws://localhost:8888/ws' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
Why is that?
As pointed out in whatwg.org's Websocket documentation (it's a copy from the standard's draft):
The WebSocket(url, protocols) constructor takes one or two arguments. The first argument, url, specifies the URL to which to connect. The second, protocols, if present, is either a string or an array of strings. If it is a string, it is equivalent to an array consisting of just that string; if it is omitted, it is equivalent to the empty array. Each string in the array is a subprotocol name. The connection will only be established if the server reports that it has selected one of these subprotocols. The subprotocol names must all be strings that match the requirements for elements that comprise the value of Sec-WebSocket-Protocol fields as defined by the WebSocket protocol specification.
Your server answers the websocket connection request with an empty Sec-WebSocket-Protocol header, since it doesn't support the Chat-1 subprotocol.
Since you're writing both the server side and the client side (and unless your writing an API you intend to share), it shouldn't be super important to set a specific subprotocol name.
You can fix this by either removing the subprotocol name from the javascript connection:
var socket = new WebSocket(serviceUrl);
Or by modifying your server to support the protocol requested.
I could give a Ruby example, but I can't give a Python example since I don't have enough information.
EDIT (Ruby example)
Since I was asked in the comments, here's a Ruby example.
This example requires the iodine HTTP/WebSockets server, since it supports the rack.upgrade specification draft (concept detailed here) and adds a pub/sub API.
The server code can be either executed through the terminal or as a Rack application in a config.ru file (run iodine from the command line to start the server):
# frozen_string_literal: true
class ChatClient
def on_open client
#nickname = client.env['PATH_INFO'].to_s.split('/')[1] || "Guest"
client.subscribe :chat
client.publish :chat , "#{#nickname} joined the chat."
if client.env['my_websocket.protocol']
client.write "You're using the #{client.env['my_websocket.protocol']} protocol"
else
client.write "You're not using a protocol, but we let it slide"
end
end
def on_close client
client.publish :chat , "#{#nickname} left the chat."
end
def on_message client, message
client.publish :chat , "#{#nickname}: #{message}"
end
end
module APP
# the Rack application
def self.call env
return [200, {}, ["Hello World"]] unless env["rack.upgrade?"]
env["rack.upgrade"] = ChatClient.new
protocol = select_protocol(env)
if protocol
# we will use the same client for all protocols, because it's a toy example
env['my_websocket.protocol'] = protocol # <= used by the client
[101, { "Sec-Websocket-Protocol" => protocol }, []]
else
# we can either refuse the connection, or allow it without a match
# here, it is allowed
[101, {}, []]
end
end
# the allowed protocols
PROTOCOLS = %w{ chat-1.0 soap raw }
def select_protocol(env)
request_protocols = env["HTTP_SEC_WEBSOCKET_PROTOCOL"]
unless request_protocols.nil?
request_protocols = request_protocols.split(/,\s?/) if request_protocols.is_a?(String)
request_protocols.detect { |request_protocol| PROTOCOLS.include? request_protocol }
end # either `nil` or the result of `request_protocols.detect` are returned
end
# make functions available as a singleton module
extend self
end
# config.ru
if __FILE__.end_with? ".ru"
run APP
else
# terminal?
require 'iodine'
Iodine.threads = 1
Iodine.listen2http app: APP, log: true
Iodine.start
end
To test the code, the following JavaScript should work:
ws = new WebSocket("ws://localhost:3000/Mitchel", "chat-1.0");
ws.onmessage = function(e) { console.log(e.data); };
ws.onclose = function(e) { console.log("Closed"); };
ws.onopen = function(e) { e.target.send("Yo!"); };
For those who use cloudformation templates, AWS has a nice example here.
UPDATE
The key thing is the response in the connection function. On the abovementioned AWS shows how this can be done:
exports.handler = async (event) => {
if (event.headers != undefined) {
const headers = toLowerCaseProperties(event.headers);
if (headers['sec-websocket-protocol'] != undefined) {
const subprotocolHeader = headers['sec-websocket-protocol'];
const subprotocols = subprotocolHeader.split(',');
if (subprotocols.indexOf('myprotocol') >= 0) {
const response = {
statusCode: 200,
headers: {
"Sec-WebSocket-Protocol" : "myprotocol"
}
};
return response;
}
}
}
const response = {
statusCode: 400
};
return response;
};
function toLowerCaseProperties(obj) {
var wrapper = {};
for (var key in obj) {
wrapper[key.toLowerCase()] = obj[key];
}
return wrapper;
}
Please note the header settings in the response. Also this response must be delivered to the requester, for this response integration must be configured.
In the AWS example consider the code:
MyIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref MyAPI
IntegrationType: AWS_PROXY
IntegrationUri: !GetAtt MyLambdaFunction.Arn
IntegrationMethod: POST
ConnectionType: INTERNET
The most important are the last two lines.

Communicating Raspberry Pi with cloudfoundry

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.

Categories