I have a small "server" applet in Python that launches an application and starts a server waiting for some data via websocket:
#!python3
import os
import subprocess
import asyncio
import websockets
async def hello(websocket, path):
name = await websocket.recv()
print("< {}".format(name))
greeting = "Hello {}!".format(name)
await websocket.send(greeting)
print("> {}".format(greeting))
subprocess.Popen(['C:\\...\\some_app.exe', 'd:\\data\\app_data.txt'])
start_server = websockets.serve(hello, 'localhost', 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
The application has a plugin that uses Javascript websockets to connect to the server. In the global part of the js file, I establish a connection:
const socket = new WebSocket('ws://localhost:8765');
//
socket.addEventListener('open', function (event) {
socket.send('Connection Established');
});
socket.addEventListener('message', function (event) {
console.log(event.data);
});
The connection does get established when this code runs, however, in another function, when I try to send data, it fails:
alert("Gonna use socket!");
if (socket.readyState == 1) {
socket.send('foo');
socket.close();
} else {
alert("Socket not ready!");
}
Why does the socket get closed? Is there any way to keep it open (or is that bad practice)?
Related
Below is my code to make socket connection by using socket.io. The problem with the following code is I am not able to get customer header set with extraHeaders at server end. Nether socket.request.headers nor socket.handshake.headers` works for me.
const socketIO = require("socket.io-client");
const socket = socketIO('wss://domain.com', {
transports: ["websocket"],
extraHeaders: {
build_number: "227"
}
});
socket.on("connect", () => {
console.log("connected");
});
I'm trying to use websockets with sails-js but I can't make it work with native javascript websockets.
the tutorial example use the sails.io.js library and it goes a little bit like this:
io.socket.on('hello', function (data) {
console.log('Socket `' + data.id + '` joined the party!');
});
function sendHello () {
// And use `io.socket.get()` to send a request to the server:
io.socket.get('/websockets/hello', function gotResponse(data, jwRes) {
console.log('Server responded with status code ' + jwRes.statusCode + ' and data: ', data);
});
}
This does work, but i want to use the native javascript websockets like this:
let ws = new WebSocket("ws://localhost:1337/websockets/hello");
ws.onopen = function (e) {
console.log("[open] Connection established");
console.log("Sending to server");
ws.send("My name is John");
};
ws.onmessage = function (event) {
console.log(`[message] Data received from server: ${event.data}`);
};
ws.onclose = function (event) {
if (event.wasClean) {
console.log(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);
} else {
// e.g. server process killed or network down
// event.code is usually 1006 in this case
console.log('[close] Connection died');
}
};
ws.onerror = function (error) {
console.log(`[error] ${error}`);
console.log(error);
};
Clean and native javascript websockets with no library needed. Unfortunately I can't seem to make it work.
When I try to connect to sails js server using native websockets I get this message:
WebSocket connection to 'ws://localhost:1337/websockets/hello' failed: Connection closed before receiving a handshake response
Impossible to make it connect, it seems like sails js don't even get the message because i make a log when i get a new connection (using the tutorial code):
module.exports = {
hello: function (req, res) {
console.log("web socket received",req.isSocket)
// Make sure this is a socket request (not traditional HTTP)
if (!req.isSocket) {
return res.badRequest();
}
// Have the socket which made the request join the "funSockets" room.
sails.sockets.join(req, 'funSockets');
// Broadcast a notification to all the sockets who have joined
// the "funSockets" room, excluding our newly added socket:
sails.sockets.broadcast('funSockets', 'hello', { howdy: 'hi there!' }, req);
// ^^^
// At this point, we've blasted out a socket message to all sockets who have
// joined the "funSockets" room. But that doesn't necessarily mean they
// are _listening_. In other words, to actually handle the socket message,
// connected sockets need to be listening for this particular event (in this
// case, we broadcasted our message with an event name of "hello"). The
// client-side code you'd need to write looks like this:
//
// io.socket.on('hello', function (broadcastedData){
// console.log(data.howdy);
// // => 'hi there!'
// }
//
// Now that we've broadcasted our socket message, we still have to continue on
// with any other logic we need to take care of in our action, and then send a
// response. In this case, we're just about wrapped up, so we'll continue on
// Respond to the request with a 200 OK.
// The data returned here is what we received back on the client as `data` in:
// `io.socket.get('/say/hello', function gotResponse(data, jwRes) { /* ... */ });`
return res.json({
anyData: 'we want to send back'
});
}
};
How can I make sails js work with native javascript websockets?
Found a simple solution!
Used the npm package ws: npm i ws
making a new hook: sails generate hook customWebSocket
in the hook :
/**
* WS hook
*
* #description :: A hook definition. Extends Sails by adding shadow routes, implicit actions, and/or initialization logic.
* #docs :: https://sailsjs.com/docs/concepts/extending-sails/hooks
*/
const WebSocket = require('ws');
module.exports = function defineWsHook(sails) {
return {
/**
* Runs when this Sails app loads/lifts.
*/
initialize: async function () {
sails.log.info('Initializing custom hook (`WS`)');
console.log("custom hook")
const wss = new WebSocket.Server({ port: 3100 });
wss.on('connection', (socket) => {
console.log('New user connected wss');
socket.on('message', function incoming(message) {
console.log(message)
});
});
}
};
};
Done and done, now i can connect to is using native websocket!
now that i have done that i realize that the socket.io library might be better for handling errors.
I want to send message to client (React JS) from another thread (because there I start basic consuming messages from pika and it blocks Flask app).
That's my simple code at client side:
const socket = io.connect('ws://127.0.0.1:5000',{transports: ['websocket'], secure: true, port: '5000'});
useEffect(()=> {
socket.emit("echo", "echo");
}, [])
socket.on('echoresponse', (data) => {
console.log(data);
});
socket.on('update_page', (data) => {
console.log(data);
});
When server receives echo, it answers back and everything work ok.
But when I try to emit from send_result_in_thread(), client receives nothing.
My server side (missing some rabbitmq pika code)
clients = []
app = Flask(__name__)
socketIo = SocketIO(app, async_mode='eventlet', cors_allowed_origins="*")
eventlet.monkey_patch()
app.debug = True
app.host = 'localhost'
app.debug = True
app.host = 'localhost'
def send_result_in_thread(cli, message):
for client in cli:
socketIo.emit('update_page', message, room=client)
print('sending message "{}" to client {}'.format(message.decode("utf-8"), client))
#socketIo.on("echo")
def echo( message):
emit('echoresponse', "I'm alive")
#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)
if __name__ == '__main__':
consumer = consumer.Consumer()
t = threading.Thread(target=consumer.run, name="run", args=(clients, ))
t.daemon = True
t.start()
socketIo.run(app, host='0.0.0.0', port='5000',
debug=True)
Another thread:
class Consumer():
def __init__(self):
self.clients = []
#and some more code for rabbitmq
def got_result(self, ch, method, properties, body):
print("CLIENTS from thread:" + str(self.clients))
socketIo.start_background_task(send_result_in_thread, self.clients, body)
def run(self, clients):
self.clients = clients
self.channel.basic_consume(self.queue_name, self.got_result)
self.channel.start_consuming()
I found similar problem here and used as a reference https://stackoverrun.com/ru/q/12599436
But it doesn't help solving the problem. Client_id is passed to thread successfully, the send_result_in_thread is called but client doesn't receive message. I have no idea why.
Thanks in advance.
Finally I've found the problem. Every thread had his own socketIo variable, so message was not send to client. My solution:
In main:
t = threading.Thread(target=consumer.run, name="run", args=(clients, socketIo))
In run:
self.socketIo = socketIo
And in got_result:
performer.socketIo.start_background_task(performer.send_result_in_thread, self.clients, self.socketIo, body.decode("utf-8"))
so I am creating a module for the members that are using my services (cloudlist.xyz).
basically, we have a voting system in our service, this module is making a connection using socket io on the server and socket io client on the module, announcing to the user when someone votes on it
Everything is working normally, but when I restart the server to do some maintenance, all users are disconnected from socket io even when the server is on again
Server side code :
var server = app.listen(process.env.PORT || 3000, () => {
console.log("Your app is listening on port " + server.address().port)
});
var io = require('socket.io')(server)
io.on("connection",function(socket) {
console.log("Someone Joined to our server api!")
})
//that's the part that he emits the event when someone votes
io.of(`vote/${bot}`).emit("voted", user_votes.val());
Module/client side:
var https = require('https');
const { EventEmitter } = require("events");
var fetch = require('node-fetch')
const io = require("socket.io-client");
module.exports = class Cloud_client extends EventEmitter {
constructor(id, token) {
super();
if (!id) throw new Error("Missing client instance on contructor");
if (!token) throw new Error("Missing token on constructor");
this.id = id;
this.token = token;
this.socket = io.connect(`https://www.cloudlist.xyz/vote/${id}`, {
reconnect:true,
autoConnect:true,
reconnectionDelay: 1000,
reconnectionDelayMax : 5000,
reconnectionAttempts: Infinity
});
this.socket.on("connect", () => this.emit("connected"));
this.socket.on("disconnect", (...args) => {this.socket.open();
});
this.socket.on("voted", (...args) => this.emit("voted", ...args));
};
this is an example of someone using the module:
var cdl = require("cloud-list")
var cloud_client = new cdl("701456902160121966","5669556617e2a070ada1688")
cloud_client.on("connected", (data) => {
console.log(`Connected to the api Server`)
})
cloud_client.on("voted", (data) => {
console.log(`Thanks,user ${data.user_name} for voting on us :)`)
})
When I connect to the server, it sends the message of this example saying "Connected to the api Server", but when I restart the server, I don't receive anything. Already tried this.socket.on("disconnect", (...args) => {this.socket.open()}); or this.socket.on("disconnect", (...args) => {this.socket.connect()}); ,but still the same thing,user can't reconnect again.
the only way for users to connect again is to restart his project, which is very bad
Socket connections require the server to be serving. Socket.io doesn't seem good for a voting system unless you want it to be real time. It's expected for clients to restart when the server restarts.
As per with working in Socket server we need to restart our node socket server during the restart of the main servers like apache or Nginx.
Because it is not an automatic process on the server.
I've been trying to resolve a really strange Socket.io bug.
If I open the page on the client while the server is running, it will fail to connect with the message:
universalModuleDefinition:3 WebSocket connection to
'ws://localhost:4000/socket.io/?EIO=3&transport=websocket&sid=f6LwPIDZubiPKE-TAAAA'
failed: Connection closed before receiving a handshake response
If I then restart the server, while leaving the page open, it connects without issue.
app.js
const app = express();
const server = require('http').Server(app);
require('./socket')(server);
// More code here
server.listen(app.get('port'))
socket.js
const io = require('socket.io');
const jackrabbit = require(`jackrabbit`);
const rabbit = jackrabbit(process.env.RABBIT_URI);
const exchange = rabbit.default();
function Socket (app) {
this.io = io(app);
this.io.on('connection', socket => {
socket.emit('sync');
socket.on('room', room => {
socket.join(room);
});
})
this.queue = exchange.queue({ name: 'worker.socket' });
this.queue.consume(this.onMessage.bind(this), { noAck: true });
}
Socket.prototype.onMessage = function (message) {
this.io.to(message.report).emit('photo', message.photo);
}
module.exports = function (app) {
return new Socket(app);
}
client
var socket = io.connect();
socket.on('connect', function () {
// This gets triggered every time (after the error above)
console.log('Connected');
// This is never logged by the server
socket.emit('room', value); // value set by template engine
});
socket.on('sync', function(){
// will not execute first time I connect, but if I restart
// the server, it runs no problem
alert('Synced with server');
})
socket.on('photo', function(data) {
// also will not be run the first time, but works if the
// server is restarted when the page is open
})
Edit:
I've tried rewriting it to
Initialise socket.io within app.js, then pass it to the socket controller
Run server.listen before requiring socket.js
Initialising the client after a timeout
Setting the transport method on the client strictly to websocket
None of these methods have worked
Found the solution to my problem (actually not an issue with any of the code I posted). I was using the compression middleware for Express, which appears to break socket.io. Solution was to add the following:
app.use((req, res, next) => {
// Disable compression for socket.io
if (req.originalUrl.indexOf('socket.io') > -1) {
return next();
}
compression()(req, res, next);
});