I have a node.js server communicating to a client web page, sending it message. This is working great based on the many tutorials and searching stack overflow :)
Where I am having an issue is when I attempt to startup a separate socket connection to a 3rd party cmd line executable instance runs as a socket server. The 3rd party executable does not adhere to the socket.io namespace/room type of events, so I read that socket.io-events may help where instead of:
socket.on('some key', function(){/**do stuff*/}); I could:
eventRouter.on('*', function(){/*do stuff*/});
For this communication, I am assuming I need to use socket.io-client on the node.js side in order to talk to the cmd executable, but I am getting exceptions trying to do a socket2.use(router); where socket2 is my socket.io-client and router is the socket.io-events object.
All runs on localhost, node to web page is port 8001 and node.js to executable is on port 8002. Please pardon the code, for I have been trying to get this to work for a few days and is a bit ugly now.
The cmd executable to execute and its arguments I have coming from the web page which works. I am able to start the exe. The EXE expects a ACK on each message sent, thus why you see the code emitting it back.
I have a interval where I set and update an element on the web page. I have another element that I set messages (msg).
var http = require('http');
var url = require('url');
var fs = require('fs');
var server;
server = http.createServer(function(req, res){
// your normal server code
var path = url.parse(req.url).pathname;
switch (path){
case '/':
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<h1>Hello! Try the Test page </h1>');
res.end();
break;
case '/socket.html':
fs.readFile(__dirname + path, function(err, data){
if (err){
return send404(res);
}
res.writeHead(200, {'Content-Type': path == 'json.js' ? 'text/javascript' : 'text/html'});
res.write(data, 'utf8');
res.end();
});
break;
default: send404(res);
}
}),
send404 = function(res){
res.writeHead(404);
res.write('404');
res.end();
};
server.listen(8001);
var str = "ack0";
var bytes = [];
for (var i = 0; i < str.length; ++i) {
bytes.push(str.charCodeAt(i));
}
// use socket.io
var io = require('socket.io').listen(server);
// define interactions with client
io.sockets.on('connection', function(socket){
//send data to client
setInterval(function(){
socket.emit('date', {'date': new Date()});
}, 1000);
//recieve client data
socket.on('client_data', function(data){
var spawn = require('child_process').spawn;
console.log('pre-spawned');
spawn(data.cmd, data.args, {});
setTimeout(function() {
console.log('hello world!');
}, 1000);
var aptIO = require('socket.io-client');
var router = require('socket.io-events')();
var socket2 = aptIO.connect('localhost:8002', {reconnect: true});
router.on('connection', function(s){
//send data to client
console.log('apt');
router.on('*', function(sock, args, next){
var name = args.shift(), msg = args.shift();
console.log(name + " " + JSON.stringify(msg));
sock.emit(bytes);
io.sockets.emit('msg', {'msg': JSON.stringify(msg)})
next();
});
s.emit(bytes);
});
console.log('spawned');
// getting runtime exceptions here...have tried various things...
socket2.use(router);
});
});
With the help from JGreenwell, I was able to resolve me issue.
I ended up having the node server communicate to the client html page via socket.io connection for messages. The node server would launch the cmd line executable providing it the port to connect to which is different from the socket.io port used.
Once started, the executable would communicate with the server via the net module. The server would just pass the information on to the socket.io connection. the js in the html page knows how to parse the message in order to increment the progress bar and list the messages in a text area control.
I took it even further by having the messages be broadcast-ed to multiple clients on the socket.io connection.
Related
I'm trying to setup some socket.io communications, the communication between my server (app.js)(runs on a raspberry pi) and a website(public/index.html) works fine. Now I want to expand it so when my app.js receives a call from index.html it emits it further to another node.js script(bed.js) that will run on another raspberry pi. I tried to use the npm module socket.io-client, but this can only receive apparently
!edit! problem has narrowed down to the setrgb part, there it won't emit.
!edit 2! when i receive setRGB, i emit setRGBclient, but that can only be received in bed.js, not in index.html, there lays my problem, i need to share the connections or force it to another connection, no clue how i fix it though
APP.JS:
let http = require('http').createServer(handler); //require http server, and create server with function handler()
let fs = require('fs'); //require filesystem module
let io = require('socket.io')(http) //require socket.io module and pass the http object (server)
let delay = require('delay');
console.log('Define each color from RGB Strip light.');
http.listen(8080); //listen to port 8080
function handler (req, res) { //create server
fs.readFile(__dirname + '/public/index.html', function(err, data) { //read file index.html in public folder
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
return res.end("404 Not Found");
}
res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
res.write(data); //write data from index.html
return res.end();
});
}
io.sockets.on('connection', function (socket) {// WebSocket Connection
socket.on("test", function(){
console.log("sampletext");
});
socket.on("setRGB", function(data){
socket.emit("setRGBClient", data);
console.log(data);
console.log("test");
});
});
bed.js:
let socket = require('socket.io-client')('http://localhost:8080');
let lightstate = false;
let stayOff = false;
let fadeState = false;
console.log("check");
socket.emit("test");
socket.on("setRGBClient" ,function(data) {
console.log(data);
});
I can just broadcast setRGBClient.
socket.broadcast.emit("setRGBClient", data);
I guess this is a learning exercise. Otherwise I’d caution against socket.io for such applications.
However I can only see the subscription for ‘setRGB’ not the emit-part.
Currently, I have a lot of linux based clients downloading firmware updates from my webserver.
After the client has successfully downloaded the firmware file, my server needs to execute a few scripts, which logs in to the client and performs some tasks.
Is there a way for a node server to keep track of the clients download progress, so I can execute the needed scripts once the file has been downloaded?
Ok so I will try.
If you serve your Firmware as static files through Apache/Nginx and direct url call. You don't get the progress inside your NodeJS script.
If you serve your files via stream inside a express controller you can listen to the progress. Look at this answer here https://stackoverflow.com/a/42273080/3168392
You will have to use a socket connection to make sure the node server gets update from the client of the progress of the file being downloaded.
Something like this
CLIENT_CODE
var socket = io('http://localhost');
socket.on('connect', function(){});
socket.on('data_reciving', parts.pop(),function(percentage){
if(parse_data(percentage) === 100){
client.socket.emit('downloadCompleted', {fileName:'test'});
}else{
//do nothing
}
});
SERVER_CODE:
sockets.on('connection', function (socket) {
//listen to the event from client
socket.on('downloadCompleted', function (data) {
connect_to_client();
do_some_operation();
socket.emit('ALLDONE',{some_data});
});
});
I guess this helps ,you can use this post for reference.
If you just want to run some code when a download has finished, you can use on-finished:
const onFinished = require('on-finished');
app.use((req, res, next) => {
onFinished(res, (err, res) => {
...log some data, perform some housekeeping, etc...
});
next();
});
As is, it will attach a "finished" listener to all responses, which is probably not what you want. Since this is plain Express middleware, you can attach it to specific routes instead (but how depends on how exactly the files are being served).
I found some code that seems to fit my needs.
With the code below, I can detect both the progress of a user's download from the server-side, and fire an event once the file transfer completes.
var http = require("http");
var fs = require("fs");
var filename = "./large-file";
var serv = http.createServer(function (req, res) {
var sent = 0;
var lastChunkSize = 0;
var stat = fs.statSync(filename);
res.setHeader('Content-disposition', 'attachment; filename=large-file.iso');
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader('Keep-Alive', 'timeout=5, max=100');
res.writeHeader(200, {"Content-Length": stat.size});
var fReadStream = fs.createReadStream(filename, { highWaterMark: 128 * 1024 });
fReadStream.on('data', function (chunk) {
if(!res.write(chunk)){
fReadStream.pause();
lastChunkSize = chunk.length;
console.log('Sent', sent, 'of', stat.size);
}
});
fReadStream.on('end', function () {
console.log('Transfer complete.');
res.end();
});
res.on("drain", function () {
sent += lastChunkSize;
fReadStream.resume();
});
});
serv.listen(3001);
I tried to go with the tutorial of this link http://web-engineering.info/node/57
But when I execute node server.js and open the browser http://localhost:3434 it says upgrade required. The server.js file is:
var WebSocketServer = require('ws').Server,
wss = new WebSocketServer({port: 3434});
wss.broadcast = function (data) {
var i = 0, n = this.clients ? this.clients.length : 0, client = null;
for (; i < n; i++) {
client = this.clients[i];
if (client.readyState === client.OPEN) {
client.send(data);
}
else console.error('Error: the client state is ' + client.readyState);
}
};
wss.on('connection', function (ws) {
ws.on('message', function (message) {
wss.broadcast(message);
});
});
you have to open your index.html in browser not http://127.0.0.1:3434
its a websocket server. You are trying to make a http connection to a websocket server.
Most probably your server socket at localhost:3434 don't have support for websocket, so the connection is terminated by the client browser.
This error indicates that on localhost:3434 you are running a HTTP server which is incapable to "upgrade" to websocket.
(Since both simple http and websocket begins with a simple http request. In that http request the client ask the server to switch to websocket protocol.)
Should you add this ?
var ws = require('websocket.io')
, server = new ws.Server()
// … somewhere in your http server code
server.on('upgrade', function (req, socket, head) {
server.handleUpgrade(req, socket, head);
});
ref https://www.npmjs.com/package/websocket.io#passing-in-requests
Check this SO too What is an http upgrade?
I tried intercepting the http request
var ws = require('websocket.io')
, http = require('http').createServer().listen(3000)
, server = ws.attach(http)
server.on('connection', function (socket) {
socket.on('message', function () { });
socket.on('close', function () { });
});
https://www.npmjs.com/package/websocket.io#passing-in-requests
For me using the npmjs documentation, I went copy-paste rogue.
Then, debugging my client-side request I noted that the URL parameter I was using was not a string as expected.
But it was shark_s's answer that helped remind me to go look at the console and interpret the error again- so thanks.
I am new to Raspberry Pi developing and I am involved in a project in which we need to take Bluetooth readings sent from different sensors (e.g. temperature sensors, wearable health sensors, etc.) previously paired with our Pi, and send them to a server using Pi as a gateway.
How can I access the port receiving the Bluetooth readings? From there on I suppose it's as simple as writing a script that takes the important information, like device ID and the measurement value, places them inside a formatted message and sends it to the server, but again I need advice.
Any help, even providing links to forums or similar sites, will be very appreciated.
UPDATE:
I am now able to read from every handle in the BLE device and parse the data into a JSON file using a bash script. However, I don't know how to tell Node that I need to update the information every 5 seconds. Here's the code I'm using:
// required modules
var http = require('http');
var fs = require('fs');
var exec = require('child_process').exec;
// for executing bash/shell scripts
function execute(command, callback){
exec(command, function(error, stdout, stderr){ callback(stdout); });
};
// executes script that updates JSON file with new readings
module.exports.getJSONFile = function(callback){
execute("./Scripts/BLEreadingsJSON.sh");
};
// creates HTTP server
http.createServer(function(request, response){
response.writeHead(200,{
'Content-Type': 'text/json' });
setInterval(function(){
var contents = fs.readFile('Scripts/nodeJS/readings.json', function(err, contents){
response.write(contents);
response.end();
});
}, 5*1000);
}).listen(8080); // listen for connections on this port
console.log('Listening to port 8080...');
When I execute this I get the following error:
pi#raspberrypi ~/Scripts $ node nodeJS/sendFileToServer.js
Listening to port 8080...
http.js:851
throw new TypeError('first argument must be a string or Buffer');
^
TypeError: first argument must be a string or Buffer
at ServerResponse.OutgoingMessage.write (http.js:851:11)
at /home/pi/Scripts/nodeJS/sendFileToServer.js:22:13
at fs.js:207:20
at Object.oncomplete (fs.js:107:15)
I suppose this happens because it is trying to access the file before it has been created. How can I do this using a callback?
I got it to work! For those still interested, I ended up using socket.io. I have a script that updates the data with new BLE readings. This node.js code listens for those updates and sends the file to the client:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var fs = require('fs')
app.get('/', function(req, res){
var index = "index.html";
res.sendFile(index, {root: "absolute_path_to_index"});
});
var text;
fs.readFile('absolute_path_to_file', function(err, data){
text = data;
});
io.on('connection', function(socket){
console.log("User connected");
socket.emit('news', text);
socket.on('disconnect', function(){
console.log("User disconnected");
});
// this line waits for changes in the file and uploads the new data
fs.watchFile('absolute_path_to_file', function(curr, prev){
fs.readFile('absolute_path_to_file', function(err, data){
socket.emit('news', data.toString());
});
});
});
http.listen(4321, function(){
console.log("Listening on port 4321...");
});
A few months ago (August 2011) I successfully created a node.js websockets server which connected to Twitter's Streaming API using basic HTTP user/password authentication. To do this, I employed Andre Goncalves' twitter-nodejs-websocket library.
Since creating this working implementation, Twitter has eliminated access to the streaming API via basic HTTP auth, in favor of OAuth. After this shift, I utilized Ciaran Jessup's node-oauth library, which has successfully given me access to the Streaming API again (when I run the server I am successfully outputting the tweets via console.log(tweet) -- see below ).
The problem now is that my websockets server is no longer working. When I run my server from the command line and hit the client web page from the browser, the websocket "onclose" event is immediately fired.
I've tried everything I can think of to get this working. Any help would be very greatly appreciated!
server.js
var sys = require('sys'),
http = require('http'),
ws = require("./vendor/ws"),
base64 = require('./vendor/base64'),
arrays = require('./vendor/arrays')
var OAuth = require('./oauth/oauth').OAuth;
var consumer_key = '[...]'; //removed for obvious security reasons...
var consumer_secret = '[...]';
var access_token = '[...]';
var access_token_secret = '[...]';
oa = new OAuth("https://twitter.com/oauth/request_token",
"https://twitter.com/oauth/access_token",
consumer_key,
consumer_secret,
"1.0A",
null,
"HMAC-SHA1");
var request = oa.get("https://stream.twitter.com/1/statuses/filter.json?track=google", access_token, access_token_secret );
// Response Parsing -------------------------------------------- //
var clients = [];
var message = "";
request.addListener('response', function (response) {
response.setEncoding('utf8');
response.addListener("data", function (chunk) {
message += chunk;
var newlineIndex = message.indexOf('\r');
// response should not be sent until message includes '\r'.
// Look at the section titled "Parsing Responses" in Twitter's documentation.
if (newlineIndex !== -1) {
var tweet = message.slice(0, newlineIndex);
clients.forEach(function(client){
// Send response to all connected clients
client.write(tweet);
});
// this just tests if we are receiving tweets -- we are: terminal successfully outputs stream //
var pt = JSON.parse(tweet);
console.log('tweet: ' + pt.text);
}
message = message.slice(newlineIndex + 1);
});
});
request.end();
// Websocket TCP server
ws.createServer(function(websocket){
clients.push(websocket);
websocket.addListener("connect", function(resource){
// emitted after handshake
sys.debug("connect: " + resource);
}).addListener("close", function(){
// emitted when server or client closes connection
clients.remove(websocket);
sys.debug("close");
});
}).listen(8081);
// This basic http server works, so we know this port is open.
//
// var http = require('http');
// http.createServer(function (req, res) {
// res.writeHead(200, {'Content-Type': 'text/plain'});
// res.end('Hello World\n');
// }).listen(8081);
client code
<script type="text/javascript" charset="utf-8">
ws = new WebSocket("ws://ec2-67-202-6-10.compute-1.amazonaws.com:8081");
ws.onmessage = function(evt) {
console.log('tweet')
};
ws.onclose = function() {
console.log("socket closed");
};
ws.onopen = function() {
console.log("connected...");
};
</script>
Maybe you updated the browser? The websocket spec is chaning rapidly. Anyway, I'd propose using socket.io because it will even still work with fallbacks if the browser is outdated or websockets got incompatible again or a crappy proxy is preventing websockets from working.
Have a look at this sample event stream (it uses server sent events) from a twitter stream:
https://github.com/chovy/nodejs-stream