For my project I needed a VPS, so I bought one on DigitalOcean. I installed MongoDB, Laravel and the whole thing runs on Nginx.
Earlier today I asked a question about a timer and the advice was to use WebSockets. According to the comment, the best approach was to use NodeJS with Socket.io. And so I did.
I followed this tutorial here (locally) and had absolutely no problems at all on localhost.
So my next step was to upload the code to my webserver and combine it with Laravel. I had some problems making connections, but after I found this Stackoverflow post, it finally worked. The server was sending a Date-object and the timer on the screen was updating realtime.
But it only worked when I manually started the script through the node terminal-command. So I followed this tutorial on DigitalOcean where you use PM2 to keep running the script, even when I close the terminal/log out of my VPS. Everything was working fine and the timer was still updating and I was actually very surprised that I didn't run into that many problems..
..until 5 minutes later. All of a sudden, the WebSocket stopped working. Maybe I had made a typo without realising, maybe the server noticed some change in the code that I didn't. I have no clue, but when I look in the developers console, it says:
GET http://<my-domain-ip>:8080/socket.io/?EIO=3&transport=polling&t=LF8I2CO net::ERR_CONNECTION_REFUSED
Of course, I googled a lot and applied all sorts of changes to my code according to (mostly) Stackoverflow answers, but now I'm really running short on ideas and have absolutely no idea why my code is not working.
The server.js file:
var http = require('http');
var url = require('url');
var fs = require('fs');
var io = require('socket.io');
var server = http.createServer(function (request, response) {
var path = url.parse(request.url).pathname;
switch(path) {
case '/' :
response.writeHead(200, { 'Content-Type' : 'text/html'});
response.write('hello world');
response.end();
break;
case '/socket.html' :
fs.readFile(__dirname + path, function (error, data) {
if(error) {
response.writeHead(404);
response.write("Oops, this doesn't exist - 404");
response.end();
} else {
response.writeHead(200, {'Content-Type' : 'text/html'});
response.write(data, 'utf8');
response.end();
}
});
break;
default :
response.writeHead(404);
response.write("Oops, this doesn't exist - 404");
response.end();
break;
}
});
server.listen(8080, '<private ip>');
console.log('Server running at http://<private ip>:8080/');
var listener = io.listen(server);
listener.sockets.on('connection', function (socket) {
setInterval(function () {
socket.emit('date', {'date' : new Date()});
});
});
(I had to set a private IP according to the DigitalOcean tutorial, so Nginx could make it work).
The Javascript code on the client's side:
<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
<script>
var baseURL = getBaseURL(); // Call function to determine it
var socketIOPort = 8080;
var socketIOLocation = baseURL + socketIOPort; // Build Socket.IO location
var socket = io(socketIOLocation);
//Build the user-specific path to the socket.io server, so it works both on 'localhost' and a 'real domain'
function getBaseURL()
{
baseURL = location.protocol + "//" + location.hostname + ":" + location.port;
return baseURL;
}
socket.on('date', function (data) {
$('#date').text(data.date.getHours() + ':' + data.date.getMinutes() + ':' + data.date.getSeconds());
})
</script>
If I remember correctly, I also set some options like proxy_pass and Upgrade $upgrade, but I can't remember where I read that / which file I applied that to, but as far as I know, those options are set correctly.
Does someone know where the problem lies? Because I'm really running out of ideas.
Thanks in advance!
Hosting companies often have ways of "managing" long running
connections to preserve their resources. One possibility is that your
hosting infrastructure needs to be specifically configured for a long
running webSocket connection so nginx (or some other piece of
networking equipment) doesn't automatically kill it after some period
of time. This is not something that is generic to all hosting - it is
specific to the configuration of your particular VPS at your
particular hosting company so you will have to get any guidance on
this topic from them.
Of possible help:
http://nolanlawson.com/2013/05/31/web-sockets-with-socket-io-node-js-and-nginx-port-80-considered-harmful/
I changed:
server.listen(8080, <private ip>);
to:
server.listen(443);
And:
var socketIOPort = 8080;
to:
var socketIOPort = 443;
and the WebSockets were working instantly, thanks to jfriend00!
Related
Being new here i am not allowed to attach image. So here the link to the image for more clarity to my problem.
Script code:
var http = require('http');
var dt = require('./myfirstmodule');
http.createServer(function (req, res) {
res.writeHead(200, {
'Content-Type': 'text/html'
});
res.write("The date and time is currently: " + dt.myDateTime());
res.end();
}).listen(8080);
As i mentioned earlier there is no issue with your code. When executing application created using http server for the first time for the Windows platform, you will get the form dialog shown in below Figure. It’s better to check Private Network and then click Allow access
In case of failure of Confirming from Windows Firewall to open a port, You will get above error.
So I'm very new to node.js and javascript, and i made a server that works great by loading up an html file on request. This html file does not contain any of it's own data, it simply sources from the internet and displays some images and text i wrote. I've decided to make the site play an audio file when it is opened. I know this is done easily with the <audio> tag in html5 but the src="" lets me take a file from the computer and place it there, of course when i open the site from another computer the file obviously isn't found and thus isn't played. I figure the audio file must be kept as a variable on the server and passed into the html file's <audio src= > tag. How do i do this? It is an .mp3(but i can get it to any other audio format) file about 30 seconds long. I just want it to play when the site is loaded from another computer(over the internet). Also how would i go about doing the same with pictures or any other data that i don't want to source from the internet but rather keep as data in my server?
var http = require('http');
var fs = require('fs');
var simpleServer = http.createServer(function(request, response){
response.writeHead(200, {"Content-Type":"text/html"});
fs.readFile('./Picture.html', null, function(error, data){
if(error){
response.writeHead(404);
} else{
response.write(data);
}
response.end();
})
});
simpleServer.listen(80, '0.0.0.0', function() {
console.log('Listening to port: ' + 80);
});
console.log("Server running...");
Short Answer
Bypassing using HTML altogether, you can also simply serve the audio file instead of Picture.html:
fs.readFile("./audiofile.mp3", function(error, data) {
if (error) {
response.writeHead(404);
} else {
response.writeHead(200, { "Content-Type": "audio/mpeg"});
response.end(data, 'utf-8');
}
});
Note:
You will have to replace the filename audiofile.mp3 and the content type audio/mpeg to their appropriate values for the file you want to send.
Check Mozilla's Complete List of MIME Types for a full list of file extensions and their associated content types.
Better Answer:
The http module is fairly low-level and is unnecessarily complicated if you're learning.
You can install express.js to your project using the command npm install express --save.
With express your code simplifies to:
const express = require('express');
const app = express();
const port = 80;
app.get('/', (request, response) => {
response.sendFile(__dirname + '/Picture.html');
});
// Anything put in the public folder is available to the world!
app.use(express.static(__dirname + '/public'));
app.listen(port, () => {
console.log(`Listening on port: ${port}`)
});
Then you just have to place all your files into a folder called "public" under your project directory and you can call them from HTML!
Ok, so I have my server.js
var express = require("express"),
app = express(),
bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static(__dirname + '/'));
app.post('/message', function(req, res) {
var jsonData = req.body;
if (jsonData.hasOwnProperty('phone1')) {
console.log("Phone 1 is connected to", jsonData.phone1.connection,
"and has a downlink speed of", jsonData.phone1.dl, "Mbps");
} else
if (jsonData.hasOwnProperty('phone2')) {
console.log("Phone 2 is connected to", jsonData.phone2.connection,
"and has a downlink speed of", jsonData.phone2.dl, "Mbps");
}
});
var port = 1337;
app.listen(port);
console.log("Running at Port " + port);
and as you see I want to do stuff when the server gets something posted on /message. I can console.log stuff yes, but I want to change things on the web page this server is serving. The POST requests are coming from another server. This server only presents them.
How can I do that without having to update the page?
I'm also using AngularJS on the client side, so any way for the client side to pick up the JSON data would be nice.
I hope to present the data in my Highcharts gauges and charts but a simple text update on a page element (e.g. <p id="example">) will do just fine for an answer to this question.
I know I can get jquery to node but I still lack the window element to manipulate the presented data. NW.js might do exactly what I want, I haven't still tried it though, but I suspect there might be another solution to this problem.
If you want to push new content to the client (opposite flow to the client requesting data from the server), WebSockets are an option (see http://socket.io/ for a common library).
Alternatively, you could setup long polling on the client side. This is using JavaScript to periodically 'poll' the server for information using setInterval or a similar approach.
Since I only need to send data from the server to the client, I ended up with Server Sent Events (SSE) and my code looks like this on the server side:
var mypage;
app.get('/connect', function(req, res){
res.writeHead(200, {
'Connection': 'keep-alive',
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache'
});
mypage = res;
});
app.post('/message', function(req, res) {
if (mypage !== undefined) {
mypage.write('data: '+ JSON.stringify( req.body ) +'\n\n');
}
});
and on the client side:
$(function () {
var server = new EventSource('/connect'),
point1 = $("#gauge1").highcharts().series[0].points[0],
point2 = $("#gauge2").highcharts().series[0].points[0];
server.onmessage = function(e) {
var jsonData = JSON.parse(e.data);
if (jsonData.hasOwnProperty('phone1')) {
point1.update(parseInt(jsonData.phone1.dl));
if (jsonData.phone1.connection === "3G") {
/* extra functionality */
} else {
/* extra functionality */
}
} else
if (jsonData.hasOwnProperty('phone2')) {
point2.update(parseInt(jsonData.phone2.dl));
if (jsonData.phone2.connection === "3G") {
/* extra functionality */
} else {
/* extra functionality */
}
}
};
});
I got extra help from my other question regarding the data formatting of SSE
Node Webkit was not the answer to my problem, since it doesn't run on the browser.
I still have some problems with receiving many posts. I'm generating every 0.5s a post message but I keep receiving 5 messages then nothing for 2 minutes then again 5 messages. I don't know why that's happening, but it's not part of this problem so I'll post another question about that.
I have a simple node.js app with socket.io (1.3.5), taken from socket.io examples:
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var port = process.env.PORT || 3000;
server.listen(port, function () {
console.log('Server listening at port %d', port);
});
// Routing
app.use(express.static(__dirname + '/public'));
io.of('/admin').on('connection', function(socket){
//handle conection on /admin namespace
});
io.of('/user').on('connection', function(socket){
//handle conection on /user namespace
});
Now in my front-end I connect to these specific namespaces like so (again, taken from the example):
var admin_socket = io('/admin');
var user_socket = io('/user');
The app is running on port 3000 and the website is opened using URL localhost:3000.
When doing that I am getting CORS errors, it seems like Socket.io on client side is not auto-detecting the port number as soon as I start using namespaces (in firefox dev tools I can see requests going to localhost/ rather than localhost:3000/).
If on my server-side I don't use namespaces:
io.on('connection', function(socket){
//handle general conection
});
And on front-end I connect this way:
var socket = io();
Everything works fine, port auto-discovery works and in firefox dev tools I can see connections being made to localhost:3000/.
Alternatively, if I still use namespaces on my back-end, and on front end I connect like so:
var admin_socket = io('localhost:3000/admin');
var user_socket = io(':3000/user'); //I can skip localhost
Again everything works (and indeed in firefox dev tools I can see network requests going to localhost:3000/).
How come the port auto-discovery is not working with namespaces? Is there a way to get it to work? Am I missing something here? Thanks.
See my answer below for a fix...
Ok so I did some debugging of code in socket.io.js and realized there is a potential bug there. On line 1050 a loc.hostname is used instead of loc.host.
This causes a hostname to be used when passing in a namespace, this doesn't include port number.
In case of no namespace being used, on line 1024 loc.host is being used and everything is fine.
I have taken a copy of the file and changed line 1050 to use host and everything works fine.
Found github issue with that, it is fixed in 1.4.x:
https://github.com/Automattic/socket.io-client/issues/812
No need to mess with ports, it pretty much should work just by
var admin_socket = io('/admin');
var user_socket = io('/user');
I don't think there is any way to get the auto port discovery to work without modifying the actual Socket.io code or waiting for a fix. The simplest thing you could do is just insert the current location.port including a colon before your namespace.
var admin_socket = io(':' + location.port + '/admin');
var user_socket = io(':' + location.port + '/user');
Or create a new function that will create a socket for you.
function sio(nsp) {
return io(':' + location.port + nsp);
}
I've recently ran into a very interesting problem while writing a web app with node.js.
Essentially, all I am doing is serving the index.html page to the client.
Here is the code:
var http = require('http');
var url = require('url');
var fs = require('fs');
var util = require('util');
var server = http.createServer(function(req, res){
var path = url.parse(req.url).pathname;
if(path == '/'){
console.log("LOADING INDEX...");
openIndex(req, res);
console.log("LOADING COMPLETE.")
} else {
res.write("Something went wrong...");
res.end();
}
}
);
var openIndex = function(req, res){
fs.readFile('./index.html', function(error, content){
if(error){
res.writeHead(500);
res.end();
}
else{
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(content, 'utf-8');
}
});
}
I've put some debugging statements just before and after the index.html page loads: "LOADING INDEX..." and "LOADING COMPLETE".
Now, I have shared the link to my server with my Facebook friends so they can see my app. Most of the time, everything works as it should, but once in a while I get this error:
LOADING INDEX...
This type of response MUST NOT have a body. Ignoring data passed to end().
and just now I've also gotten:
LOADING INDEX...
This type of response MUST NOT have a body. Ignoring write() calls.
The process never raches the "LOADING COMPLETE" statement.
I've tried to reproduce this countless times (accessing my app on different machines, browsers, devices, OS-versions) but every time it works as it should.
I've looked around for other people having this problem, and it seems that somehow, a body is getting into a GET response? I'm not entirely sure what this means or how to fix my code to prevent that from happening. Also, I'm not sure what the clients that produce this error see? Do they get to see my app? (i.e. are these just warnings and as far as they are concerned everything is fine?)
Any help with this will be greatly appreciated.
Xaan
If you're just using a static index.html, why not use express.static to serve it automatically?
app.use("/index.html", express.static(__dirname + '/index.html'));
This would cause expressjs to automatically handle HEAD requests, which should solve your problem.