I want to implement a server-side push functionality using NodeJS.
For now, all I want is a server that listens to requests to a specific URL (e.g. http://localhost:8000/inputData?data=hello)
Once this request is made, I want all clients viewing client.html to run alert(hello);. What I did is the following, on the server side:
var app = require('http').createServer(handler),
io = require('socket.io').listen(app),
parser = new require('xml2json'),
fs = require('fs'),
url = require('url');
var qs = require('querystring');
app.listen(8000);
console.log('server listening on localhost:8000');
function handler(req, res) {
var url_parts = url.parse(req.url, true);
var pathname = url_parts.pathname;
switch(pathname){
case '/inputData':
var data = url_parts.query.data;
socket.emit('notifyData', data); //socket is undefined
break;
default:
fs.readFile(__dirname + '/client.html', function(err, data) {
if (err) {
console.log(err);
res.writeHead(500);
return res.end('Error loading client.html');
}
res.writeHead(200);
res.end(data);
});
}
;
}
At the client side, in client.html:
<script>
var socket = io.connect('http://localhost:8000');
socket.on('notifyData', function (data) {
alert(data);
});
</script>
I get an error saying that socket is not defined. I know that I should define it earlier but I am not sure how to do that..
Is this the right way to approach such problem? or should the emit be in a different file e.g. inputData.html? I'd rather complement the code I already have, because I might need to make a set of operations right before var data= url_parts.query.data
You have a design issue on your server as the socket variable is not defined at all. If what you're trying to do is to broadcast to all clients when any one hits that particular URL, then you can do that with:
io.sockets.emit('notifyData', data);
If you're trying to emit to a single client socket, then you will have to find a way to figure out which socket in the io.sockets namespace it is that you're trying to send to.
From the docs, it looks like you haven't initialized the server correctly.
var app = require('http').createServer(handler)
var io = require('socket.io')(app);
var fs = require('fs');
app.listen(8000);
/*...*/
io.on('connection', function (socket) {
socket.emit('notifyData', { hello: 'world' });
});
and on the client you should have
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost:8000');
socket.on('notifyData', function (data) {
alert(data);
});
</script>
Related
I've a node api(POST)in which the sensor keep on pushing the data to the MongoDB. Now I've an api(GET) which fetches the data from the database and displays on the dashboard. To get the continuous stream of data, I want to use SOCKET.IO module. But the problem is, how could I get the recently saved record from the db and show that on dashboard without reloading the page. Please have a look at my code.
SERVER.JS
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
// and manything like router, middleware, etc...
io.on('connection', function(socket){
console.log('a user connected');
});
http.listen(3000, function(){
console.log('listening on port:3000');
});
ROUTES FILE
var router = require("express").Router();
router.post("/upload/device/data/:tempId", TemplateController.AddDeviceData); //To insert the data to the DB
router.get("/view/template/device/logs/:tempUniqID", TemplateController.deviceLogs); //To get the data from DB
TEMPLATE CONTROLLER FILE
module.exports={
AddDeviceData:async function(req, res){ //Controller to post data
let err, deviceLog;
[err, deviceLog]=await
to(TemplateService.AddDeviceLogs(req.params.tempId, req.body));
if(err) return res.serverError(err.message);
if(deviceLog&&deviceLog!==false){
return res.ok(deviceLog);
}else{
res.badRequest("Sorry cannot add Device log data");
}
},
deviceLogs: async function(req, res){ //Controller to fetch data
let err, logs;
let deviceId = req.query.device;
[err, logs]=await to(TemplateService.displayLogs(req.params.tempUniqID, deviceId));
if(err) return res.serverError(err.message);
if(logs&&logs!==false){
return res.ok(logs);
}else{
res.badRequest("Sorry cannot add Device log data");
}
}
}
TEMPLATE SERVICE FILE
module.exports={
//Service to post data
AddDeviceLogs:async function(templateId, payload){
let err, deviceData;
payload.template=templateId;
const myCollection=templateId;
[err, deviceData]=await to(mongoose.connection.db.collection(myCollection).insert(payload));
if(err) TE(err.message, true);
socket.emit('data', deviceData);
return (deviceData)? deviceData.result:false;
},
//Service to get data
displayLogs:async function(tempUniqID, deviceID){
let err, respData;
var Query = (deviceID)? {"template": tempUniqID, "deviceId": deviceID}:{template: tempUniqID};
[err, respData]=await to(mongoose.connection.db.collection(tempUniqID).find(Query).sort({_id: -1}).limit(20).toArray())
if(err) {TE(err, true);}
return (respData)? respData:false;
}
}
Now I want to get most recently stored data in GET api using socket without reloading the page or without executing the GET route-api. I'm not getting which service I should use server socket-emit event in and how.
You can run node and your socket io in the same port, the following example used express and socket.io. I also created sensor code to imagine this solution:
You should use your route file like this:
ROUTES FILE
var router = require("express").Router();
router.post("/upload/device/data/:tempId", TemplateController.AddDeviceData);
router.get("/", function (req, res, next) {
res.sendFile('C:/Users/user/Desktop/data.html');
})
In MVC you will have a root file declare all works, you should delare your socket here.
Because in your codebase, every time you call AddDeviceLogs function, it will re-declare websocket, and your socket client in html file will disconnect, that's why it only work for the first time.
Then you should declare it global, for example:
server.js
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
// and manything like router, middleware, etc...
io.on('connection', function(socket){
console.log('a user connected');
});
http.listen(3000, function(){
console.log('listening on port:3000');
});
TEMPLATE SERVICE FILE
module.exports={
AddDeviceLogs:async function(templateId, payload){
let err, deviceData;
payload.template=templateId;
const myCollection=templateId;
io.emit('data', payload) // emit to all client
[err, deviceData]=await to(mongoose.connection.db.collection(myCollection).insert(payload));
if(err) TE(err.message, true);
return (deviceData)? deviceData.result:false;
}
}
data.html
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
</head>
<body>
<script>
var socket = io.connect("http://localhost:3000/",{"forceNew": true});
socket.on('data', function(data){
if (data) {
$('#deviceid').text(data.deviceId);
$('#heat').text(data.heat);
$('#humidity').text(data.humidity);
}
});
</script>
<h4>Welcome to socket.io testing program!</h4>
<div id="deviceid"></div>
<div id="heat"></div>
<div id="humidity"></div>
</body>
</html>
I am trying to do something that sounds quite simple but unfortunately I can't manage to get right.
I just need to stream an image from a file with socket.io.
I have read several links that were either outdated or incomplete and here is where I am right now (not working) :
server.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var bodyParser = require('body-parser');
var fs = require('fs');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:false}));
app.get('/',function (req,res){res.sendStatus(200)});
app.get('/image',function (req,res){res.sendFile(__dirname+'/index.html')});
http.listen(3000, function () {
console.log("server listening");
});
io.on('connection',function(socket){
console.log("socket connection");
var interval = setInterval(function(){
fs.readFile('./image.jpeg',function(err,buff){
if(err){
console.error(err);
}
else{
socket.emit('image',{image:true,buffer:buff.toString('base64')});
}
});
},1000);
socket.on('disconnect', function(){
console.log("socket deconnection");
clearInterval(interval);
});
});
index.html
<html>
<head>
<meta charset="UTF-8">
<title>IMAGE</title>
</head>
<body>
<script src="/socket.io/socket.io.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
$(function(){
var socket = io();
var ctx = document.getElementById('canvas').getContext('2d');
socket.on('image',function(info){
if(info.image){
var img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
};
img.src = 'data:image/jpeg;base64,' + info.buffer;
console.log("Image received");
}
})
});
</script>
</body>
</html>
I have also tried several other configurations close to this one for the client file but none of them worked.
I read the file at interval because in the real project the image.jpeg file will change at times.
Thank you very much if you can help !
Sending images over a socket is not very efficient. I stumbled upon something similar when I had to send big buffers of game world data to every client. Sending those packets over a socket connection was obviously not an option for me.
I solved it by storing a key that referenced to a specific file on the server, and only sending that key to the client. Then the client would send a request containing the key to a http server, which eventually would respond with the file.
Here is some shortened code just to illustrate:
On the server:
...
const files = {};
...
...
function (request, response) { // http request function
const key = request.url.parse(key);
const file = fs.readFile(files[key]);
response.send(file);
}
...
...
function sendFile (socket, file) { // function that sends and stores the key
const key = generateKey();
files[key] = file;
socket.send('key', key);
}
...
...
sendFile(socket, '/path/to/file'); // send key to socket
...
This way the big files are being downloaded over http instead of the socket.
On the client:
...
socket.on('key', function (key) {
fetch('/file/' + key, function (response) {
const file = response.blob(); // blob or whatever
});
});
...
Hope this helps you :D
I am working on realtime data visualization application using node.js, express and socket.io.
Requirement:
Have to emit the events based on the client request.
For example: If user enter the url as http://localhost:8080/pages socket.io should emit the topic pages to client and another user request for http://localhost:8080/locations socket should emit location to that particular user.
Code
var server = app.listen("8080");
var socket = require('socket.io');
var io = socket.listen(server);
var config = {};
io.on('connection', function (socket) {
config.socket = io.sockets.socket(socket.id);
socket.on('disconnect', function () {
console.log('socket.io is disconnected');
});
});
app.get('/*', function(req, res) {
var url = req.url;
var eventName = url.substring('/'.length);
//pages and locations
config.socket.volatile.emit(eventName, result);
});
Client Code:
//No problem in client code.Its working correctly.
Sample code as follows
socket.on('pages', function (result) {
console.log(result);
});
Problem:
It is emitting pages and locations to both the clients.
Any suggestion to overcome this problem.
I couldn't understand your approach on this, but because you said you're rendering different pages, It means you can serve different code, so what about doing it like this:
Server Side:
var server = app.listen("8080");
var socket = require('socket.io');
var io = socket.listen(server);
var config = {};
app.get('/pages', function(req, res) {
res.render('pages.html');
});
app.get('/locations', function(req, res) {
res.render('locations.html');
});
io.on('connection', function (socket) {
socket.on('pagesEvent', function(data){
socket.volatile.emit('pages', {your: 'data'});
});
socket.on('locationsEvent', function(data){
socket.volatile.emit('locations', {your: 'data'});
});
});
On Client side:
pages.html:
socket.on('connect', function(){
socket.emit('pagesEvent', {});
});
socket.on('pages', function(data){
// do stuff here
});
locations.html:
socket.on('connect', function(){
socket.emit('locationsEvent', {});
});
socket.on('locations', function(data){
// do stuff here
});
You are doing it wrong, WebSockets supposed to work same in both directions. Client emit event to Server, server emit back to Client/Subscribers.
The way you are doing things, seems like a way of implementing API, but for some reason you are trying to implement it with WebSockets, instead of XHR.
I'm fairly new to node.js and I've found its quite complicated separating a project into multiple files as the project grows in size. I had one large file before which served as both a file server and a Socket.IO server for a multiplayer HTML5 game. I ideally want to separate the file server, socket.IO logic (reading information from the network and writing it to a buffer with a timestamp, then emitting it to all other players), and game logic.
Using the first example from socket.io to demonstrate my problem, there are two files normally. app.js is the server and index.html is sent to the client.
app.js:
var app = require('http').createServer(handler)
, io = require('socket.io').listen(app)
, fs = require('fs')
app.listen(80);
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
index.html:
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
To separate file server and game server logic I would need the function "handler" defined in one file, I would need the anonymous function used a callback for io.sockets.on() to be in another file, and I would need yet a third file to successfully include both of these files. For now I have tried the following:
start.js:
var fileserver = require('./fileserver.js').start()
, gameserver = require('./gameserver.js').start(fileserver);
fileserver.js:
var app = require('http').createServer(handler),
fs = require('fs');
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
module.exports = {
start: function() {
app.listen(80);
return app;
}
}
gameserver:
var io = require('socket.io');
function handler(socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
}
module.exports = {
start: function(fileserver) {
io.listen(fileserver).on('connection', handler);
}
}
This seems to work (the static content is properly served and the console clearly shows a handshake with Socket.IO when the client connects) although no data is ever sent. It's as though socket.emit() and socket.on() are never actually called. I even modified handler() in gameserver.js to add console.log('User connected'); however this is never displayed.
How can I have Socket.IO in one file, a file server in another, and still expect both to operate correctly?
In socket.io 0.8, you should attach events using io.sockets.on('...'), unless you're using namespaces, you seem to be missing the sockets part:
io.listen(fileserver).sockets.on('connection', handler)
It's probably better to avoid chaining it that way (you might want to use the io object later). The way I'm doing this right now:
// sockets.js
var socketio = require('socket.io')
module.exports.listen = function(app){
io = socketio.listen(app)
users = io.of('/users')
users.on('connection', function(socket){
socket.on ...
})
return io
}
Then after creating the server app:
// main.js
var io = require('./lib/sockets').listen(app)
i would do something like this.
app.js
var app = require('http').createServer(handler),
sockets = require('./sockets'),
fs = require('fs');
function handler (req, res) {
fs.readFile(__dirname + '/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
sockets.startSocketServer(app);
app.listen(80);
and sockets.js
var socketio = require('socket.io'),
io, clients = {};
module.exports = {
startSocketServer: function (app) {
io = socketio.listen(app);
// configure
io.configure('development', function () {
//io.set('transports', ['websocket', 'xhr-polling']);
//io.enable('log');
});
io.configure('production', function () {
io.enable('browser client minification'); // send minified client
io.enable('browser client etag'); // apply etag caching logic based on version number
io.set('log level', 1); // reduce logging
io.set('transports', [ // enable all transports (optional if you want flashsocket)
'websocket'
, 'flashsocket'
, 'htmlfile'
, 'xhr-polling'
, 'jsonp-polling'
]);
});
//
io.sockets.on('connection', function (socket) {
console.log("new connection: " + socket.id);
socket.on('disconnect', function () {
console.log("device disconnected");
});
socket.on('connect_device', function (data, fn) {
console.log("data from connected device: " + data);
for (var col in data) {
console.log(col + " => " + data[col]);
}
});
});
}
};
i just copy&pasted some of my old code - don't really know what changed in the last versions of socket.io, but this is more about the structure than the actual code.
and i would only use 2 files for your purposes, not 3.
when you think about splitting it up further, maybe one other file for different routes ...
hope this helps.
I have had a crack at this as well and I am fairly happy with the result. Check out https://github.com/hackify/hackify-server for source code.
I've another solution. You can use require.js creating a module and pass "app" as an argument. Within the module you can start socket.io and organize your sockets.
app.js:
var requirejs = require('requirejs');
requirejs.config({
baseUrl: './',
nodeRequire: require
});
requirejs(['sockets'], function(sockets) {
var app = require('http').createServer()
, fs = require('fs')
, io = sockets(app);
// do something
// add more sockets here using "io" resource
});
In your socket.js module you can do something like this:
define(['socket.io'], function(socket){
return function(app){
var server = app.listen(3000)
, io = socket.listen(server);
io.sockets.on('connection', function (socket) {
console.log('connected to socket');
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
// more more more
});
return io;
}
});
I hope help you with my contribution.
I'm trying out Websockets/Node.js/Socket.io/Express for the first time and I'm trying to create a simple chat program. Everything runs fine and I see both clients in my node termial.
But when I try to execute my socket.send(), I get an error in Firefox (socket.send is not a function). It doesn't complain about socket.connect() so I know the socket.io.js is loaded.
Here is my server code:
var sys = require('util');
var express = require('express');
var io = require('socket.io');
var app = express.createServer();
app.listen(8080);
app.use(express.static(__dirname));
app.get('/', function (req, res) {
res.render('index.html', {
title: 'Chat'
});
});
var socket = io.listen(app);
socket.on('connection', function (client) {
client.on('message', function (message) {
console.log("Message: " + JSON.stringify(data));
socket.broadcast(message);
});
client.on('disconnect', function () {});
});
My client code:
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
var socket = new io.Socket("http://localhost:8080");
socket.connect();
Then I do some code to get the chat message and send it.
socket.send(JSON.stringify(values));
Explanations
You haven't initialized Socket.io correctly on the server-side and client-side.
Client Side
new io.Socket("http://localhost:8080"); doesn't give you the object that you want, you need new io.connect("http://localhost:8080");.
You need to wait until the client is connected to the server before sending a message.
Server side
socket is the object send back by Socket.IO, you need to use socket.sockets to have access to on.
To broadcast a message, you need to use the client object like this: client.broadcast.send()
The variable data doesn't exist on your broadcast. You probably mean message.
Solution
Server
var sys = require('util'),
express = require('express'),
io = require('socket.io'),
app = express.createServer();
app.listen(8080);
app.use(express.static(__dirname));
app.get('/', function (req, res) {
res.render('index.html', {
title: 'Chat'
});
});
var io = io.listen(app);
io.sockets.on('connection', function (client) {
client.on('message', function (message) {
console.log("Message: " + JSON.stringify(message));
client.broadcast.send(message);
});
client.on('disconnect', function () {});
});
Client
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
<script>
var socket = new io.connect("http://localhost:8080"),
connected = false;
socket.on('connect', function () {
connected = true;
});
// Use this in your chat function.
if (connected) {
socket.send(JSON.stringify(values));
}
</script>
socket.broadcast(message); should be io.sockets.emit('key', message);
when you use the socket object passed in threw the connect event your only emitting information to that client, to emit to all clients you have to use io.sockets.emit().
also with socket.send(JSON.stringify(values)); I think you want to do socket.emit(namespace, data);
see my connection file from one of my projects here: https://github.com/AdminSpot/HangoutCanopy/blob/master/javascripts/connection.js
You have to wait for socket.io to connect on the client side
var socket = new io.Socket("http://localhost:8080");
socket.connect();
socket.on('connect', function() {
socket.emit('event', data);
});