I got a game working with socket.io. It's working fine when playing locally and via my IP address (not LAN but real IP) when connecting via my own computer.
However, when I give my IP and port to someone else, the index HTML page is loaded all fine but the socket.io 'connection' doesn't work.
It displays the error on line 1659 of socket.io.js.
Socket.prototype.handshake = function (fn) {
var self = this
, options = this.options;
function complete (data) {
if (data instanceof Error) {
self.connecting = false;
self.onError(data.message);
} else {
fn.apply(null, data.split(':'));
}
};
var url = [
'http' + (options.secure ? 's' : '') + ':/'
, options.host + ':' + options.port
, options.resource
, io.protocol
, io.util.query(this.options.query, 't=' + +new Date)
].join('/');
if (this.isXDomain() && !io.util.ua.hasCORS) {
var insertAt = document.getElementsByTagName('script')[0]
, script = document.createElement('script');
script.src = url + '&jsonp=' + io.j.length;
insertAt.parentNode.insertBefore(script, insertAt);
io.j.push(function (data) {
complete(data);
script.parentNode.removeChild(script);
});
} else {
var xhr = io.util.request();
xhr.open('GET', url, true);
if (this.isXDomain()) {
xhr.withCredentials = true;
}
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
xhr.onreadystatechange = empty;
if (xhr.status == 200) {
complete(xhr.responseText);
} else if (xhr.status == 403) {
self.onError(xhr.responseText);
} else {
self.connecting = false;
!self.reconnecting && self.onError(xhr.responseText);
}
}
};
xhr.send(null); //This is the line 1659.
}
};
Note: All the files are inside a folder on C: drive, not under a User.
Is the problem related to security access? Or something else?
Code for Server + Client
//Server
express = require('express');
http = require('http');
app = express();
server = http.createServer(app);
io = require('socket.io').listen(server);
app.use(express.static(__dirname + '/public'));
server.listen(3000);
app.get('/', function (req, res) { res.sendfile(__dirname + '/index.html'); });
//Client
<script src="/socket.io/socket.io.js"></script>
<script>var socket = io.connect('http://192.168.1.161:3000');</script>
Router Configuration
http://puu.sh/3ACGz.png
Make sure that your port (for socket.io) is forwarded by your router. And you are using public IP (static).
As well you should remember that most browsers will not allow to connect via WebSockets to another address/port from the page. For security reasons, your IP/Domain and Port should be the same as your IP/Domain and Port you server html and js from.
Related
Web soket connection error
I've got a problem with my server. It uses express and express-ws for web sockets. The problem is that this server works fine on the local host. But when i run it with the help of ssh (see localhost.run) and access the site by the given link from another computer (through Chrome), the web socket doesn't open and the next error shows up in the console
main.js:12 WebSocket connection to 'ws://localhost:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
even though i added cert and key to the server connection. P.S. The site loads too, it's only that the socket doesn't work.
here is the server.js code:
"use strict";
const fs = require("fs");
const credentials = {
key: fs.readFileSync("./key.pem"),
cert: fs.readFileSync("./cert.pem")
};
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const https = require("https");
const PORT = process.env.PORT || 3000;
app.use(express.static(__dirname + "/public/Messenger"));
app.use(express.static(__dirname + "/public/Login"));
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const server = new https.createServer(credentials);
const expressWs = require("express-ws")(app, server);//if i take the
//second argument (server) away, it works fine on localhost:3000, but
//with this it fires the error:
//WebSocket connection to 'ws://localhost:3000/' failed: Connection
//closed before receiving a handshake response
const routes = require("./MVC/Router/router.js"); //importing route
routes(app);
app.listen(PORT, () => {
console.log("listening on port " + PORT);
});
here is the router.js:
"use strict";
module.exports = function(app) {
const database = require("../Controller/controller.js");
// database Routes
app.route("/").get(database.loadPage);
app.route("/login").get(database.loginPageLoad);
app
.route("/signIn")
.get(database.signInPageLoad)
.post(database.signIn);
app
.route("/submitLogin")
.post(database.loginSubmit)
.get(database.showUsers);
app.ws("/", database.sendmsg);
};
which redirects the processing flow to the next part of controller.js:
const CLIENTS = [];
let counter = 0;
exports.sendmsg = (ws, req) => {
console.log(cache.get("lorem"));
ws.on("message", msg => {
if (msg === "connected") {
console.log("connected");
CLIENTS.push([ws, counter]);
ws.send(JSON.stringify({ counter }));
counter++;
} else if (JSON.parse(msg).msg && JSON.parse(msg).ID) {
CLIENTS.forEach(box => {
if (box[1] === msg.ID) {
console.log(`user ${box[1]} is closed`);
box.push("closed");
box[0].close();
} else {
return;
}
});
} else {
sendAll(msg);
}
ws.on("close", () => {
console.log("disconnected");
ws.close();
});
});
};
function sendAll(message) {
for (let i = 0; i < CLIENTS.length; i++) {
if (CLIENTS[i][0].readyState === 1) {
CLIENTS[i][0].send(message);
}
}
}
The last piece of code is just what it does on the server, don't care of it too much. The problem is that the web socket doesn't get open when i enter the link from another computer. How can i solve it?
I have a simple and working web server written in NodeJS as below:
var http = require("http");
var fs = require("fs");
console.log("Web server started");
var config = JSON.parse(fs.readFileSync("./private/config.json"));
var server = http.createServer(function(req,res){
console.log("received request: " + req.url);
fs.readFile("./public" + req.url,function(error,data){
if (error){
// Not sure if this is a correct way to set the default page?
if (req.url === "/"){
res.writeHead(200,{"content-type":"text/plain"});
res.end("here goes index.html ?");
}
res.writeHead(404,{"content-type":"text/plain"});
res.end(`Sorry the page was not found.\n URL Request: ${req.url}`);
} else {
res.writeHead(200,{"content-type":"text/plain"});
res.end(data);
}
});
});
Now I want my web server to restart and listen to a new port when port number changes in the config file. So I add below code:
fs.watch("./private/config.json",function(){
config = JSON.parse(fs.readFileSync("./private/config.json"))
server.close();
server.listen(config.port,config.host,function(){
console.log("Now listening: "+config.host+ ":" +config.port);
});
});
This works fine and when I change the port on config file, I can access my web server on the new port. However, I also can access it on the previous port as well. I thought I am closing my web server on the previous port before I listen to the new port. What am I missing ?
I appreciate your help :)
As Mukesh Sharma mentioned, Server.close() stops accepting new connections and keeps existing connections. That is, server stays open for all alive sockets(until they naturally die due to keep-alive time) but no new sockets will be created.
I found out this question can be a possible duplicate of this question
So I followed the suggested solution mentioned by Golo Roden in the link and it worked. Basically you need to remember open socket connections and destroy them after you close the server. Here is my modified code:
var http = require("http");
var fs = require("fs");
console.log("Web server started");
var config = JSON.parse(fs.readFileSync("./private/config.json"));
var server = http.createServer(function(req,res){
console.log("received request: " + req.url);
fs.readFile("./public" + req.url,function(error,data){
if (error){
// Not sure if this the correct method ?
if (req.url === "/"){
res.writeHead(200,{"content-type":"text/plain"});
res.end("welcome to main page");
}
res.writeHead(404,{"content-type":"text/plain"});
res.end(`Sorry the page was not found.\n URL Request: ${req.url}`);
} else {
res.writeHead(200,{"content-type":"text/plain"});
res.end(data);
}
});
});
server.listen(config.port,config.host,function(){
console.log("listening: "+config.host+ ":" +config.port);
});
var sockets = {}, nextSocketId = 0;
server.on('connection', function (socket) {
// Add a newly connected socket
var socketId = nextSocketId++;
sockets[socketId] = socket;
console.log('socket', socketId, 'opened');
// Remove the socket when it closes
socket.on('close', function () {
console.log('socket', socketId, 'closed');
delete sockets[socketId];
});
});
fs.watch("./private/config.json",function(){
config = JSON.parse(fs.readFileSync("./private/config.json"))
console.log('Config has changed!');
server.close(function () { console.log('Server is closing!'); });
for (var socketId in sockets) {
console.log('socket', socketId, 'destroyed');
sockets[socketId].destroy();
}
server.listen(config.port,config.host,function(){
console.log("Now listening: "+config.host+ ":" +config.port);
});
});
When I navigate to a sub page of my site, my socket.io server is running the connection module for the index page.
Here is the complete console log upon navigation to www.example.com/query
authenticated updated ==> { GbLZ5jxHz0S5uyNNAAAA: { user: 'yrn1jro2fw1nk4dyy4', group: 'uehw1o2grq1oy11y9xrgyw' } }
client connected to INDEX with id GbLZ5jxHz0S5uyNNAAAA
preloading index
client connected to QUERY with id GbLZ5jxHz0S5uyNNAAAA
preloading query
My main question deals with I am getting the second and third lines. I expect to connect to "QUERY" but not to "INDEX".
When I navigate to www.example.com the console prints as expected with a single connection.
Here is my entire server.js file that I just simplified and ran to generate the simplified console log above. You can ignore the authentication stuff, I mostly included that for people that might find it useful.
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var util = require("./js/util.js");
var authenticated = {}; // [socket_id:{user:000,group:000},socket_id:{user:000,group:000}] // stores currently authenticated sockets
server.listen(80);
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.get('/query', function (req, res) {
res.sendFile(__dirname + '/query.html');
});
app.get('/favicon.ico', function (req, res) {
res.sendFile(__dirname + '/favicon.ico');
});
app.use("/js", express.static(__dirname + '/js'));
app.use("/css", express.static(__dirname + '/css'));
app.use("/images", express.static(__dirname + '/images'));
var mysql = require('mysql');
var db = mysql.createConnection({host:'localhost',user:'root',password:'fakepw',database:'baseofdata'});
db.connect();
var index = io.of('/').on('connection', function (socket) {
// console.log(socket);
console.log("client connected to INDEX with id",socket.id);
socket.on("authenticate",function(data){
console.log("authenticate data ==> ",data);
db.query("SELECT user,usergroup FROM group_users INNER JOIN users ON group_users.user = users.id WHERE username = ? AND password = ?",[data.user,data.pass],function(err, rows) {
console.log("auth returned ==> ",rows,err);
if (err === null && rows.length > 0)
{
authenticated[socket.id] = {user:rows[0].user,group:rows[0].usergroup};
encoded = {};
encoded.user = util.encode(rows[0].user);
encoded.usergroup = util.encode(rows[0].usergroup);
socket.emit("authenticated",encoded);
preload();
}
else socket.emit("unauthorized");
});
});
if (typeof authenticated[socket.id] !== 'object')
{
console.log(socket.id,"does not exist -- sending unauthorized");
socket.emit("unauthorized");
}
else preload();
// OTHER INDEX LISTENERS HERE
socket.on('disconnect', function(){
delete authenticated[socket.id];
console.log(socket.id,"deleted");
});
function preload()
{
console.log("preloading index");
// PRELOAD INDEX
}
});
var query = io.of('/query').on('connection', function (socket) {
console.log("client connected to QUERY with id",socket.id);
if (typeof authenticated[socket.id] !== 'object')
{
console.log(socket.id,"does not exist -- sending unauthorized");
socket.emit("unauthorized");
}
else
{
console.log("preloading query");
// PRELOAD QUERY
}
// OTHER QUERY LISTENERS HERE
socket.on('disconnect', function(){
delete authenticated[socket.id];
console.log(socket.id,"deleted");
});
});
io.use(function(socket, next){
if (socket.handshake.query.u !== 'null' && socket.handshake.query.u !== undefined)
{
authenticated[socket.id] = {};
authenticated[socket.id].user = socket.handshake.query.u;
authenticated[socket.id].group = socket.handshake.query.g;
console.log("authenticated updated ==>",authenticated);
}
if (socket.request.headers.cookie) return next();
next(new Error('Authentication error'));
});
Here is my applicable client code
<!DOCTYPE html>
<html>
<head>
<title>Query Page</title>
<link rel="icon" href="favicon.ico" sizes="32x32" type="image/ico">
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>
<script>
var socket = io.connect("127.0.0.1/query",{query:"u=" + readCookie("u") + "&g=" + readCookie("g")});
I am clearly missing something. Also, I am new to node and socket.io and would appreciate any guidance.
EDIT:
Changed edits to an answer.
Also changed the client connection from "example.com" to the server's IP address. Though, I doubt it mattered but I wondered if the issue was related to my DNS redirecting URLs.
The root "namespace" does not act as I though it would.
Change
var index = io.of('/').on('connection', function (socket) {
to
var index = io.of('/index').on('connection', function (socket) {
So the actual index page will not rely on the default namespace but instead have it's own module.
I'm trying to proxy json data from a private TCP port 13854 to a public web socket on port 8080. Why can't I get any data when browsing http://localhost:8080?
var http = require('http').createServer(httpHandler),
fs = require("fs"),
wsock = require('socket.io').listen(http),
tcpsock = require('net');
var proxyPort = 8080;
var serviceHost = 'localhost';
var servicePort = 13854;
function httpHandler (req, res) {
res.setHeader("Access-Control-Allow-Origin", "http://example.com");
res.end();
}
http.listen(proxyPort);
console.info("HTTP server listening on " + proxyPort);
wsock.sockets.on('connection', function (socket) {
var tcpClient = new tcpsock.Socket();
tcpClient.setEncoding("ascii");
tcpClient.setKeepAlive(true);
tcpClient.connect(servicePort, serviceHost, function() {
console.info('CONNECTED TO : ' + serviceHost + ':' + servicePort);
tcpClient.on('data', function(data) {
data = "" + data
//send format request to socket
if (data[0] != '{'){
s.write(JSON.stringify({
enableRawOutput : false,
format : "Json"
}) + "\n");
return;
}
console.log('DATA: ' + data);
socket.emit("httpServer", data);
});
tcpClient.on('end', function(data) {
console.log('END DATA : ' + data);
});
});
socket.on('tcp-manager', function(message) {
console.log('"tcp" : ' + message);
return;
});
socket.emit("httpServer", "Initial Data");
});
THANKS!
First of all, change the line
res.setHeader("Access-Control-Allow-Origin", "http://example.com");
to
res.setHeader("Access-Control-Allow-Origin", "*");
Because you are browsing to localhost, your request will be rejected because the origin is not http://example.com.
Secondly, in order to receive data, you must setup a web socket connection from the client. Just browsing to http://localhost:8080 creates an http connection and not a web socket connection.
I propose to create an HTML page locally and use that by double-clicking on it (instead of going through your server); later you can host the page on your node.js server.
Look at the examples on http://socket.io to correctly create a socket.io client.
I solved the problem by reorganizing my code and keeping the sockets separated. For whatever reason, it seems that Access-Control-Allow-Origin is not needed. I am using a Chrome plugin called "Simple Web Socket Client" to get around needing to write my own client.
var ws = require("nodejs-websocket"),
net = require("net");
var server = ws.createServer(function(conn) {
conn.on("close", function(code, reason) {
console.log("Connection closed");
});
}).listen(8080);
var tcp = new net.Socket();
console.log('connecting to 127.0.0.1:13854');
tcp.connect(servicePort, '127.0.0.1', function() {
//this socket requires sending data on connection
tcp.write(JSON.stringify({
enableRawOutput: false,
format: "Json"
}) + "\n");
});
tcp.on("data", function(data) {
if (server == null || server.connections == null) {
return;
}
//broadcast message:
server.connections.forEach(function(conn) {
conn.sendText(data);
});
}
What is the best way to do this?
I'd like to redirect all requests from www.mysite.com to mysite.com
*.mysite.com to mysite.com would be ideal
I would think some type of middleware. I still hate this, because it seems so inelegant and slightly wasteful, but I think my only option is to do this server-side.
Since Express 3 doesn't use its own HTTP server (instead you pass your app to http.createServer), it doesn't know what port it's running on unless you tell it. That said, you can do basically what you want to do with the following:
app.use(function(request, response, next) {
var newHost = request.host.replace(/^www\./, '');
if (request.host != newHost) {
// 301 is a "Moved Permanently" redirect.
response.redirect(301, request.protocol + "://" + newHost + request.url);
} else {
next();
}
});
You could export this in a module and wrap it in a generator that takes a port:
// no_www.js
module.exports = function(port) {
app.use(function(request, response, next) {
var newHost = request.host.replace(/^www\./, '');
if (request.host != newHost) {
var portStr = '';
if (request.protocol == 'http' && port != 80) portStr = ':' + port;
if (request.protocol == 'https' && port != 443) portSt r= ':' + port;
// 301 is a "Moved Permanently" redirect.
response.redirect(301, request.protocol + "://" + newHost + portStr + request.url);
} else {
next();
}
});
}
// app.js
var noWww = require('./no_www');
var app = express();
app.configure("development", function() {
app.set("port", 3000);
});
...
app.use(noWww(app.get('port')));