socket.io - can't get it to work, having 404's on some kind of polling call
Asked Answered
S

6

43

I'm trying to get a server set up with socket.io, with bad results.

I am mostly following this guide, although it's somewhat out of date: http://www.williammora.com/2013/03/nodejs-tutorial-building-chatroom-with.html

The problem comes with socket.io, I'm not sure if it's client or server side. It appears to be trying to continuously poll the server, but is getting 404's back. That sounds like socket.io isn't running, but it all looks okay to me. It may also have something to do with paths and having a "public" directory, but I don't really know.

127.0.0.1 - - [Thu, 17 Jul 2014 00:51:36 GMT] "GET /socket.io/?EIO=2&transport=polling&t=1405558296120-0 HTTP/1.1" 404 73 "http://localhost:8080/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4"
127.0.0.1 - - [Thu, 17 Jul 2014 00:51:37 GMT] "GET /socket.io/?EIO=2&transport=polling&t=1405558297181-1 HTTP/1.1" 404 73 "http://localhost:8080/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4"
127.0.0.1 - - [Thu, 17 Jul 2014 00:51:39 GMT] "GET /socket.io/?EIO=2&transport=polling&t=1405558299207-2 HTTP/1.1" 404 73 "http://localhost:8080/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4"

Server

var exec = require( "child_process" ).exec;
var path = require( "path" );
var morgan = require( "morgan" );
var bodyParser = require( "body-parser" );
var _ = require( "underscore" );
var express = require( "express" );
var app = express();
var http = require( "http" ).createServer( app );
var io = require( "socket.io" )( http );

app.set( "ipaddr", "127.0.0.1" );
app.set( "port", 8080 );

//support JSON, urlencoded, and multipart requests
app.use( bodyParser() );

//log the requests using morgan
app.use( morgan() );

//specify the Jade views folder
app.set( "views", __dirname + "/../views" );

//set the view engine to Jade
app.set( "view engine", "jade" );

//specify static content
app.use( express[ "static" ]( "public", __dirname + "/../public" ) ); //using map-access of static so jslint won't bitch

io.on( "connection", function( socket )
{
    console.log( "A user connected." );
});

Client

script( src="js/socket.io.js" )
var socket = io.connect( "http://localhost:8080" );

I got the client js file from: node_modules/socket.io/node_modules/socket.io-client/socket.io.js

That path doesn't match either what the tutorial I'm using says, or what socket.io says to use, so maybe that's the problem, but it looks like it's probably the correct file.

Any help here?

EDIT For additional information, here is my folder hierarchy:

Webserver/
    my_modules/
        server.js
    node_modules/
        body-parser/
        express/
        jade/
        morgan/
        socket.io/
        underscore/
    public/
        css/
        js/
            server.js
    views/
        index.jade
    index.js
    package.json

The server.js in the my_modules folder is where I start socket.io on the server side. The server.js in the js folder is the client code to connect from the website.

Shantishantung answered 17/7, 2014 at 0:58 Comment(2)
Are you using nginx (or other webserver) in front of node? if so, it looks like it doesn't proxy to node properly.Nonie
Nope, just straight up node, with all the stuff I have listed in my requires (express etc). No nginx or apache.Shantishantung
N
81

It looks like Socket.IO can't intercept requests starting with /socket.io/. This is because in your case the listener is app -- an Express handler. You have to make http be listener, so that Socket.IO will have access to request handling.

Try to replace

app.set( "ipaddr", "127.0.0.1" );
app.set( "port", 8080 );

with

http.listen(8080, "127.0.0.1");

See docs for details: http://socket.io/docs/#using-with-express-3/4

Nonie answered 17/7, 2014 at 19:0 Comment(5)
You are the man! Thanks so much, that did the trick. I'll have to post a comment in the guide I was using.Shantishantung
Using http instead of app.listen ( in case of express ) fixed this for me, thank you.Blas
I have an express.Router used by app, and experienced the same problem. Letting http listen instead also solved my problem still keeping the app routing intact. Thanks a bunch @Curious! I did find the same page as you refer to, but somehow managed to overlook the solution! :DEzzo
Ohh, dear, you save my time, thanks a lot for your answer, I have tried to find solution for 2 days, thanks God I find your answer, Big + from meEwaewald
You're the best, you got me out of a big troubleBraid
C
7

In my case, I created my app with the Express application generator. To solve this problem, instead of edit the app.js file on the root folder of the project, I edited the file bin/www on the line after the server is defined:

/**
 * Create HTTP server.
 */

var server = http.createServer(app);
var io = require('socket.io')(server); // Add this here

Update

I found a better way here Express Generator and Socket.io

Cepheus answered 27/4, 2017 at 16:12 Comment(1)
Sure enough, this worked for me. I didn't even know about the bin/www folderGeosphere
H
3

Just for check that is your server or just client problem you could use this web: websocket-echo, if this client connect right to your server (the first form client is usefull if your server is online, if is on your host, cuoting from websocket.org...

Using a text editor, copy the following code and save it as websocket.html somewhere on your hard drive

Code: websocket-test

The only thing different from mine that i could observe is the io client source: <script src="/socket.io/socket.io.js"></script> on your client side.

On your server you should try this way:

var express = require('express'), 
app = express(),
http = require('http'),
server = http.createServer(app),
io = require('socket.io').listen(server),
exec = require('child_process').exec,
util = require('util');

//serve our code
app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded());
//listening on connections

io.on('connection', function (socket) {
    console.log('client connected!'); 
}
server.listen(8080);

Note that this way works fine with this dependencies: I recommend you add this code below to your package.json file:

"dependencies": {
    "express": "3.x.x",
    "socket.io": "*",
    "util": "*"
  }

cheers!

Hale answered 17/7, 2014 at 8:27 Comment(3)
Doesn't look like your code helps, and also you have an older version of express (json is no longer a dependency in my version and must be installed separately). How do I use websocket-echo? I tried putting in ws://localhost:8080 as the URL and it doesn't seem to connect.Shantishantung
Also when I try to use /socket.io/socket.io.js as the URL for the client code, I get a 404. My folder hierarchy is now edited into my post above.Shantishantung
yes, sorry about websocket-echo, seems to be useless if you are trying to reach a server running on your host, instead you should try your own test like this: [link] pastebin.com/NW3dWb2c ( property of www.websocket.org ), sorry if json line confuses you..is from my project :P . In order to use socket.io (client) you have to find where is installed your socket.io folder (generally on node-modules), in your case, i think that you need to put: /socket.io/socket.io.js . Again sorry if my code doesn't helped you.Hale
N
2

Re-posting this answer here, as no other answer worked for me and this one did the trick. It took me a while to find it, so hopefully I'm saving some of you time.

import io from 'socket.io-client'

const socket = io('http://localhost:3000', {
    reconnectionDelay: 1000,
    reconnection: true,
    reconnectionAttemps: 10,
    transports: ['websocket'],
    agent: false,
    upgrade: false,
    rejectUnauthorized: false
});
Napoleonnapoleonic answered 5/9, 2021 at 13:25 Comment(4)
In general if an answer on a thread suffices to answer a different question, the questions can be marked as a duplicate. Is that the case here, would you say?Plaque
@Plaque I wouldn't say so, but just because I don't think this answer answers this question (or the other question) directly. The accepted answer for both questions didn't solve my problem, but this answer did. Put another way: there might not be an SO question for this answer, but people with that question might show up here.Napoleonnapoleonic
@Plaque side note, beginner question: what is the reason to trim out voting advice? It was an effort to not 'steal' upvotes, but perhaps I missed some convention?Napoleonnapoleonic
On the side note, I'd give two suggestions: (1) that succinct technical writing is preferred, and (2) that voting should be allowed to be organic. I doubt that people vote based on advice anyway - where people ask for upvotes for themselves it is either routinely ignored, or they get a downvote.Plaque
U
1

NextJS and ExpressJS Users:

You may find it useful to know how to connect to a custom URL.

  1. The client and server must have the same path configured (/socket.io by default).
  2. The client must request the correct initializer URL (/socket.io?<querystring> by default)

If you need to create a custom endpoint for your WebSocket server, for example because you are implementing it inside of the NextJS API system, or you are otherwise implementing inside a restricted path structure, you must set the path on both the client and the server.

In this example,

  1. the client fetches an HTTP endpoint on the server at /api/socket,
  2. The server initializes the WebSocket server not already active. This happens because the HTTP endpoint loads the WebSocket server
  3. The client accesses the WebSocket server at /api/socket/socket.io

Note that the WebSocket takes the path variable and creates a new web server on ${path}/socket.io.

It's worth understanding that the WebSocket server also creates its own HTTP server at ${path}/socket.io, which a client connects to and receives JSON information on how to connect to the actual socket. This HTTP server is distinct from the ExpressJS or NextJS HTTP server.

Server

import { Server, Socket } from "socket.io";
// server is provided by expressJS or NextJS in this example
// NextJS: const server = req.socket.server;
// ExpresssJS: const server = http.createServer(app);

// pseudocode for a REST API handler at `/api/socket`
const httpRouteHandler = (request: Request, response: Response) {
  // if the WebSocket server is already running, do nothing
  if (server.io) {
    response.end();
    return; 
  }
  // Note that this variable represents the WebSocket server URL
  // which may or may not be the same as the REST API endpoint,
  // depending on your framework or organizational restrictions
  const socketRootUrl = "/api/socket";
  const io = new Server(server, { path: socketRootUrl });
  // connect events here
  // store the WebSocket server somewhere
  server.io = io;
  res.end();
}

Client

import io from "socket.io-client";

// this path must match the server's path setting
const socketRootUrl = "/api/socket";

// tell the server to open a WebSocket server
await fetch(socketRootUrl);

// connect to the WebSocket Server with the custom path
const socket = io({
  path: `${socketRootUrl}/socket.io`,
});

Testing

You can test that you are connecting to the right WebSocket HTTP server URL.

curl "https://example.com/api/socket/socket.io/?EIO=4&transport=polling"

If the connection was successful, you will get a JSON response:

0{"sid":"Lbo5JLzTotvW3g2LAAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}
Upali answered 15/6, 2023 at 22:15 Comment(0)
F
1

You shouldn't bind socket.io to the HTTP connection server. Always change app.listen to http.listen

Here's an example from my index.ts file:

// Express server
const app: express.Application = express();

// Node.JS HTTP server
const httpServer = createServer(app);

// Socket.IO server
const io = new Server(httpServer,{
  cors: {
    origin: "*",
  },
});

// Start server with socket.io and express attached
httpServer.listen(process.env.PORT || 8081, function () {
  console.log("App started at port:", process.env.PORT || 8081)
})

If you face any issues, Let me know, I'm always open to new discussions. Thanks

Forgave answered 25/8, 2023 at 6:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.