I have two servers, server 1 hosting laravel echo and server 2 hosting NodeJS. I want server 2 to be able to listen and transmit on a laravel-echo channel which is in server 1.
Anyone have an idea on how to proceed?
I tested with socket IO but I don't receive the events emitted from laravel echo server.
Here is the code I am using
const io = require('socket.io-client');
const socket = io('http://localhost:6001');
socket.on('*', (data) => {
console.log(data);
});
socket.emit('subscribe', {
channel: 'licence-update-7e32cd49-8714-42bf-804b-d823c5698ecc'
});
socket.on('connect', () => {
socket.emit('subscribe', {
// channel: 'private-channel',
channel: 'licence-update-7e32cd49-8714-42bf-804b-d823c5698ecc'
// auth: Cache.get('private-channel:socket-id')
});
});
socket.on('event-name', (data) => {
console.log(data);
});
the code executes but I do not receive a notification.
I want server 2 to be notified whenever there are actions on server 1 and vice versa.
Works using version socket.io.client version 2.4.0 of laravel-echo-client
Related
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.
I'm trying to create a web page where the user can authenticate to a remote server via ssh with username/password, and then interact with the remote server.
I'm not looking to create a full interactive terminal: the app server will execute a limited set of commands based on user input and then pass the responses back to the browser.
Different users should interact with different ssh sessions.
My app is built in Meteor 1.8.1, so the back end runs under Node JS, version 9.16.0. It's deployed to Ubuntu using Phusion Passenger.
I have looked at several packages that can create an interactive ssh session but I am missing something basic about how to use them.
For example https://github.com/mscdex/ssh2#start-an-interactive-shell-session
The example shows this code:
var Client = require('ssh2').Client;
var conn = new Client();
conn.on('ready', function() {
console.log('Client :: ready');
conn.shell(function(err, stream) {
if (err) throw err;
stream.on('close', function() {
console.log('Stream :: close');
conn.end();
}).on('data', function(data) {
console.log('OUTPUT: ' + data);
});
stream.end('ls -l\nexit\n');
});
}).connect({
host: '192.168.100.100',
port: 22,
username: 'frylock',
privateKey: require('fs').readFileSync('/here/is/my/key')
});
This example connects to the remote server, executes a command 'ls' and then closes the session. It isn't 'interactive' in the sense I'm looking for. What I can't see is how to keep the session alive and send a new command?
This example of a complete terminal looks like overkill for my needs, and I won't be using Docker.
This example uses socket.io and I'm not sure how that would interact with my Meteor app? I'm currently using Meteor methods and publications to pass information between client and server, so I'd expect to need a "Meteor-type" solution using the Meteor infrastructure?
child_process.spawn works but will only send a single command, it doesn't maintain a session.
I know other people have asked similar questions but I don't see a solution for my particular case. Thank you for any help.
I got this working by following these instructions for creating an interactive terminal in the browser and these instructions for using socket.io with Meteor.
Both sets of instructions needed some updating due to changes in packages:
meteor-node-stubs now uses stream-http instead of http-browserify
https://github.com/meteor/node-stubs/issues/14 so don't use the hack for socket
xterm addons (fit) are now separate packages
xterm API has changed, use term.onData(...) instead of term.on('data'...)
I used these packages:
ssh2
xterm
xterm-addon-fit
socket.io
socket.io-client
and also had to uninstall meteor-mode-stubs and reinstall it to get a recent version that doesn't rely on the Buffer polyfill.
Here's my code.
Front end:
myterminal.html
<template name="myterminal">
<div id="terminal-container"></div>
</template>
myterminal.js
import { Template } from 'meteor/templating';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import './xterm.css'; // copy of node_modules/xterm/css/xterm.css
// xterm css is not imported:
// https://github.com/xtermjs/xterm.js/issues/1418
// This is a problem in Meteor because Webpack won't import files from node_modules: https://github.com/meteor/meteor-feature-requests/issues/278
const io = require('socket.io-client');
Template.fileExplorer.onRendered(function () {
// Socket io client
const PORT = 8080;
const terminalContainer = document.getElementById('terminal-container');
const term = new Terminal({ 'cursorBlink': true });
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
term.open(terminalContainer);
fitAddon.fit();
const socket = io(`http://localhost:${PORT}`);
socket.on('connect', () => {
console.log('socket connected');
term.write('\r\n*** Connected to backend***\r\n');
// Browser -> Backend
term.onData((data) => {
socket.emit('data', data);
});
// Backend -> Browser
socket.on('data', (data) => {
term.write(data);
});
socket.on('disconnect', () => {
term.write('\r\n*** Disconnected from backend***\r\n');
});
});
});
Server:
server/main.js
const server = require('http').createServer();
// https://github.com/mscdex/ssh2
const io = require('socket.io')(server);
const SSHClient = require('ssh2').Client;
Meteor.startup(() => {
io.on('connection', (socket) => {
const conn = new SSHClient();
conn.on('ready', () => {
console.log('*** ready');
socket.emit('data', '\r\n*** SSH CONNECTION ESTABLISHED ***\r\n');
conn.shell((err, stream) => {
if (err) {
return socket.emit('data', `\r\n*** SSH SHELL ERROR: ' ${err.message} ***\r\n`);
}
socket.on('data', (data) => {
stream.write(data);
});
stream.on('data', (d) => {
socket.emit('data', d.toString('binary'));
}).on('close', () => {
conn.end();
});
});
}).on('close', () => {
socket.emit('data', '\r\n*** SSH CONNECTION CLOSED ***\r\n');
}).on('error', (err) => {
socket.emit('data', `\r\n*** SSH CONNECTION ERROR: ${err.message} ***\r\n`);
}).connect({
'host': process.env.URL,
'username': process.env.USERNAME,
'agent': process.env.SSH_AUTH_SOCK, // for server which uses private / public key
// in my setup, already has working value /run/user/1000/keyring/ssh
});
});
server.listen(8080);
});
Note that I am connecting from a machine that has ssh access via public key to the remote server. You may need different credentials depending on your setup. The environment variables are loaded from a file at Meteor runtime.
Scenario is i have a server where socket(1) runs i have one more server where socket(2) client connects to socket(1)
I have one browser socket which connects to socket(1)
Idea is to do request from browser and bring data from socket(2) server
Not sure how to difference between socket clients as all the sockets are similar to socket(1)
Ideally there will be multiple browser sockets and multiple socket(2) clients
Browser sockets can make request to any of the socket(2) clients
How to implement it using nodejs socket.io
Server
socket.on('action', (action) => {
if(action.type === 'server/hello'){
io.sockets.emit('broadcast',{ description: clients + ' clients connected!'});
console.log('Got hello data!', action.data);
}
});
Browser client
var socket = io.connect('localhost:3000', {reconnect: true});
socket.on('connect', function(data) {
socket.emit('joined', 'Hello World from client this is client plxx');
});
socket.on('response2', function(data) {
console.log("got it ", data);
$('#messages').append($('<li>').text(JSON.stringify(data)));
});
Server client
var io = require('socket.io-client');
var socket = io.connect('http://localhost:3000', {reconnect: true});
socket.on('broadcast', function (t) {
socket.emit("data", {data: 32})
console.log('broadcast! my host is est');
});
i should be able to communicate between socket clients
What I understood from your question is: you need to differentiate between sockets from different clients.
To solve that I would suggest simply emitting the socket source from the client on connect.
And on the server split the sockets into two lists.
Example:
Server
const BROWSER_CLIENTS = {};
const SERVER_CLIENTS = {};
io.on("connection", socket => {
socket.on("source", payload => {
if (payload == "browser")
BROWSER_CLIENTS[socket.id] = socket;
else if (payload == "server")
SERVER_CLIENTS[socket.id] = socket;
});
socket.on("disconnect", () => {
delete BROWSER_CLIENTS[socket.id];
delete SERVER_CLIENTS[socket.id];
});
});
Browser Client
socket.on("connect", () => {
socket.emit("source", "browser");
});
Server Client
socket.on("connect", () => {
socket.emit("source", "server");
});
Now when you receive an event you can detect from which source it originated. And if you need to send to all sockets of one type of clients you can simply do this:
Server
for (let i in BROWSER_CLIENTS)
BROWSER_CLIENTS[i].emit("Hello Browsers")
for (let i in SERVER_CLIENTS)
SERVER_CLIENTS[i].emit("Hello Servers")
EDIT: I found this link and thought you could make use of it. Socket.io Rooms
I am using Socket.io to connect a React client to a Node.js server and the query option in socket.io to identify uniquely every new client. However, the server creates multiple sockets for every client and, when I need to send something from the server, I don't know which socket use, because I have more than one, and all of them are connected.
The client code:
import io from "socket.io-client";
...
const socket = io(process.env.REACT_APP_API_URL + '?userID=' + userID, { forceNew: true });
socket.on('connect', () => {
socket.on('new-order', data => {
const { add_notification } = this.props;
add_notification(data);
});
The server code:
....
server = http
.createServer(app)
.listen(8080, () => console.log(env + ' Server listening on port 8080'));
io = socketIo(server);
io.on('connection', socket => {
const userID = socket.handshake.query.userID;
socket.on('disconnect', () => {
socket.removeAllListeners();
});
});
And here the server-side that emits events to the client:
for (const socketID in io.sockets.connected) {
const socket = io.sockets.connected[socketID];
if (socket.handshake.query.userID === userID) {
// Here, I find more than one socket for the same condition, always connected.
socket.emit(event, data)
}
}
Here, it is possible to see all these socket for the same client:
I tried to send events for all socket from a given userID, however, multiple events are triggered to the client, showing duplicated data to the user. I also tried to send events to the last socket, but, sometimes it works, sometimes doesn't.
Someone have a clue how to uniquely identify a socket when there are several clients?
Hi I'm really new to MQTT and I've read a lot of posts and blogs about it the last days, yet I seem not to fully understand the different parts needed, such as Broker, Clients.
I want two node apps to communicate with each other via a local mqtt service. As far as I understand, this mqtt service is called broker. I managed it to let 2 node apps communicate via a public broker like this:
app1 (sender)
const mqtt = require('mqtt');
// to be changed to own local server/service
const client = mqtt.connect('http://broker.hivemq.com');
client.on('connect', () => {
let id = 0;
setInterval(() => {
client.publish('myTopic', 'my message - ' + ++id);
}, 3000);
});
app2 (receiver)
const mqtt = require('mqtt');
// to be changed to own local server/service
const client = mqtt.connect('http://broker.hivemq.com');
client.on('connect', () => {
client.subscribe('myTopic');
});
client.on('message', (topic, message) => {
console.log('MSG: %s: %s', topic, message);
});
As this worked, I wanted to move on by replacing the public broker with a private one. After a while I found mqtt-server as a node package.
So I tried the following as a third node app, which is supposed to be the broker for app1 and app2:
server (MQTT broker)
fs = require('fs');
mqttServer = require('mqtt-server');
let subscriptions = [];
let servers = mqttServer(
// servers to start
{
mqtt: 'tcp://127.0.0.1:1883',
mqttws: 'ws://127.0.0.1:1884',
},
// options
{
emitEvents: true
},
// client handler
function (client) {
client.connack({
returnCode: 0
});
client.on('publish', (msg) => {
let topic = msg.topic;
let content = msg.payload.toString();
// this works, it outputs the topic and the message.
// problem is: app2 does not receive them.
// do we have to forward them somehow here?
console.log(topic, content);
});
client.on('subscribe', (sub) => {
let newSubscription = sub.subscriptions[0];
subscriptions.push(newSubscription);
console.log('New subscriber to topic:', newSubscription.topic);
});
});
servers.listen(function () {
console.log('MQTT servers now listening.');
});
Problem
After adjusting the connection-Uris (both to ws://127.0.0.1:1884) in app1 and app2 The server app receives all messages that are published and recognises that someone connected and listens to a specific topic.
But: While the server gets all those events/messages, app2 does not receive those messages anymore. Deducing that, something with this broker must be wrong, since using the public broker everything works just fine.
Any help is appreciated! Thanks in advance.
I can't get mqtt-server to work either, so try Mosca.
Mosca only needs a back end if you want to send QOS1/2 messages, it will work with out one.
var mosca = require('mosca');
var settings = {
port:1883
}
var server = new mosca.Server(settings);
server.on('ready', function(){
console.log("ready");
});
That will start a mqtt broker on port 1883
You need to make sure your clients are connecting with raw mqtt not websockets, so makes sure the urls start mqtt://