How do I use socket.io from a server at a subfolder? - javascript

I've been following the tutorial at https://www.tutorialspoint.com/socket.io/, and I have the following 2 files:
app.js:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res) {
res.sendFile('index.html', {root: __dirname});
});
io.on('connection', function(socket) {
console.log('A user connected');
//Send a message after a timeout of 4 seconds
setTimeout(function() {
socket.send('Sent a message 4 seconds after connection!');
}, 4000);
socket.on('disconnect', function () {
console.log('A user disconnected');
});
});
http.listen(80, function() {
console.log('listening on *:80');
});
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Hello world</title>
</head>
<script src = "/socket.io/socket.io.js"></script>
<script>
var socket = io();
socket.on('message', function(data){document.write(data)});
</script>
<body>Hello world</body>
</html>
If I run this in Node.js version 10.16.3 and visit http://localhost/, everything works fine. The server console logs:
A user connected
And the browser eventually displays:
Sent a message 4 seconds after connection!
So far, so good!
The problems arise when I try to run this from my website. Let's pretend it's called example.com.
My web hosting provider uses the same version of Node.js, and has set it up so that each Node.js application runs in a subfolder. Let's pretend it's called example.com/x.
When visiting http://example.com/x, the browser displays:
Cannot GET /x/
So in app.js, I changed this:
app.get('/', function(req, res) {
res.sendFile('index.html', {root: __dirname});
});
...to this:
app.get('/x', function(req, res) {
// Redirect if no slash at the end
if (!req.url.endsWith('/')) {
res.redirect(301, req.url + '/')
}
res.sendFile('index.html', {root: __dirname});
});
Now the initial page loads, but the browser console logs:
The resource from “http://example.com/socket.io/socket.io.js” was blocked due to MIME type (“text/html”) mismatch (X-Content-Type-Options: nosniff).
Loading failed for the <script> with source “http://example.com/socket.io/socket.io.js”.
ReferenceError: io is not defined
Obviously, my app doesn't get a chance to handle this request because it's addressed to the main domain.
So I changed both files:
In app.js, I changed this line:
var io = require('socket.io')(http);
to this:
var io = require('socket.io')(http, {path: "/x/socket.io"});
And in index.html, I changed this line:
<script src = "/socket.io/socket.io.js"></script>
to this:
<script src = "/x/socket.io/socket.io.js"></script>
This got rid of the errors in the browser console, but it still doesn't seem to be connecting.
The browser never displays:
Sent a message 4 seconds after connection!
Same when I run all this in http://localhost/x/, and the Node.js console never logs:
A user connected
Is there an easier way to go about all this than the changes I've been making so far?
If not, what more do I need to do to get this working?

One step left.
In index.html, change this line:
var socket = io();
...to this:
var socket = io.connect("/", {path: "/x/socket.io"});
This should work for both http://example.com/x and http://localhost/x.

Related

How can I connect to an established server (NOT localhost) with node.js & socket.io?

I am very new to web servers / node.js / socket.io and I'm trying to make a very simple website just to learn how to connect 2 people. All of the tutorials I've found teach you how to run your app on localhost, but I want to run it on a server so everyone can access it.
I found this hosting website - Zeit-Now - and managed to put my website online. My website works just fine on localhost, but I can't get it to work when i put it online. I think the problem is in these 2 lines in my code, where the '???' are.
index.html:
<!DOCTYPE html>
<html>
<head>
<title>A2</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
</head>
<body>
<input id="sendMsg" type="text" placeholder="TYPE TO SEND">
<input id="receiveMsg" type="text" placeholder="RECEIVE HERE">
<button id="sendBtn">Send</button>
<script>
var socket = io.connect('https://web-socket-2.quaei.now.sh/');
var sendMsg = document.getElementById('sendMsg');
var receiveMsg = document.getElementById('receiveMsg');
var sendBtn = document.getElementById('sendBtn');
// Emit events
sendBtn.addEventListener('click', () => {
socket.emit('chat', {
message: sendMsg.value
});
});
// Listen for events
socket.on('chat', data => {
receiveMsg.value = data.message;
});
</script>
</body>
</html>
index.js:
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
app.use(express.static('public'));
io.on('connection', socket => {
console.log('Connection! ->', socket.id);
socket.on('chat', data => {
io.sockets.emit('chat', data);
});
});
I've also tried:
var socket = io.connect(https://web-socket-2.quaei.now.sh/);
and
var server = app.listen(80, https://web-socket-2.quaei.now.sh/);
I expect my website to read what is written in the first input field and output it in the second one when clicked send (which works fine on localhost), but it doesn't work when I put it online with Zeit-Now.
Edit:
now.json:
{
"version": 2,
"name": "web-socket-2",
"builds": [{
"src": "public/**/*",
"use": "#now/static"
},
{
"src": "*.js",
"use": "#now/node"
}
],
"routes": [{
"src": "/",
"dest": "public/index.html"
}]
}
I have a #websocket-2 folder with now.json, index.js and public folder. In public folder I have index.html.
How can I make it work on this website: https://web-socket-2.quaei.now.sh/ ?
I guess the problem you face at the moment is that you want to run the server directly on the website. But it's need to be run by node like this:
node index.js.
Therefore you need access to the server like some ssh access and then start it there with the command named above. Based on the logs, I can say that the client can't find the server.
You also need to make sure that it resolves to the right path.
Start it with app.listen(80) and then try to connect to
https://web-socket-2.quaei.now.sh:80
try this
const app = express();
const server = http.createServer(app);
const io = socketio(server);
server.listen(80, '0.0.0.0');
Make sure your server has websockets enabled. (It does not actually need websockets, but it will use it if possible for better transport)
Reading about zeit.co I can see they dont support websockets, not sure if they support sucket either. I would recommend you hitting them up with email to confirm this if code below does not help you.
To run connection on client side, after you have installed socket.io-client or after you have included script in header, which you have.
You simply connect using io.connect('SERVER URL') which will return your socket connection instance. (no need to pass server if running on same server)
const socket = io.connect('http://youserver.com');
// You can listen to conenct and disconnect to track your connection
socket.on('connect', socket => {
console.log('Connected', socket.id);
});
Thats it on client side.
On server side, you need to pass server to your socket.io module
const io = require('socket.io')(server);
// You can listen to your socket connecting through connect and disconnect
io.on('connect', (socket) => {
console.log('socket connected', socket);
});
You can track your errors on client side using 'error' event, and make sure to check your console to see the logs about the connection.
In case you are using express server, and socket is raising 404 errors in console, make sure you initiate server using http instead of express.
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
// your socket and routes
server.listen(process.env.PORT || 3500, () => {
console.log(`Localhost working on ${process.env.PORT || 3500}`);
}

Socket.io version 2.1.0 error 404

I found this error using socket.io latest version: "index.js: 83 POST http: //localhost/socket.io/? EIO = 3 & transport = polling & t = MDUHEO9 404 (Not Found)".
I understand the reason: the true address must be http: // localhost: 3000 / socket.io /.
Do you know how I can correct?
I have read many discussions, but no one has a suitable solution for version 2.1.0, even in a discussion I read a downgrade proposal, I would like to avoid it.
client.js:
<script src="http://localhost:3000/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
$(function () {
var socket = io().connect('http://localhost:3000');
$('form').submit(function(){
socket.emit('chat message', $('#m').val());
$('#m').val('');
return false;
});
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
});
</script>
server.js:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket){
socket.on('chat message', function(msg){
io.emit('chat message', msg);
});
});
Change this:
var socket = io().connect('http://localhost:3000');
to this:
var socket = io.connect('http://localhost:3000');
or to this:
var socket = io('http://localhost:3000')
Either of these last two are the same.
You will want to use only one form of connect, either io(...) or io.connect(...), not both. When you call just io() you're trying to connect to the default URL which will be the URL of the current web page and then when you then try to also do .connect() after that, you're trying to connect again. You only want one connection and to the specified URL.
The problem here is that you've defined any endpoint to server socket.io client file. That's why you are getting 404.
If you've installed in with npm, you can allow express to server that kind of static files for you by just adding the following line
app.use(express.static(__dirname + '/node_modules'));
if you've installed socket.io with bower, use
app.use(express.static(__dirname + '/node_modules'));
Or you can place that socket.io's client library at any location and server the file against the requests like
app.get('/socket.io', function(req, res){
res.sendFile("Path to socket.io.js file");
});
And you don't need to write full address in
<script src="http://localhost:3000/socket.io/socket.io.js"></script>
Just use
<script src="/socket.io/socket.io.js"></script>

Node.js/Express error: cannot GET /

So I have some code (app.js, in the server)
console.log("Server started. If you're reading this then your computer is still alive."); //Unnecessary test command to make sure everything works.
var express = require("express");
var app = express();
var serv = require("http").Server(app);
app.get("/", function(req, res) {
res.sendFile(__dirname + "/client");
});
app.use("/client", express.static(__dirname + "/client"));
serv.listen(2000);
//Set up server stuff. This isn't touched.
var io = require("socket.io")(serv, {});
io.sockets.on("connection", function(socket) {
console.log("Socket connection"); //This will print to the server, not the developer console in your browser.
});
//Initialize sockets and set up socket listeners. This isn't touched either, except when adding new events.
And some more code:
<!DOCTYPE html>
<html>
<head>
<title>Multiplayer!</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<script>
var socket = io();
</script>
</body>
</html>
My folder structure is:
multiplayer_game
app.js
package.json
node_modules (folder)
node stuff
client (folder)
index.html
js (folder)
img (folder)
server (folder)
Where the farther in the file is, the more "nested" it is.
When I open the page by doing node app.js (folder already cd'ed) and go to localhost:2000, I get "Cannot GET /". When I go to localhost:2000/client, I get everything fine. What can I do to fix my code?
You will need a router for that.
const router = express.Router;
router.get('/', function(req, res) {
res.sendFile(__dirname + "/client");
});
app.use('/', router);
A middleware is not capable to handle various http methods.

Failed to load resource socket.io

I am trying to use in socket.io in my node.js app, but my client can't get the library from my sever and I don't know why.
Error: Failed to load resource: the server responded with a http://localhost:3000/socket.io/socket.io.js status of 404 (Not Found)
Server site:
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.use(express.static('public'));
app.get('/', function (req, res) {
res.sendFile( __dirname + "/" + "index.html" );
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
io.on('connection', function(socket){
console.log('a user connected');
});
Client side:
<head>
<link rel="stylesheet" href="/stylesheets/style.css" />
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>
</head>
Your index.html code couldn't find socket.io because you are sendig only Index.html for response. Please try following code.
Use following code:
//"client" is folder for client code which contains index.html
app.use(express.static(__dirname + '/client'));
Remove following code. Node.js will find index.html automatically.
app.get('/', function (req, res) {
res.sendFile( __dirname + "/" + "index.html" );
});
Change your index.html
New code:
<script src="https://cdn.socket.io/socket.io-1.3.7.js"></script>
Old code:
<script src="/socket.io/socket.io.js"></script>
I don't know exactly why your code isn't working, but if socket.io is installed properly, then it automatically configures a route in Express to serve the request for /socket.io/socket.io.js. So, apparently socket.io is not configured/installed properly in your situation.
I can tell you that this initialization of Express and socket.io works and does not have the problem you speak of:
var express = require('express');
var app = express();
var server = app.listen(80);
var io = require('socket.io')(server);
I had the same problem in my index.html while learning Node.js.
The console prompted similar error
I removed socket.io module by executing npm r socket.io in my project directory and re-installed it using npm i socket.io and then it worked fine.

Javascript file not loading in MEAN stack application

I am playing around and trying to learn the MEAN stack to create a single page application. I was able to get my back-end routes to work correctly. I've tested it using Postman and get the results I expect, however I cannot get my front-end to talk to the back-end correctly.
I reduced my front-end to very basic html and javascript, but still get a syntax error "Unexpected token <". When I inspect my coreTest.js file in the browser, I see that it's just a copy of my html file, rather than javascript code.
When I run index.html by itself (without server.js) coreTest.js runs just fine. I'm not sure why, when running my server.js file, my coreTest.js file doesn't work properly.
server.js
// Setup
var express = require('express');
var app = express(); // create app w/ express
var mongoose = require('mongoose'); // used for MongoDB connection
var morgan = require('morgan'); // log requests to the console
var bodyParser = require('body-parser'); // pull information from HTML POST
var methodOverride = require('method-override'); // simulate DELETE and PUT requests
// Configure
mongoose.connect('localhost:27017/test'); // connect to localhost db "test"
// varify connection
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error: '));
app.use(express.static(__dirname + '/public')); // set static files location
app.use(morgan('dev')); // log every request to console
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-url
app.use(bodyParser.json());
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse vnd.api+json as json
app.use(methodOverride());
// define db model
var Todo = mongoose.model('Todo', {
text: String
});
var router = express.Router();
router.route("/todos")
.get(function(req, res) {
Todo.find(function(err, todos) {
if (err)
res.send(err);
res.json(todos);
});
})
.post(function(req, res) {
var todo = new Todo();
todo.text = req.body.text;
todo.save(function(err) {
if (err)
res.send(err);
res.json({mes: "Todo item created"});
})
});
router.route("/todos/:todo_id")
.delete(function(req, res) {
Todo.remove( {
_id: req.params.todo_id
}, function(err, todo) {
if (err)
res.send(err);
res.json({mes: "Successfully deleted"});
});
});
// route to application
router.route("*")
.get(function(req, res) {
res.sendfile('./public/index.html');
});
// Register our routes
app.use('/api', router);
// Listen, start node app
app.listen(8080);
console.log("app listening on port 8080");
coreTest.js
alert("ping");
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Starter MEAN Single Page Application</title>
<script type="text/javascript" src="coreTest.js"></script>
</head>
<body>
I did it!! Wooo!!
</body>
</html>
I suspect the problem is between
app.use(express.static(__dirname + '/public'));
and
// route to application
router.route("*")
.get(function(req, res) {
res.sendfile('./public/index.html');
});
When you're running the server, your client-side code is trying to load "coreTest.js", which is matched by the last rule ; and so the server returns the content of index.html.
I'm not sure what you mean about "runing index.html by itself" ; if you mean "visiting file://..../public/index.html", it works because the browser will try to load coreTest.js from your file system.
Try using the "static" plugin only to serve files from "public", and use this prefix in the client side code :
app.use("/public", express.static(...))
and in the client
<script type=".." src="public/coreTest.js"></script>
Getting the path rights will depend on what's on your disk.
This might help, too : static files with express.js
I was able to solve my issue using what phtrivier recommended.
Instead of serving my files from "public" I chose to serve them from "api" because I already set my router to take requests from that path as well.
app.use("/api", express.static(__dirname + '/public'));
I didn't need to change my reference to coreTest.js in the html because my files were organized with coreTest already in the root directory like this:
public
-index.html
-coreTest.js

Categories