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
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'm really new to node.js and I'm starting to learn it. Actually I'm trying to develop a simple chat application just for learning purpose.
I'm also using socket.io package.
That's what I've done:
1) index.js
var express = require('express');
var app = express();
var path = require('path');
var http = require('http').Server(app);
var io = require('socket.io')(http);
var users = {};
var socket = io.listen(3001);
var dateFormat = require('dateformat');
app.use('/public',express.static(path.join(__dirname, '/public')));
app.get('/',function(req,res){
res.sendFile(__dirname+'/index.html')
});
//CHAT
socket.on('connection',function(client){
client.on('join',function(name){
var date = dateFormat(new Date(),"HH:MM:ss dd-mm-yyyy");
var hour = dateFormat(new Date(),"HH:MM:ss");
users[client.id] = {"name":name,"id":client.id,"status":1,"joinDate":date};
client.emit("update", "You have connected to the server.");
var msg ={'msg':"Client: "+name+" joined the channel", 'user':name,'hour':hour,'fromServer':1};
io.sockets.emit('update', msg);
});
client.on('request-list',function(){
client.emit('user-list',users);
});
client.on('chat',function(msg){
//socket.broadcast.emit('update',msg);
//THIS DOESN'T WORK
socket.emit('update', msg);
});
client.on('disconnect',function(){
delete users[client.id];
if(Object.keys(users).length>0){
console.log(Object.keys(users).length);
} else{
console.log("NESSUNO");
}
});
});
//END CHAT
http.listen(3000,function(){
console.log('listening on 3000');
});
2) main.js
$(document).ready(function(){
var socket = io.connect('127.0.0.1:3001');
var chatStarted = false;
$('[data-type="loginMask"] [data-component="login-button"]').off().on('click',function(){
socket.emit('join',$('[data-component="login-nickname"]').val());
localStorage.setItem('user',$('[data-component="login-nickname"]').val());
});
socket.on('update',function(msg){
if(!chatStarted){
$('[data-type="loginMask"]').hide();
$('[data-type="chatMask"]').show();
socket.emit('request-list');
chatStarted=true;
}else{
$('[data-component="chat"] [data-component="chat-messages"]').append(formatMessage(msg));
}
});
socket.on('user-list',function(list){
var html ='';
$.each(list,function(k,item){
html += '<div data-component="userlist" data-id="'+item.id+'">';
html += '<span data-component="userlist-status">'+getStatus(item.status)+'</span>';
html += '<span data-component="userlist-user">'+item.name+'</span>';
html += '<span data-component="userlist-caret"><i class="fa fa-caret-right" aria-hidden="true"></i></span>';
html += '</div>';
});
$('[data-type="userlist"]').append(html).promise().done(function(){
});
});
$('[data-component="chat-text"]').off().on('keydown',function(e){
if(e.which==13){
$('[data-component="chat-send"]').trigger('click');
}
});
$('[data-component="chat-send"]').off().on('click',function(){
var msgToSend = {'msg':$('[data-component="chat-text"]').val(),'user':localStorage.getItem('user'),'fromServer':0};
$('[data-component="chat"] [data-component="chat-messages"]').append(formatMessage(msgToSend));
//socket.broadcast.emit('chat',msg);
//THIS DOESN'T WORK
socket.emit('chat',msgToSend);
$('[data-component="chat-text"]').empty();
});
});
I tried to use the broadcast function in two parts of the code (where there's the comment THIS DOESN'T WORK :), but I always receive undefined in both parts. If I leave the code as it's now the message will be sent to all clients included the sender, I want to send the message to everyone except the client.
As I said I'm new to node and socket.io as well, so I can't really understad what is the problem.
In the socket.io chat example here they made use of broadcast, so I guess it's a method of the socket.
Thank you in advance for your help
To broadcast to all connected clients from the server, you use:
io.emit(...)
To broadcast to all connected clients except one use:
socket.broadcast.emit(...)
where socket is the one you do not want to broadcast to.
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>
I'm playing around with nodejs and specifically looking at nowjs
I've got now up and running on a server running node and I have a separate web server. I successfully have the node server returning the client script and I reference this on the web server. This returns a 200 response code and all looks well. However I get javascript errors telling me that 'now' is undefined. As far as I understand it the 'now' variable should be available via the client script but this doesn't seem to be the case. Does anyone know if this set-up is possible? So the set-up is similar to the below pseudo code
//Server 1 node.com
if(request.url === '/nowjs/now.js'){
var file = 'path_to_clientlib/now.js';
fs.readFile(file, function(e, data) {
if (e) {
throw e;
}
response.writeHead(200,{'Content-Type': 'application/javascript'});
response.end(data);
}
and server.com
<script src="/jquery.js"></script>
<script src="http://node.com/nowjs/now.js"></script> <!-- This is returned properly -->
<script>
$(document).ready(function(){
now.receiveMessage = function(name, message){
$("#messages").append("<br>" + name + ": " + message);
}
$("#send-button").click(function(){
now.distributeMessage($("#text-input").val());
$("#text-input").val("");
});
now.name = prompt("What's your name?", "");
});
</script>
<div id="messages"></div>
<input type="text" id="text-input">
<input type="button" value="Send" id="send-button">
Straight away the console just returns 'now' is not defined
First of all there are enough modules that provide static file serving support, but if you want to manually serve a file I would do it like this...
var mime = require('mime') // Get mime type based on file extension. use "npm install mime"
, util = require('util')
, fs = require('fs');
function serveFile(filename, res) {
var filePath = process.cwd() + filename;
var stat = fs.statSync(filePath);
res.writeHead(200, {
'Content-Type':mime.lookup(filePath),
'Content-Length':stat.size
});
var readStream = fs.createReadStream(filePath);
return util.pump(readStream, res);
}
// Your code...
Or check out the module node-static on NPM or Github
About how to use NowJS (from the docs)
On the server
var httpServer = require('http').createServer(function(req, response){
// See code above how to serve static files...
});
httpServer.listen(8080);
var nowjs = require("now");
var everyone = nowjs.initialize(httpServer);
everyone.now.logStuff = function(msg){
console.log(msg);
}
On the client
<script type="text/javascript" src="http://localhost:8080/nowjs/now.js"></script>
<script type="text/javascript">
now.ready(function(){
// "Hello World!" will print on server
now.logStuff("Hello World!");
});
</script>
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);
});