I would like to assign a unique identifier to each Websocket connection on the server side. I want to be able to access this identifier upon detecting that a socket close. I'm using the ws and nanoid
wss.on('connection', (ws, req) => {
ws.id = nanoid();
console.log(ws.id);
ws.on('close', (ws, req) => {
console.log(ws.id);
});
});
But unfortunately the second log in the close event listener prints undefined.
How do I maintain state of the WebSocket on the server side?
Thanks!
You just need to send msg to the available connections
try to add :
wss.clients.forEach(client => {
if(client !== ws && client.readyState === WebSocket.OPEN){
client.send(data)
}
})
Related
I am trying to create a "rooms" like feature with the npm 'ws' package, similar to how rooms work in socket.io. I dont want to use socket.io, but cant figure out how to get information on the user/room at connection without sending an extra message.
I took this from the ws docs and it allows me to iterate through all the clients, but i would like to also differentiate a room?
import WebSocket, { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function connection(ws) {
// would like to add a user to a room here?
ws.on('message', function message(data) {
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
// only send to clients in the same room
}
});
});
});
This is the client side I have been using to test:
import WebSocket from 'ws';
const ws = new WebSocket('ws://localhost:8080');
ws.on('open', function open() {
ws.send(JSON.stringify({roomId: 1}));
});
ws.on('message', function message(data) {
console.log('received: %s', data);
});
If you are wanting to assign a room at connection you change the path of the url and then access it through the req object, then add it as a property on the connecting websocket.
Then you can send the roomId with any message and check that the roomId for message and the roomId for the client match before sending.
wss.on('connection', function connection(ws, req) {
ws.roomId = req.url;
ws.on('message', function message(data) {
const parsedMessage = JSON.parse(data);
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
if(client.roomId === parsedMessage.roomId){
ws.send(data);
}
}
});
});
});
In order to connect to this socket you would have to change your client to send to the url + the roomId.
const ws = new WebSocket('ws://localhost:8080/<put room id here>');
Without sending a second message this appears to get the functionality you are looking for
I am using ssh2 and socket.io to enable a real-time ssh connection to a remote server for users of my Meteor 1.8.1 app. The app runs on Ubuntu under Nginx and Phusion Passenger. Here is what the app needs to do:
Each authorised user already has an account on the remote server.
A user will start a session by entering their credentials and clicking a "connect" button in the app.
The user can browse directory listings within their home directory on the remote server.
No user should have access to another user's ssh session.
Their ssh session should be removed from the server when the user clicks a "disconnect" button.
I have the ssh connection working but I can't figure out how to destroy the ssh connection at the end of the user's session. Each time they press disconnect" then "connect", another ssh session is started and the old ssh session is still operational, so each ssh command that is sent is executed multiple times and multiple responses are sent to the browser.
I'm also concerned that the connection isn't secure; in development I'm creating the server with require('http').createServer();. In production, on my Ubuntu server with SSL configured, is it enough to use require('https').createServer(); or is there other configuration required, e.g. of Nginx? Socket.io falls back to older technologies when websocket isn't available; how is that secured?
Main question: why am I seeing duplicate SSH sessions every time the user disconnects and then connects?
Secondary question: where can I find up to date instructions on how to secure socket.io? Or should I give up on socket.io and use WebSocket?
I have read a lot of articles and stack overflow posts, but I'm finding this very confusing and most of the material is out of date. For example socketio-auth is not maintained. I can find almost nothing in the Socket.io documentation on authentication or authorization - there is a handshake entry but it's not clear to me from this whether it's the function I need or how to use it.
Here's my code.
Server
io.on('connection', (socket) => {
console.log('socket id', socket.id); // this shows a new id after disconnect / reconnect
const conn = new SSHClient();
socket.on('disconnect', () => {
console.log('disconnect on server');
conn.end();
});
conn.on('ready', () => {
socket.emit('message', '*** SSH CONNECTION ESTABLISHED ***');
socket.emit('ready', 'ready');
conn.shell((err, stream) => {
stream.write('stty -echo \n'); // don't echo our own command back, or the user's password...
if (err) {
return socket.emit('message', `*** SSH SHELL ERROR: ' ${err.message} ***`);
}
socket.on('path', (path) => {
// path is a request for a directory listing
if (typeof path === 'string') {
const bashCommand = `ls -l ${path} --time-style=full-iso`;
console.log('*** WRITE'); // if you disconnect and reconnect this runs twice. Disconnect and reconnect again, it runs 3 times.
console.log('socket id again', socket.id); // this shows the same new socket id each time
stream.write(`${bashCommand} \n`);
}
});
stream.on('data', (d) => {
socket.emit('data', response); // tell the browser!
}).on('close', () => {
conn.end();
});
});
}).on('close', () => {
socket.emit('message', '*** SSH CONNECTION CLOSED ***');
}).on('error', (err) => {
socket.emit('message', `*** SSH CONNECTION ERROR: ${err.message} ***`);
}).connect({
'host': hosturl,
'username': ausername,
'agent': anagent, // just for dev I'm using public / private key from my local machine but this will be replaced with the user's entered credentials
});
}).on('disconnect', () => {
console.log('user disconnected');
});
server.listen(8080);
Client:
const io = require('socket.io-client');
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {};
const myEmitter = new MyEmitter();
const PORT = 8080;
let socket;
myEmitter.on('connectClicked', () => {
if (socket) {
this.connected.set(socket.connected);
}
if (this.connected.get() === false) {
socket = io(`http://localhost:${PORT}`);
socket.on('connect', () => {
this.connected.set(true);
socket.on('ready', () => {
console.log('ready');
});
// Backend -> Browser
socket.on('message', (data) => {
console.log('socket on message', data);
});
// Backend -> Browser
socket.on('data', (data) => {
console.log('got data', data);
this.parseResponse(data); // client function to handle data, not shown here
});
// Browser -> Backend
myEmitter.on('selectDirectory', () => {
console.log('*** SELECT DIRECTORY');
socket.emit('path', pathArray.join('/')); // path array is set in client code, it is a simple array of directory names
});
socket.on('disconnect', () => {
console.log('\r\n*** Disconnected from backend***\r\n');
this.connected.set(false);
});
});
}
myEmitter.on('disconnectClicked', () => {
socket.disconnect();
});
});
The answer to keeping the ssh connections separate is to maintain a list of current ssh connections and rework the code so that received ssh data is sent only to the browser that corresponds to the incoming message.
I've also given up on socket.io because I can't be confident about security. I'm now using Meteor's inbuilt DDP messaging system via the Meteor Direct Stream Access package. I think this avoids opening up any new points of access to my web server.
Im having this alot of http petitions (6k INSIDE LAGGING) in 1-3 minutes in the console when i receive or send data to a socketio connection.
Im using node+express in the backend and vue on the front
Backend:
app.js
mongoose.connect('mongodb://localhost/app',{useNewUrlParser:true,useFindAndModify:false})
.then(result =>{
const server = app.listen(3000)
const io = require('./sockets/socket').init(server)
io.on('connection', socket =>{
// console.log('client connected')
})
if(result){console.log('express & mongo running');
}
})
.catch(error => console.log(error))
I created a io instance to use it on the routes
let io
module.exports = {
init: httpServer => {
io = require('socket.io')(httpServer)
return io;
},
getIo:()=>{
if(!io){
throw new Error('socket io not initialized')
}
return io;
}
}
Then, on the route, depending of the logic, the if,else choose what type socket response do
router.post('/post/voteup',checkAuthentication, async (req,res)=>{
//some logic
if(a.length <= 0){
io.getIo().emit('xxx', {action:'cleanAll'})
}
else if(b.length <= 0){
io.getIo().emit('xxx', {action:'cleanT',datoOne})
}
else{
io.getIo().emit('xxx', {action:'cleanX',dataTwo,dataOne,selected})
}
res.json({ serverResponse:'success'})
})
In the front (component) (activated with beforeUpdate life cycle hook)
getData(){
let socket = openSocket('http://localhost:3000')
socket.on('xxx', data => {
if(data.action === 'cleanX'){
if(this.selected === data.selected){
this.ddd = data.dataTwo
}
else if(!this.userTeamNickname){
this.qqq= data.dataOne
}
}
else if(data.action === 'cleanAll'){
this.ddd= []
this.qqq= []
}
else if(data.action === 'cleanT'){
this.ddd= data.dataOne
}
})
},
1. What kind of behavior can produce this such error?
2. Is any other most efficient way to do this?
It looks like socket.io is failing to establish a webSocket connection and has never advanced out of polling. By default, a socket.io connection starts with http polling and after a bit of negotiation with the server, it attempts to establish a webSocket connection. If that succeeds, it stops doing the polling and uses only the webSocket connection. If the the webSocket connection fails, it just keeps doing the polling.
Here are some reasons that can happen:
You have a mismatched version of socket.io in client and server.
You have some piece of infrastructure (proxy, firewall, load balancer, etc...) in between client and server that is not letting webSocket connections through.
You've attached more than one socket.io server handler to the same web server. You can't do that as the communication will get really messed up as multiple server handlers try to respond to the same client.
As a test, you could force the client to connect only with webSocket (no polling at all to start) and see if the connection fails:
let socket = io(yourURL, {transports: ["websocket"]})'
socket.on('connect', () => {console.log("connected"});
socket.on('connect_error', (e) => {console.log("connect error: ", e});
socket.on('connect_timeout', (e) => {console.log("connect timeout: ", e});
It's been a while since I've worked with Node and Websockets. Basically how do I get socket.send() to work from another function is what I'm stuck on.
const server = new WebSocket.Server({ port: 8080 });
server.on('connection', socket => {
socket.on('message', message => {
console.log(`received from a client: ${message}`);
});
socket.send('yo world!');
});
function onMessageHandler (target, context, msg, self) {
client.say(target, response);
server.socket.send(response);
console.log(response);
}
}
How do I get my onMessageHandler to trigger a socket send, this is fail... server.socket.send(response);
Seeing your question i think there is a lack of understanding on how Websockets work. I am assuming you're using https://github.com/websockets/ws
There are two things. First is the WebSocketerver which you've named as server and then an Individual Socket which you've named as socket
Now the thing to understand is socket is not accessible outside server.on() callback The reason for this is there could be 1000 of sockets connected at a given instance and there would be no way to uniquely identify a particular socket you want to send message to.
So ask yourself the question that your application wants to send message to an individual socket to send to everyone who is connected to your server (basically broadcast)
If you want to send to an individual, you will have to uniquely identify the user
this._wss = new WebSocket.Server({
port: ENV_APP_PORT_WS
});
this._wss.on("connection", async (ws: AppWebSocket, req: IncomingMessage) => {
// const ipAddress = req.connection.remoteAddress; // IP Address of User
logger.info(req);
const queryParams = url.parse(req.url, true).query;
let authUser: User;
try {
authUser = await this._authenticateWebSocket(queryParams);
} catch (e) {
// Terminate connection and return...
}
// WS User INIT
ws.isAlive = true;
ws.userId = authUser.id;
ws.uuid = Helpers.generateUUIDV4();
ws.send(JSON.stringify({
type: "connected",
env: ENV
}));
});
The above code will add a property to each socket object that will enable it to uniquely identify a particular user/socket.
While sending =>
onMessageHandler(targetUserId: number, message: string) {
const allSockets = <AppWebSocket[]>Array.from(this._wss.clients.values());
const targetSocket = allSockets.find(w => w.userId === targetUserId);
targetSocket.send(message);
}
If You want to send to all connect users, it's quite easy:
https://github.com/websockets/ws#server-broadcast
I'm trying to create a simple chat application just for the challenge and to learn javascript even more.
For this, I use the built-in package net to do my socket server and client.
To do it simply , I have a client that send a Stringify object when it connect to the server and I want my server to send a message to every client currently connected.
This is the structure of my message :
socket.write(JSON.stringify({
type: message type,
data: message data,
author: message author
}))
Sorry for my bad english and thanks for everyone who read this.
You can do it like this. This is taken from the ws github page https://github.com/websockets/ws
There are many ways to achieve this but in Node this is probably the best solution.
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// Broadcast to all.
wss.broadcast = function broadcast(data) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
};
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
// Broadcast to everyone else.
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});