Socket.io server crash after client reload/leave page - javascript

I have a strange situation. I'm writing a web application for streaming rtsp from ip cameras. Everything is fine except when client that watches the stream reload or leave page the server crashes(not always).
Here's the crash output:
zlib.js:499
var newReq = self._handle.write(flushFlag,
^
TypeError: Cannot read property 'write' of null
at Zlib.callback (zlib.js:499:33)
Server side:
const app = require('express')();
const child_process = require('child_process');
const server = require('http').Server(app);
const io = require('socket.io')(server);
io.of('/stream').on('connection', function(socket) {
let room;
socket.on('startStream', function(camuid) {
room = camuid;
let stream;
socket.join(room);
if (processes.indexOf(camuid) == -1) {
processes.push(camuid);
logic.Onvif.getStreamLink(camuid).then(rtsp_link => {
stream = child_process.spawn("ffmpeg", [
'-y',
'-re',
"-i", rtsp_link,
'-f', 'mjpeg',
'-'
], {
detached: false
});
stream.stdout.on('data', (data) => {
const frame = Object.values(new Uint8Array(data));
socket.nsp.to(room).emit('camfeed', frame);
});
}).catch((e) => {
socket.nsp.to(room).emit('streamError', true);
});
}
});
On the client side:
const io = require('socket.io-client');
socket = io.connect(`http://${location.hostname}:8088/stream`);
socket.on('connect', function() {
socket.emit('startStream', $this.$props.id);
});
socket.on('camfeed', function (data) {
//Here the data is displayed on canvas
});
I tried to find out what part of code in the server causes that behaviour with no luck so far.
I tried put functions, listeners and emitters in try{}catch and console.log every step to see where it stops but the output is not always the same.
So I started looking for a solution and found a github issue saying that the zlib is responsible for compressing data to gzip before sending and the error is caused by trying to process data that doesn't exist. On the other hand I don't have zlib installed as a dependency and as far as I know zlib package isn't used anymore because node has this functionality built-in. I searched the node_modules and found minizlib package but no clue whose is that dependency.

I believe you need to use
socket.on("disconnect",function(){
//delete all user data here
})
so it stops using the objects you created

Related

invalid local file header signature error in yauzl

I am working on an unzip feature which requires to read zip file from a remote destination, unzip the contents and then create those files over the network to the destination. I am using yauzl with the RandomAccessReader method to read the incoming stream and publish events.
const zipQueue = [];
const yauzl = new Yauzl(reader);
const zipfile = yauzl.fromRandomAccessReader(this, this.contentLength, { lazyEntries: true, autoclose: false});
zipfile.on('entry', function(entry){
zipQueue.push(entry);
zipfile.readEntry();
}).on('end', function() {
for(const entry of zipQueue) {
if(/\/$/.test(entry.fileName)) {
this.emit('directory');
} else {
const readStream = zipfile.openReadStream(entry);
this.emit('file');
}
}
})
I am getting the zipfile and the entry objects in proper format, but when I try to open the readStream with zipfile.openReadStream() it fails invalid local file header signature: 0x8074b50 after 30 odd entries. I suspect it has something to do with race condition. Is there any other npm package which offers similar solution?
In my case, I was getting that error because I was piping it from another stream which wasn't done writing, so it was reading an incomplete file.
By listening to the stream 'end' event and only doing it after, it started working.
stream.on('end', () => {
// Unzip here
});

Getting variables From a config file to the browser

I have a rather complex setup which requires the web browser local storage has the computer's name populated in order for the application to work properly. In order to do this I read from a configuration file:
kiosk-name: Mort
I read the config file when I start my node.js web server:
var filesys = require('fs');
var os = require('os');
filesys.readFile(project_path + '/kiosk.cfg', 'utf8', function(err, data) {
var kioskname;
if (err) {
//console.log(err);
kioskname = os.hostname();
} else {
var configArray = data.split(':');
if('' != configArray[1]) {
kioskname = configArray[1];
} else {
kioskname = os.hostname();
}
}
});
All of this works as designed, using the computer's os.hostname() as a default when the config file is not populated.
The client side features a base page (index.html) which loads a default page (default.html) into an iframe. Based on a websocket messaging system the default page gets replaced by another page from a remote IP. In an older version of the system (prior to implementing a config file) we were able to set the local storage element with the following code:
var win = document.getElementsByTagName('iframe')[0].contentWindow;
win.postMessage(JSON.stringify({key: 'kiosk-name', data: kioskName}), "*");
We identify the iframe when the websocket message is received and then send a post message containing a JSON string to set the local storage element. In this case kioskName is a variable containing a hard-coded value.
The Problem
Now that we wish to read values from a config file we need a way to pass kioskname out to the client-side JavaScript so we can set the local storage element in the iframe.
I attempted putting the file reading function in an export wrapper:
(function(exports){
// file reading code here
return kioskname;
})(typeof exports === 'undefined' ? this['kioskname']={} : exports);
I got an error:
Uncaught ReferenceError: require is not defined
Placing a static value in the export function (with out the require's allows the export function to work properly, but doesn't allow me to read the config file which requires both the os and fs modules.
How do I get the value returned from the config file to a place where I can use it on the client-side to set a local storage element?
This is a creative solution which may not be suitable for every case as it involves utilizing a websocket between the Node.js web server and the client.
Websocket setup to send to client (assumes webserver at 'node_server':
var io = require('socket.io').listen(node_server); // 'attaches' socket.io to this web server
io.sockets.on('connection', function (socket) {
socket.emit('message', 'socket.io connected'); // output a connection message
// receive JSON message and send to the websocket
socket.on('message', function (data) {
var address = node_server.address();
var client = dgram.createSocket("udp4");
var message = new Buffer(data);
// out of the airlock!
client.send(message, 0, message.length, address.port, address.address, function(err, bytes) {
client.close();
});
});
});
Read the config file, then parse and send a message to the socket (done on the server-side):
filesys.readFile(project_path + '/kiosk.cfg', 'utf8', function(err, data) {
var kioskname;
if (err) {
//console.log(err);
kioskname = os.hostname();
} else {
var configArray = data.split(':');
if('' != configArray[1]) {
kioskname = configArray[1];
} else {
kioskname = os.hostname();
}
}
// create JSON string for transmission
KioskName = JSON.stringify({'config':{'kiosk-name': kioskname}});
var send_KioskName = setInterval(function(){ // could be a setTimeout for a one time send
io.sockets.emit('message', KioskName.toString()); // send config file data to browser via socket
}, 30000);
});
NOTE this can be expanded to send multiple pieces of data via JSON to the client should the need arise. A couple of small edits are all that is needed to setup a more detailed JSON object.
Receive the socket message on the client side (this code is loaded by the client), then parse. The resulting object is added to the namespace for this application, making the object available to multiple scripts when required.
CAUTION: You should only use this methodology for objects which do not interfere with objects you may create or destroy in your scripts along the way.
// make sure a string is JSON before using
function isJSON(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
// set up object 'array's
var kioskname = {};
// connect a socket to listen for incoming messages from the Big Giant Head
var socket = io();
socket.on('message', function (data) {
if(isJSON(data)) {
// parse the json
var json = $.parseJSON(data);
// determine how to use this JSON object, multiple objects may be sent
if('config' == Object.keys(json)[0]) {
/*
* send config data where needed - future proofed, just add cases
* and namespaced objects where required
*/
kioskname['name'] = json.config['kiosk-name'];
}
}
});
// attach objects to namespace
window.KIOSK.kioskname = kioskname;
Now we can use the object to set local storage. In our case we post a message to the app's server and it responds with localStorage.setItem():
Post the message:
var currentFrame = document.getElementsByTagName('iframe')[0].contentWindow;
currentFrame.postMessage(JSON.stringify({key: 'user-name', data: KIOSK.kioskname.name}), "*");
By opening a socket and using the JSON string passed through the socket to populate a namespaced object we are able to use server-side information from a configuration file in our application's client.

Web Audio- streaming file from server to client

I'm trying to stream audio from a server containing an audio file to a client using BinaryJS. My code was inspired by the code in this question: Playing PCM stream from Web Audio API on Node.js
Here's what my server code looks like:
// create a BinaryServer using BinaryJS
var BinaryServer = require('binaryjs').BinaryServer;
// gotta be able to access the filesystem
var fs = require('fs');
// create our server listening on a specific port
var server = BinaryServer({port: 8080});
// do this when a client makes a request
server.on('connection', function(client){
// get the audio file
var file = fs.createReadStream(__dirname + '/JDillaLife.mp3');
// convert to int16
var len = file.length;
var buf = new Int16Array(len);
while(len--){
buf[len] = data[len]*0xFFFF;
}
var Stream = client.send();
Stream.write(buf.buffer);
console.log("Server contacted and file sent");
});
console.log('Server running on port 8080');
And my client code:
var Speaker = require('speaker');
var speaker = new Speaker({
channels: 2,
bitDepth: 32,
sampleRate: 44100,
signed: true
});
var BinaryClient = require('binaryjs').BinaryClient;
var client = BinaryClient('http://localhost:8080');
client.on('stream', function(stream, meta){
stream.on('data', function(data){
speaker.write(data);
});
});
This is a very rough draft and I'm almost certain it won't play nicely right away, but right now it's throwing an error when I run that seems to take issue with the line var buf = new Int16Array(len);, and I'm not sure why this is happening. It says there's a "type error," but I'm not sure why this would happen when assigning a new object to an empty variable. I'm new to JS (and the whole untyped languages thing in general) so is this an issue with what I'm assigning?
I think the issue here is that you're accessing file.length, while file is a Stream object which I don't think have a length property. So what you're doing is basically saying
new Int16Array(undefined);
and hence the type error.
fs.createReadStream is documented here; https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options
You can use the Stream object to read data in chunks using stream.read(256), as documented here; https://nodejs.org/api/stream.html#stream_readable_read_size
Hopefully that gives you enough pointers to proceed!

Play Audio from client when message is recieved from socket.io - node.js

I've searched for a while for a solution to this problem, but haven't found much.
My goal is to recieve a message from a udp client, which the server recieves and forwards to a web client, which plays an audio clip each time a message is recieved. However, for some reason the audio will not play. If I open the page straight from my directory, the audio can be played, but if I try and access it through localhost it fails to load. Does anyone know of a solution?
Here is the client side javascript.
var mySound = new Audio('/public/audio/Bloom.mp3');
mySound.load();
var socket = io.connect('http://localhost');
socket.on('message', function(data){
console.log(data);
$('#content').text(data);
mySound.play();
//document.getElementById('audiotag1').play();
});
This page is served by server.js, a node.js file using socket.io and express. I don't receive any errors from my console.log.
Here is the server.js
var app = require('express')()
, server = require('http').Server(app)
, io =require('socket.io')(server)
, dgram = require('dgram');
var httpPort = 1234;
var udpPort = 5000;
server.listen(httpPort);
app.use(server.express.static( __dirname + '/public'));
app.get('/', function(request, response){
var ipAddress = request.socket.remoteAddress;
console.log("New express connection from: " + ipAddress);
response.sendfile(__dirname + '/public/index.html');
});
var udpSocket = dgram.createSocket('udp4', function(msgBuffer){
var jsonMessage = JSON.parse(msgBuffer);
io.sockets.emit('message', JSON.stringify(jsonMessage));
});
udpSocket.bind(udpPort, '127.0.0.1');
You can go to this link to see the error chrome has.
http://postimg.org/image/xkv7a2kwb/
Does anyone have any ideas on how to fix this?
This error usually occurs when you have incorrect Express config for static files.
From your client-side JavaScript I can see that you store your static files in public folder.
So somewhere on the server-side you should have something like this:
app.use('/public', express.static('/path/to/public'));
Doing this way http://localhost/public/audio/Bloom.mp3 request will be served with /path/to/public/audio/Bloom.mp3 file.
Caveat:
Since Express 4 there is "no more app.use(app.router)" and "All routing methods will be added in the order in which they appear". This means that you have to put the above line of code before first app.get(...), app.post(...), etc.
Hope this information will help someone.
we can upload tone into cloud storage and then use that source like below
playAudio() {
let audio = new Audio()
audio.src ='https://res.cloudinary.com/Apple_Notification.mp3'
audio.load()
audio.play()
}
trigger this function in client-side whenever a new message comes in

Node.js serving images for canvas

I'm trying to build a simple game with node.js to get more knowledge about networking and stuff related to it. But now I'm stuck. I've been pulling my hair out and searching the web for some hours now, but I can't seem to find the solution. I only found out some useful modules like path.js and mime.js.
My server's code looks like this:
var http = require("http");
var host = "127.0.0.1";
var port = 3000;
var url = require('url');
var fs = require("fs");
var path = require("path");
var mime = require('mime');
var server = http.createServer(function(request, response) {
console.log("Request received: " + request.url);
fs.readFile(__dirname + '/game.html', function(error, data) {
if (error) {
response.writeHead(404, {"Content-type":"text/plain"});
response.end("Sorry, the page was not found");
} else {
var holder = url.parse(request.url);
var ext = path.extname(holder.pathname);
response.setHeader('Content-type',"" + mime.lookup(ext));
response.writeHead(200);
response.end(data);
if (ext == ".png") {
response.setHeader('Content-type',"image/png");
response.writeHead(200);
response.end(data);
} else if (ext == ".jpeg") {
response.setHeader('Content-type',"image/jpeg");
response.writeHead(200);
response.end(data);
}
}
});
});
var io = require('socket.io').listen(server);
The server variable is what seems to cause me problems.
The game I'm trying to implement lies in here: http://jsfiddle.net/6mWkU/2/
Nevermind the graphics ;)
With my server's code, none of the images are served. I tried to use path.js and mime.js, so it sets the specific Content-type each call, but it didn't work.
Hopefully you guys know, what's wrong and can help a newbie out!
Your server does not work in the corrent way, on each request you read the content of the file '/game.html' (you could read it and cache, updating only on some changes made), the you respond to each request (!) with the content of the html file without any check, and after you check the extension of the request (but it was already responded), and if it's true you write again to the response (here you should have some errors in the node's console), you can not write after end in the writable streams.
I have for you 2 solutions:
// The difficult solution
/* Inside the createServer callback */
if (request.url === '/') {
// read the file and respond with html
} else {
if (/* check for the existence of the urls */) {
// get the content of the file and respond with it's content and content-type
// and don't forget to do this asynchronously!
} else {
// return 404
}
}
// The simple solution
I would recommend you to use my module simpleS, it will do everything for you (!). will serve the static files and will give you the chance to use the power of websockets. Just put all the files inside a folder, for example "static", then use the following code:
var simples = require('simples');
var server = simples(3000);
server.get('/', function (connection) {
connection.type('html');
connection.drain(__dirname + '/game.html');
});
server.serve(__dirname + '/static');
/*
And that's all!
To work with websockets read the documentation please
*/

Categories