I have to use Socket.IO with ExpressJS route. I am able to emit an event to client but unable to listen to event emitted from client. This issue comes in case when I have to use socket with Express route.
My server.js looks like this: (here emit command works but io.sockets.on doesn't). I have checked issues with similar problems but still didn't get any clear answer.
var express = require('express');
var app = express();
var server = app.listen(3000);
var io = socketio(server);
app.set('socketio', io);
app.post('/deploy', function(request, response) {
var io = request.app.get('socketio');
var dapp = "some data";
io.sockets.emit('deploy', dapp);
io.sockets.on('deploy_result', (result) => {
console.log(result);
});
})
io.sockets.on (or io.on) won't let you listen to all events, it's just for "connection" event. You'll have to attach your listener to each socket in order to listen to all events, like this:
io.on('connection', function (socket) {
socket.on('deploy_result', (result) => {
console.log(result)
})
})
Also it seems like you're trying to get an "acknowledgement" for an emit, in which case there already exists a better way - the acknowledgement callback, simply pass a callback method as an additional argument (after the data):
server.js
io.on('connection', function (socket) {
socket.emit('deploy', {some: 'data'}, function acknowledgement_callback (result) {
console.log(result)
})
})
client.js
socket.on('deploy', (data, acknowledgement_callback) => {
// Do something with `data`
// Then call the callback with any result:
acknowledgement_callback('result')
// This will fire the "acknowledgement_callback" above on server-side
})
You will need to install express and socket.io eg. in the directory where you have your files as you can see in your codes and then reference those links appropriately
I have updated your code so issue of express and socket will work. its now left for you to ensure that your application run as you like.
here is the link on how to install express
https://www.npmjs.com/package/express
var socket = require( './socket.io' );
var express=require('./express');
var app=express();
var server = require('http').createServer(app);
var io = socket.listen( server );
var port = process.env.PORT || 3000;
app.post('/deploy', function(request, response) {
var io = request.app.get('socketio');
var dapp="some data";
io.sockets.emit('deploy',dapp);
io.sockets.on('deploy_result', (result)=>{
console.log(result);
});
})
Related
I'm actually trying to make a real-time connection between two different apps. I've found a bunch of tutorials about how to make a chat using socket.io, but that doesn't really help me since it's just the same app duplicated in multiple windows.
I'm making a pick & ban overlay for League of Legends in local development. My first thought was to display the empty overlay on one hand and create an interface to manually update it on the other hand. Socket.io seems to be the right thing to use in my case since it can provide new data without having to reload the component.
This is what I wrote in both apps :
const express = require('express');
const socket = require('socket.io');
// App setup
const app = express();
const server = app.listen(4200, function () {
console.log('Listening to requests on port 4200')
});
// Static files
app.use(express.static('public'));
// Socket setup
const io = socket(server);
io.on('connection', function (socket) {
console.log('Made socket connection', socket.id);
socket.on('change', function (data) {
io.sockets.emit('change', data);
});
});
But I fail to connect them as they have to listen to the same port. What am I doing wrong?
(Forgive my bad English and lack of syntax, I'm doing my best here. :p)
I am certainly not an expert on network programming, but as far as I know you need to have one listening app (backend) and another one to connect to it (client). And you define what happens with all the data (messages) that backend recieves (for example sending the messages it recieves to all the clients in the same chat room).
If I am correct to assume you are trying to connect two listening apps?
simple google search of "nodejs socket server client example" revealed this https://www.dev2qa.com/node-js-tcp-socket-client-server-example/ might wanna take your research in this direction
u can try something like this way
var express = require('express');
var socket = require('socket.io');
// App setup
var app = express();
var server = app.listen(8080, () => {
console.log('App started')
})
// Static file
app.use(express.static('public'))
// Socket SetUp
var io = socket(server);
io.on('connection', socket => {
console.log('made the connection')
socket.on('chat',data => {
io.sockets.emit('chat',data)
});
socket.on('typing',data => {
socket.broadcast.emit('typing',data);
});
})
create another file and
var socket = io.connect('http://localhost:8080')
// Elenment
var message = document.getElementById('message');
handle = document.getElementById('handle');
btn = document.getElementById('send');
output = document.getElementById('output');
feedback = document.getElementById('feedback');
// Emit Events
btn.addEventListener('click', () => {
socket.emit('chat', {
message: message.value,
handle: handle.value
})
})
message.addEventListener('keypress', () => {
socket.emit('typing', handle.value)
})
socket.on('chat',data => {
feedback.innerHTML = '';
output.innerHTML += '<p><strong>' + data.handle +': </strong>' +
data.message + '</p>'
})
socket.on('typing', data => {
feedback.innerHTML = '<p><emp>' + data + ' is typing a message... </emp></p>'
})
details are given here node socket chat app
Ok, figured it out. Here's how it works using express and vue together :
First, setup socket.io in your express server js file :
const express = require('express')
const { Server } = require('socket.io')
const http = require('http')
const app = express()
const server = http.createServer(app)
const io = new Server(server, {
cors: {
origin: '*',
methods: ['GET', 'POST', 'REMOVE']
}
})
const PORT = process.env.PORT || 8080
io.on('connection', (socket) => {
console.log('New socket user')
socket.on('SEND_MESSAGE', data => {
console.log('received message in back')
io.emit('MESSAGE', data)
})
})
server.listen(PORT, () => { console.log(`Server started on port : ${PORT}`)})
As you can see we received from the client "SEND_MESSAGE" and we trigger MESSAGE from the server to forward the information to all the clients. The point I was missing is that we bind SEND_MESSAGE on the socked created from the connection but we emit from the io server.
Now you vue part :
import io from 'socket.io-client'
export default {
data() {
return {
messages: [],
inputMessage: null,
socket: io('http://localhost:8080')
}
},
mounted() {
this.socket.on('MESSAGE', data => {
this.messages.push(data)
})
},
methods: {
sendMessage() {
const message = {
senderID: this.myID,
message: this.inputMessage,
sendAt: new Date()
}
this.socket.emit('SEND_MESSAGE', message)
this.inputMessage = null
},
},
}
I'm trying to setup my server with websockets so that when I update something via my routes I can also emit a websocket message when something on that route is updated.
The idea is to save something to my Mongo db when someone hits the route /add-team-member for example then emit a message to everyone who is connected via websocket and is a part of whatever websocket room that corresponds with that team.
I've followed the documentation for socket.io to setup my app in the following way:
App.js
// there's a lot of code in here which sets what to use on my app but here's the important lines
const app = express();
const routes = require('./routes/index');
const sessionObj = {
secret: process.env.SECRET,
key: process.env.KEY,
resave: false,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection }),
secret : 'test',
cookie:{_expires : Number(process.env.COOKIETIME)}, // time im ms
}
app.use(session(sessionObj));
app.use(passport.initialize());
app.use(passport.session());
module.exports = {app,sessionObj};
start.js
const mongoose = require('mongoose');
const passportSocketIo = require("passport.socketio");
const cookieParser = require('cookie-parser');
// import environmental variables from our variables.env file
require('dotenv').config({ path: 'variables.env' });
// Connect to our Database and handle an bad connections
mongoose.connect(process.env.DATABASE);
// import mongo db models
require('./models/user');
require('./models/team');
// Start our app!
const app = require('./app');
app.app.set('port', process.env.PORT || 7777);
const server = app.app.listen(app.app.get('port'), () => {
console.log(`Express running → PORT ${server.address().port}`);
});
const io = require('socket.io')(server);
io.set('authorization', passportSocketIo.authorize({
cookieParser: cookieParser,
key: app.sessionObj.key, // the name of the cookie where express/connect stores its session_id
secret: app.sessionObj.secret, // the session_secret to parse the cookie
store: app.sessionObj.store, // we NEED to use a sessionstore. no memorystore please
success: onAuthorizeSuccess, // *optional* callback on success - read more below
fail: onAuthorizeFail, // *optional* callback on fail/error - read more below
}));
function onAuthorizeSuccess(data, accept){}
function onAuthorizeFail(data, message, error, accept){}
io.on('connection', function(client) {
client.on('join', function(data) {
client.emit('messages',"server socket response!!");
});
client.on('getmessage', function(data) {
client.emit('messages',data);
});
});
My problem is that I have a lot of mongo DB save actions that are going on in my ./routes/index file and I would like to be able to emit message from my routes rather than from the end of start.js where socket.io is connected.
Is there any way that I could emit a websocket message from my ./routes/index file even though IO is setup further down the line in start.js?
for example something like this:
router.get('/add-team-member', (req, res) => {
// some io.emit action here
});
Maybe I need to move where i'm initializing the socket.io stuff but haven't been able to find any documentation on this or perhaps I can access socket.io from routes already somehow?
Thanks and appreciate the help, let me know if anything is unclear!
As mentioned above, io is in your global scope. If you do
router.get('/add-team-member', (req, res) => {
io.sockets.emit('AddTeamMember');
});
Then every client connected, if listening to that event AddTeamMember, will run it's associated .on function on their respective clients. This is probably the easiest solution, and unless you're expecting a huge wave of users without any plans of load balancing, this should be suitable for the time being.
Another alternative you can go:
socket.io lib has a rooms functionality where you can join and emit using the io object itself https://socket.io/docs/rooms-and-namespaces/ if you have a knack for this, it'd look something like this:
io.sockets.in('yourroom').broadcast('AddTeamMember');
This would essentially do the same thing as the top, only instead of broadcasting to every client, it'd only broadcast to those that are exclusive to that room. You'd have to basically figure out a way to get that users socket into the room //before// they made the get request, or in other words, make them exclusive. That way you can reduce the amount of load your server has to push out whenever that route request is made.
Lastly, if neither of the above options work for you, and you just absolutely have to send to that singular client when they initiate it, then it's going to get messy, because you have to have some sort of id to that person, and since you have no reference, you'd have to store all your sockets upon connection, and then make a comparison. I do not fully recommend something like this, because well, I haven't ever tested it, and don't know what type of repercussions could happen, but here is a jist of an idea I had:
app.set('trust proxy', true)
var SOCKETS = []
io.on('connection', function(client) {
SOCKETS.push(client);
client.on('join', function(data) {
client.emit('messages',"server socket response!!");
});
client.on('getmessage', function(data) {
client.emit('messages',data);
});
});
router.get('/add-team-member', (req, res) => {
for (let i=0; i< SOCKETS.length; i++){
if(SOCKETS[i].request.connection.remoteAddress == req.ip)
SOCKETS[i].emit('AddTeamMember');
}
});
Keep in mind, if you do go down this route, you're gonna need to maintain that array when users disconnect, and if you're doing session management, that's gonna get hairy really really quick.
Good luck, let us know your results.
Yes, it is possible, you just have to attach the instance of socket.io as long as you get a request on your server.
Looking to your file start.js you just have to replace your functions as:
// Start our app!
const app = require('./app');
app.app.set('port', process.env.PORT || 7777);
const io = require('socket.io')(app.app);
const server = app.app.listen(app.app.get('port'), () => {
server.on('request', function(request, response){
request.io = io;
}
console.log(`Express running → PORT ${server.address().port}`);
});
now when you receive an event that you want to emit some message to the clients you can use your io instance from the request object.
router.get('/add-team-member', (req, res) => {
req.io.sockets.emit('addteammember', {member: 6});
//as you are doing a broadcast you just need broadcast msg
....
res.status(200)
res.end()
});
Doing that i also were able to integrate with test framework like mocha, and test the events emited too...
I did some integrations like that, and in my experience the last thing to do was emit the msg to instances in the socket.
As a good practice the very begining of middleware functions i had were doing data validation, data sanitization and cleaning data.
Here is my working example:
var app = require('../app');
var server = require('http').Server(app);
var io = require('socket.io')(server);
io.on('connection', function(client) {
client.emit('connected');
client.on('disconnect', function() {
console.log('disconnected', client.id);
});
});
server.on('request', function(request, response) {
request.io = io;
});
pg.initialize(app.config.DATABASEURL, function(err){
if(err){
throw err;
}
app.set('port', process.env.PORT || 3000);
var server1 = server.listen(app.get('port'), function(){
var host = 'localhost';
var port = server1.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
});
Your io is actually the socket object, you can emit events from this object to any specific user by -
io.to(userSocketId).emit('eventName', data);
Or you can broadcast by -
io.emit('eventName', data);
Just create require socket.io before using it :)
You can use emiter-adapter to emit data to client in other process/server. It use redis DB as backend for emitting messages.
I did something similar in the past, using namespaces.
Let's say your client connect to your server using "Frontend" as the namespace.
My solution was to create the instance of socket.io as a class in a separate file:
websockets/index.js
const socket = require('socket.io');
class websockets {
constructor(server) {
this.io = socket(server);
this.frontend = new Frontend(this.io);
this.io.use((socket, next) => {
// put here the logic to authorize your users..
// even better in a separate file :-)
next();
});
}
}
class Frontend {
constructor(io) {
this.nsp = io.of('/Frontend');
[ ... ]
}
}
module.exports = websockets;
Then in App.js
const app = require('express')();
const server = require('http').createServer(app);
const websockets = require('./websockets/index');
const WS = new websockets(server);
app.use('/', (req, res, next) => {
req.websocket = WS;
next();
}, require('./routes/index'));
[ ... ]
Finally, your routes can do:
routes/index.js
router.get('/add-team-member', (req, res) => {
req.websocket.frontend.nsp.emit('whatever', { ... });
[ ... ]
});
I am new to nodeJS, i want to fetch data from webApi using nodeJS, for instance, when i make a call to the
mydomain/getAllStudents
i want all the students data and when i do
mydomain/student/4
then i want only the data of the student with the ID=2
-challenge-
using express i can specify the route like this
var app=express();
app.get('/getAllStudents',(request,response)=>{
console.log('we are listening !!!');
}).listen(8080);
but when i try to make a call inside the callback function, i not able to get the value, my complete code
var http=require('http');
var express=require('express');
var app=express();
var options={host: '172.17.144.6',
port: 8394,
path: '/api/Masterdata/getAllStudents',
method: 'GET'}
app.get('/getAllStudents',(request,response)=>{
http.get(options,(res)=>{
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
console.log('we are listening !!!');
}).listen(8080);
what i am trying to achieve is, when i hit mydomain/getAllStudents i want to get all the data. how can i do this with nodeJS? can i do this with nodeJS statisfying my requirement?
So basically you want to expose the data through express after parsing them from another API.
You can do like this:
To make the call from node js you can use this library:
node-rest-client
var Client = require('node-rest-client').Client;
var remoteURL = "172.17.144.6:8394/api/Masterdata/getAllStudents";
var express = require('express');
var students = express.Router();
students.route('/getAllStudents')
.get(function(req, res) {
client.get(remoteURL, function (data, response) {
console.log(data);
if(response.statusCode === 200)
res.status(200).send(data);
});
});
I think what I need to do is reset Socket.io entirely after each test using socket.io.
Any ideas? Here's what I'm doing:
I have a test using Socket.IO's Client library, an angular service (github.com/chrisenytc/ng-socket), requireJS, and a local test server. When using karma's watch feature, it runs fine the first time, but fails subsequent attempts (event listeners aren't triggered, debug logging shows an 'xhr poll error').
the simple version of the test is (full version):
define(['services/serviceModule', 'angular-mocks'], function() {
describe('ILC Server Services', function() {
var socket;
beforeEach(module('ignisLibriColloqui.services', function(Config, $socketProvider) {
$socketProvider.setUrl(Config.ilcTestServerUrl);
Config.ilcServerUrl = Config.ilcTestServerUrl;
url = Config.ilcTestServerUrl;
}));
beforeEach(inject(function(ILCServerService, $socket) {
ilcServerService = ILCServerService;
socket = $socket;
}));
it('expects emit(\'ping\') to trigger on(\'pong\')', function(done) {
socket.emit('ping',{})
socket.on('pong',function(data) {
done();
});
});
});
});
and the simple server is (full version)
var httpPort = 10999;
var socketPort = 5001;
var restify = require('restify');
var socketio = require('socket.io')(socketPort);
var fs = require('fs');
var server = restify.createServer({
name: 'ilcServer Mock'
});
var io = socketio.listen(server);
server.get(/.*/, restify.serveStatic({
'directory': __dirname,
'default': './app/index.html',
'maxAge': 0
}));
io.sockets.on('connection', function(socket) {
var socketId = socket.id;
console.log('user connected');
socket.on('ping', function(data) {
console.log('ping');
socket.emit('pong', data);
});
socket.on('disconnect',function() {
console.log('disconnect');
socket.disconnect();
});
});
server.listen(httpPort, function() {
console.log('restify server listening at %s', server.url, 'socket port:', socketPort);
});
Edit
After carefully exploring the connections using the logging described by the Socket.io docs it has become clear to me that disconnecting the client is not enough. The client logs the "pong" events without firing the socket.on('pong',event), even when using an afterEach to cause socket.disconnect() and socket.io.connect(function(){done()}) to trigger, the new calls to socket.emit are not triggered.
Should I put the redis subscription event out of the io.connect callback if I want to send the data to everyone who is connected? Or is it better to put it inside the io.connect like this:
io.on('connection', function(socket){
sub.on('message',function(channel,msg){
Project.findAll({ where: {id: msg} },{raw:true}).success(function(d) {
console.log(d);
io.sockets.emit("activities",d);
})
});
});
Would there be any difference?
Node.js
var express = require('express'),
app = express(),
http = require('http').createServer(app),
io = require("socket.io").listen(http),
redis = require("redis"),
Sequelize = require('sequelize');
var pub = redis.createClient();
var sub = redis.createClient();
sub.subscribe('global');
app.get('/p/:tagId', function(req, res){
res.render('index.html')
});
sub.on('message',function(channel,msg){
Project.findAll({ where: {id: msg} },{raw:true}).success(function(d) {
console.log(d);
io.emit("activities",d);
})
});
io.on('connection', function(socket){
//** code **//
})
Can anyone show me what's wrong with the node.js's code?
Your second code sample looks correct. You don't want to put the sub.on('message', function(channel, msg) { inside the socket.io connection handler. That would add a new event handler every time someone connects.
Did you test if it is working? You need to publish something onto the channel global for the message callback to be triggered.
pub.publish('global', 'here is a message');