I'm actually working on a little project, where i'm supposed to recreate a drawing multiplayer game with node.js, mongoDB, socket.io and canvas.
The drawer is working like a charm, and the server seems to work well too. I got my register/login/sessions and database up and working, the only problem is socket.io. When an user is joining the game room, he can see the drawer and tools, but no connection. Why ? The browser can't find socket.io.js.
What I did :
I verified if it was installed, it is with npm install socket.io.
I checked if the server was starting it when turning the server on : Got "socket.io started" in my console.
I checked my HTML code, here it is :
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
According to the billions of tutorials/dev sites/help subjects, this is supposed to work. But it's not. When opening the console of my browser, I got this :
X GET http://localhost:1337/socket.io/socket.io.js NOT FOUND.
I don't know where is the problem, I can't figure this out and it's giving me a huge headache.. So I'm here.
Thanks in advance for helping ! :)
Given the code in your comment, you're not using the correct variable for initializing socket.io.
Try this:
var express = require('express');
var app = express();
var server = app.listen(1337);
var io = require('socket.io').listen(server);
...
So instead of having socket.io 'listen' on the Express app instance, it should listen to what app.listen(...) returns (which happens to be an http.Server instance).
For anyone landing here because they're going through the v4.x socket.io get started example, all you need to do is add another endpoint to your index.js file
index.js
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
// add this
app.get('/socket.io/socket.io.js', (req, res) => {
res.sendFile(__dirname + '/node_modules/socket.io/client-dist/socket.io.js');
});
///
io.on('connection', (socket) => {
console.log('a user connected');
});
server.listen(3000, () => {
console.log('listening on *:3000');
});
Related
So I'm working on an online multiplier game that has a lobby system through socket.io. The lobby system allows users to join and leave games, all of which is managed by socket.io. When I test the site on localhost it works fine, but when I put the project on the web, I run into an issue that doesn't allow users to leave and join rooms smoothly like before.
When the user sends a POST request to join a lobby, I use ejs to render the lobby html with a file called lobby.ejs that has the following relevant code:
<script> let lobby = <%- JSON.stringify(data.lobby) %>; let name = <%- JSON.stringify(data.name) %></script>
<script src="./js/ejs/ejs.js"></script>
<script src="./js/lobby.js"></script>
in lobby.js, I do:
const socket = io()
Here's what's relevant on the server side in my app.js file:
var express = require('express')
var bp = require('body-parser')
var app = express()
var server = require('http').createServer(app)
var urlEncodedParser = bp.urlencoded({ extended: false })
app.set('view engine', 'ejs')
app.use(express.static('views'))
app.get('/', (req, res) => {
res.render('index', {qs: req.query})
})
app.post('/lobby', urlEncodedParser, (req, res) => {
res.render('lobby', {data: req.body})
})
const PORT = process.env.PORT || 8080
server.listen(PORT, () => {
//console.log('server started')
})
var io = require('socket.io')(server)
The issue is that whenever a user enter's a lobby, and i make the socket connection with const socket = io()
I get the handshake error. Then when the user closes out of the tab, the player list isn't updated in the lobby for every other user (this worked on localhost without an issue).
I get that the 400 error means the server got something from the client request it couldn't understand or didn't like but I'm not sure what that could be when it worked fine on localhost.
If there's any important information I left out please let me know. This is my first time posting a question so I'm not too familiar with best practices yet.
I'm trying to use gun in an express/node project, however I want to mount the endpoint as /db. I had hoped the following code would work but keep getting a Route.get() requires callback functions error:
var express = require('express');
var Gun = require('gun');
var app = express();
var port = 8080;
var gun = new Gun({
file: './data.json'
});
// mount the gun db server
app.get('/db', gun.router);
// regular express route
app.get('/', function(req, res) {
res.send('other stuff...');
});
// start the server
app.listen(port, function () {
console.log('Web server listening on port ' + port);
});
Any suggestions?
Doherty!
GUN can be used with express, but it is not an express route. For example, lets first go over a simple gun server mounted with express:
var express = require('express');
var Gun = require('gun');
var app = express();
app.use(Gun.serve).use(express.static(__dirname));
var server = app.listen(80);
Gun({file: 'data.json', web: server});
( https://github.com/amark/gun/blob/master/examples/express.js )
GUN's API is now available in the browser at:
<script src="http://YOURSERVER.com/gun.js"></script>
<script>
var gun = Gun('http://YOURSERVER.com/gun');
gun.get('key').get('hello').put('world!');
gun.get('key').get('hello').on(function(data){ console.log(data) });
</script>
GUN is not available as an express route. For example, this does not work:
http://YOURSERVER.com/data/key/hello?put=world!
Why?
GUN is a realtime database, if you use a REST or CRUD routes with express as its API, then you lose the realtime capabilities. Meaning you would have to write your own custom long-polling implementation, which defeats the point of having an express route.
I understand, but I still want a REST or CRUD API for GUN?
It should not be hard to create an HTTP route that proxies gun. Here is some pseudocode that should help get you started. If you build it, please make it an Open Source module on NPM so others can enjoy it!!!
// this is pseudocode!!!
app.get('/data', (req, res) => {
path(req).val(data => res.send(data))
});
app.put('/data', (req, res) => {
path(req).put(req.param.put, ack => {
res.ack? 0 : res.ack = res.send(ack)
})
});
var path = (req) => {
var ref = gun;
req.path.split('/').forEach(key => ref = ref.get(key));
return ref;
}
Let us know if you build it! As always, the community chatroom is friendly and active. Ask for help there, and ask questions here. Thanks for making this a SO question!
I'm trying to add instant messaging to an existing app. But I'm not sure how should configure socket.io module.
I've already tried the following:
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
const config = require('./config/config.js');
var expressConfig = require('./config/express.js').front,
models = require('./app/models'),
passConfig = require('./config/passport-front.js');
const app = expressConfig();
const passport = passConfig();
app.set('port', config.frontPort);
const http = require('http').Server(app);
const io = require('socket.io')(http);
io.on('connection', function (socket) {
console.log('Connected');
});
models.sequelize.sync().then(function () {
var server = http.listen(app.get('port'), function () {
console.log('Express server listening on port ' + server.address().port);
});
});
module.exports = http;
Since connected is not being logged when I open a page I'm assuming something is missing. Where is my mistake?
You didn't show your client-side code so it's hard to tell you what's wrong. Do you get any error in the browser? What is the network traffic on the browser? Without that it's hard to tell anything specific.
Take a look at this answer:
Getting data from/writing data to localhost with Express and Socket.io
It has a working example of something similar to what you're trying to do.
In general - remember that you need to include the socket.io client-side code in the browser:
<script src="/socket.io/socket.io.js"></script>
and then you need to connect with io() with something like:
var s = io();
s.on('color', function (color) {
document.body.style.backgroundColor = color;
});
The 'color' here is just an example that comes from this project on GitHub:
https://github.com/rsp/node-live-color
It's an example of a website that changes colors selected in messages that come vie socket.io.
Please excuse any noobiness, I'm learning. :)
I have Socket.IO set up so that I can use io.sockets.emit inside of my routes, and I have that working. There are a few problems.
(SOLVED? SEE EDIT 3) To use, I cannot start with the word socket. I have to start with ioor I get "ReferenceError: socket is not defined." I'd like to be able to use socket.broadcast.emit to emit the event to all clients except for the current user. Right now I'm having to do a check on the client side to not execute the event if it's the current user and it's becoming a real headache as I'm having to emit more events as my project progresses.
(SOLVED, SEE EDIT 1 & 2) I have to run the application with node app.js and restart the server manually every time I make a server-side change. When I run nodemon, I get "Port 3000 is already in use." I feel that this must be related to the following...
(SOLVED, SEE EDIT 2) When pushing to Heroku, I have the port from the code below changed from 3000 to 80 in bin/www and app.js, but it does not work (I can see a 404 error for sockets in the console). If this and #2 are caused by dealing with http/ports in both places, how do I properly set this up and why does node app.js work?
I only need to run Socket.IO on the route shown below (battlefield). Am I already doing this with am I already doing this with require('./routes/battlefield')(io)?
bin/www
var app = require('../app');
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
app.js
var app = express();
var http = require('http').Server(app);
http.listen(3000);
var io = require('socket.io')(http);
app.set('socketio', io);
var battlefield = require('./routes/battlefield')(io);
battlefield.js
var express = require('express');
var router = express.Router();
var returnRouter = function(io) {
router.get('/', function(req, res, next) {
// other stuff
io.sockets.emit('message', 'This works');
socket.broadcast.emit('message', 'Socket is undefined');
})
return router;
};
module.exports = returnRouter;
I tried wrapping my routes in io.on('connection', function (socket) { to be able to use socket, and instead of 'Socket is undefined,' the event does not occur.
var returnRouter = function(io) {
io.on('connection', function (socket) {
router.get('/', function(req, res, next) {
// other stuff
socket.emit('message', 'This is never emitted');
})
})
return router;
};
I apologize for such a lengthy question. THANK YOU for any help! 💜
EDIT1: Writing out this question helped me understand the problem better. I commented out server.listen(port); in my bin/www and nodemon now works. However, the app crashes on Heroku. My Procfile is web: node ./bin/www... does that need to be changed?
EDIT2: After figuring out Edit1 and a bit of Googling, I found that I can't have server.listen(); (bin/www) and http.listen(3000); (app.js).
In bin/www, I removed server.listen();.
In app.js, for clarity's sake I changed var http = ... to var server = ... and had it listen for process.env.PORT || '3000';, taken from bin/www. I also removed app.set('socketio', io); because it looks like that was doing nothing... I wonder why it was in there.
app.js
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var port = process.env.PORT || '3000';
server.listen(port);
This also makes Heroku work because of process.env.PORT, hurray! I'm guessing node app.js worked because I was initializing the app with app.js, I guess bin/www is not executed when you do that?
I still need help with #1 (using socket.broadcast.emit) 😇.
EDIT 3: Well, it took me literally the entire day but I believe I have it figured out with one quirk. Of course I couldn't use socket, it is a parameter given on connect. I also need to access socket across different routes and found this SO question. This is what I ended up doing in battlefield.js:
var returnRouter = function(io) {
var socket;
router.get('/', authenticatedUser, function(req, res, next) {
io.on('connection', function(client){
socket = client;
});
// other stuff
res.render('battlefield', {/* data */});
setTimeout(function(){socket.emit('test', 'It works!')}, 500);
});
router.post('/', function(req, res, next) {
// socket can be accessed
});
return router;
};
module.exports = returnRouter;
(Note: I took out a lot of my own code so I don't know if this is copy and pasteable ready, and you should probably check that socket is not null)
Without setTimeout, socket is undefined on GET '/'. To my understanding, the page must render first... Strange that 200 sometimes doesn't work for me and 500 does. I can leave it at 500, but this is for a game so time is pretty important.
My questions now are:
Can this be improved / is there a way I can do this without setTimeout? Am I 'connecting' clients properly with this code and am I (question #4 up there^) using Socket.IO efficiently?
P.S. If no one answers ^ these questions, I'll edit this, answer the question, and accept my answer as best answer.
When you use sockets when doing routing in Node its not that useful.
When ever you navigate to a different name space (eg www.example.com --> www.example.com/some-name-space) your front end variables are deleted and you need to resend them. This works great if you pass an object along with the GET request for that name space. But it doesn't need sockets.
Its done like this on your router file
var canAlsoBePassed = {some: "things"};
router.get('/', function(req, res, next) {
res.render('index', { items: "Can be passed directly", variables: canAlsoBePassed });
});
For sockets the best kind of applications are for single page apps or to replace AJAX requests. Another great thing sockets allows is for the server to be able to push information without the client asking for it.
To answer your question about SetTimeout, no you dont need this.
Make sure the socket script running on your client side is waiting for the document to be loaded.
$(document).ready(function() {
When an io.on('connection' event fires on your server side you know you have a new client to serve.
emit an event from the server side something like a welcome event that makes the client join a specific room. Once you have them in that room you can be listening for any events emitted to that room.
See socket.io official info
Custom namespaces
To set up a custom namespace, you can call the of function on the server-side:
var nsp = io.of('/my-namespace');
nsp.on('connection', function(socket){
console.log('someone connected'):
});
nsp.emit('hi', 'everyone!');
On the client side, you tell Socket.IO client to connect to that namespace:
var socket = io('/my-namespace');
Might not be the most accurate answer to your questions but I hope it pushes you in the right direction.
I'm pretty new to NodeJS and trying to get Socket.IO running with an Express application. I've tried to stick to the docs and tutorials as close as possible, but in the end, socket.io.js cannot be found on client side.
The server is being started as follows:
var app = require('../app');
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() { debug('...') } );
Routing/serving pages:
var express = require('express');
var router = express.Router();
router.get('/', function (req, res) {
res.render('index', { title: 'Express' });
});
Including socket.io:
var express = require('express');
var http = require('http').Server(express);
var io = require('socket.io')(http);
And in the HTML file, for clients:
<script src="/socket.io/socket.io.js" type="text/javascript">
But when loading the page, this JS-file is never found. I suspect that including socket.io via that previously created http server is somehow wrong... Or am I missing something else?
Update
I just found out that the snipped that includes socket.io is executed before the server is started (first snippet). They're in different files and the server-start one (named www.js) is configured as "start" script in package.json, the other one (websockets.js) required indirectly by that.
If I put
var io = require('socket.io')(server);
at the end of www.js, everything works just fine. But is there a way to do it from websockets.js, which is loaded before, e.g. by a server start callback function?
Try specifying the full path, like this
<script src="http://localhost:8000/socket.io/socket.io.js"></script>
And connect to your server from client side:
var socket = io.connect('http://localhost:8000/');
also check if socket.io module is available in your node_modules directory.