I need socket.io instance in several places in my app. To achieve this in express i can do this:
app.set('io', io);
In koa right now i have this:
app.use( async ( ctx, next ) => {
ctx.io = io;
await next();
});
This works, but this middleware executes every time my server recieves request. Is there a better way to do this?
I don't know how you are fully implementing but there are a couple things that you can do is you can either pass an addition argument and upgrade the connection to a websocket that will bypass the rest of the middlewares. Or, what I do personally is just have any websocket connection go to a different end point. This will help with any future scalability issues. for example, if you need to create clusters of your server then you will have more control as well will help you testing your backend easier. That's what I would do atleast. My socket.io back end looks like this:
server.ts
oh yea I'm using typescript in the back end
require('dotenv').config({ path: __dirname + '/.env' });
import Koa from 'koa';
const koa = new Koa();
import cors from '#koa/cors';
const PORT = process.env.CHAT_PORT || 3000;
const ENV = process.env.NODE_ENV || 'development';
const server = require('http').createServer(app, { origins: 'http://server.ip' });
const io = (module.exports.io = require('socket.io')(server));
import SocketManager from './lib/SocketManager';
app.use(
cors({
origin: '*',
optionsSuccessStatus: 200,
}),
);
// server setup
server.listen(PORT, (err: ErrorEvent): void => {
if (err) console.error('❌ Unable to connect the server: ', err);
console.log(`💻 Chat server listening on port ${PORT} - ${ENV} environment`);
});
io.on('connection', SocketManager);
then just create a socket manager that imports the io instance and you can then go ahead and handle all the connections.
I hope this is the answer you were looking for/gave you some better insight.
Related
I'm creating my product and stuck with this problem. One day I setuped socket.io and everything worked well. On the next day I migrate my server and client from http to https. After the migration client side and server side still connected, but I can't emit from client side to server and from server to client.
Server side
I have my ssl certificate inside ./security/cert.key and ./security/cert.pem they are loading correctly. My server running on https://localhost:5000
import fs from "fs";
import https from "https";
import socketio from "socket.io";
import express from "express";
// HTTPS optiosn
const httpsOptions = {
key: fs.readFileSync("./security/cert.key"),
cert: fs.readFileSync("./security/cert.pem"),
};
// Setup express and https server
const app = express();
const server = https.createServer(httpsOptions, app);
// Setup socket io
const io = socketio.listen(server, {
origins: "https://localhost:3000",
transports: ["websocket"],
});
server.listen(5000, () => {
console.log(`server listening on https://localhost:5000`);
});
io.listen(server);
io.on("connection", (socket) => {
console.log("new socket connected!");
console.log(`data = ${socket.handshake.query.data}`);
socket.emit("some-event");
socket.on("some-event-2", () => console.log("some-event-2 happened!"));
});
Client Side
My example react component. My react app is running on https://localhost:3000. HTTPS is connected and working well.
import React from "react";
import io from "socket.io-client";
const Sandbox: React.FC = () => {
const query = {
"data": 123,
};
const socket = io.connect("https://localhost:5000", {
secure: true,
query,
transports: ["websocket"],
});
socket.on("connect", () => console.log("connect!"));
socket.on("some-event", () => console.log("some event happened"));
socket.emit("some-event-2");
return <React.Fragment />;
};
export default Sandbox;
And now the problem. On client side in console I should see connect! and some event happened
And on server side I should see the messages new socket connected! and data = 123, some-event-2 happened!. But instead my client side console is completely clear
And server side console have only a few logs, but dont contains emit logs
What should I do? Maybe I'm incorrectly using socket.io with https?
I fixed my error.
The problem was that I was firstly create https server and after that only call .listen() on it. listen() - is not a void, it's return another server obj. You need to pass the result of .listen() function inside your io.listen()
// Don't do that❌
var server = https.createServer(options, app);
server.listen(5000);
io.listen(server);
// Do that✅
var server = https.createServer(options, app).listen(5000);
io.listen(server);
I have a simple socket.io app and it works just fine on local and also it's installed successfully on AWS server using plesk admin dashboard but when I connect to the app I always get forbidden {"code":4,"message":"Forbidden"} .. the entry point seems to work great http://messages.entermeme.com .. any idea what could be wrong with it ?
Frontend code
import io from 'socket.io-client'
const socket = io('https://messages.entermeme.com', {
transports: ['polling'],
})
socket.emit('SUBSCRIBE')
Backend code
const cors = require('cors')
const app = require('express')()
const server = require('http').Server(app)
const io = require('socket.io')(server)
server.listen(9000)
app.use(cors())
io.set('transports', [
'polling'
])
io.origins([
'http://localhost:8000',
'https://entermeme.com',
])
io.on('connection', (socket) => {
socket.on('SUBSCRIBE', () => {
//
})
})
had a similar issue but when using nginx. So in case you still need some help:
In the end it turned out to be the URL I specified as socket origins. I didn't specify the port since the origin for me was also running on port 80 (443 for SSL) like in your example above:
io.origins([
'http://localhost:8000',
'https://entermeme.com', // <--- No port specified
])
I updated my config and added the port. So for you it would be:
io.origins([
'http://localhost:8000',
'https://entermeme.com:80', // <--- With port (or 443 for SSL)
])
I'm setting up my dev system to use https, and Chrome is complaining about my websocket not begin secure:
VM4965:161 Mixed Content: The page at 'https://mywebsite.io/' was
loaded over HTTPS, but attempted to connect to the insecure WebSocket
endpoint 'ws://mywebsite.io:4000/subscriptions'. This request has
been blocked; this endpoint must be available over WSS.
Here's my current server-side setup for WS, based on the Apollo docs:
const localHostString = 'mywebsite.io';
const pubsub = new PubSub();
// additional context you use for your resolvers, if any
const context = {connectors: connectors};
//SET UP APOLLO QUERY / MUTATIONS / PUBSUB
//start a graphql server with Express handling a possible Meteor current user
createApolloServer({
schema,
context
});
const METEOR_PORT = 3000;
const GRAPHQL_PORT = 4000;
const server = express();
server.use('*', cors({ origin: `https://${localHostString}:${METEOR_PORT}` }));
server.use('/graphql', bodyParser.json(), graphqlExpress({
schema,
context
}));
server.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
subscriptionsEndpoint: `ws://${localHostString}:${GRAPHQL_PORT}/subscriptions`
}));
// Wrap the Express server
const ws = createServer(server);
ws.listen(GRAPHQL_PORT, () => {
console.log(`GraphQL Server is now running on http://${localHostString}:${GRAPHQL_PORT}`);
console.log(`GraphiQL available at http://${localHostString}:${GRAPHQL_PORT}/graphiql`);
// Set up the WebSocket for handling GraphQL subscriptions
new SubscriptionServer({
execute,
subscribe,
schema
}, {
server: ws,
path: '/subscriptions',
});
});
How can I update this so as to use WSS rather than WS websockets?
Thanks in advance to all for any info.
It looks like you're doing
subscriptionsEndpoint: `ws://${localHostString}:${GRAPHQL_PORT}/subscriptions
Maybe instead, change ws to wss. ie:
subscriptionsEndpoint: `wss://${localHostString}:${GRAPHQL_PORT}/subscriptions
I want to use the websocket in my RN project. And I did it using the ws at server side and the RN built-in websocket implementation.
But it seems not so convinient since I use socket.io before.
So I tried to use socket.io:
In RN:
import './userAgent'
import io from "socket.io-client/socket.io"
In component:
componentDidMount() {
this.socket = io('https://localhost:4080',{jsonp: false});
this.socket.on('hello', (msg) =>{
this.setState({response:msg})
});
}
In the server:
io.on('connection', function(socket){
socket.emit('hello','hello world')
console.log('a user connected');
socket.on('disconnect',function(){
console.log('user disconnected')
})
})
And in userAgent.js
window.navigator.userAgent = 'react-native';
That is just the result I googled and they said it will work. But for me, the chrome debugger stopped at:
function load() {
var r;
try {
r = exports.storage.debug;
} catch(e) {}
return r;
}
And it says the storage is not defined.
Then I looked into the socket.io.js and find that the exports.storage is window.localStorage. So I disabled the remote js debug, and the code began running.
But the server continues to log : a user connected . as if my RN app is keeping on connecting to the server. And it seems the socket.on() did not work at client side.
react-native version:0.27.2
socket.io-client version:1.4.8
Anyone knows where is going wrong?
Well,finally I found the solution after looking through the socket.io source.
It seems that the socket.io does not use 'websocket' as transport defaultly. It will use the 'polling' in my case, so just explicityly set it :
componentDidMount() {
var socket = io('http://localhost:4080', { jsonp: false, transports: ['websocket'] })
socket.on('hello', (msg) => {
//do something
});
}
Now it works.
But what still confuses me is that in brower client I do not set the transports and it just work well but in react-native it doesn't. Not figured out why.
I have the current setup:
Nodejs Proxy (running http-reverse-proxy) running on port 80.
Rails server running on port 3000
Nodejs web server running on port 8888
So any request starting with /nodejs/ will be redirected to nodejs web server on 8888.
Anything else will be redirected to the rails server on port 3000.
Currently Socket.io requires a connection url for io.connect.
Note that /nodejs/socket.io/socket.io.js is valid and returns the required socket.io client js library.
However, I am not able to specify connection_url to /nodejs/ on my server.
I have tried http://myapp.com/nodejs and other variants but I am still getting a 404 error with the following url http://myapp/socket.io/1/?t=1331851089106
Is it possible to tell io.connect to prefix each connection url with /nodejs/ ?
As of Socket.io version 1, resource has been replaced by path. Use :
var socket = io('http://localhost', {path: '/nodejs/socket.io'});
See: http://blog.seafuj.com/migrating-to-socketio-1-0
you can specify resource like this:
var socket = io.connect('http://localhost', {resource: 'nodejs'});
by default resource = "socket.io"
If you are using express with nodejs:
Server side:
var io = require('socket.io')(server, {path: '/octagon/socket.io'});
then
io.on('connection', function(socket) {
console.log('a user connected, id ' + socket.id);
socket.on('disconnect', function() {
console.log('a user disconnected, id ' + socket.id);
})
})
socket.on('publish message ' + clientId, function(msg) {
console.log('got message')
})
Client side:
var socket = io('https://dev.octagon.com:8443', {path: '/octagon/socket.io'})
then
socket.emit('publish message ' + clientId, msg)
I use below approach to achieve this goal:
client side:
var socket = io.connect('http://localhost:8183/?clientId='+clientId,{"force new connection":true});
server side:
var io = require('socket.io').listen(server);
io.sockets.on('connection', function(socket) {
console.log("url"+socket.handshake.url);
clientId=socket.handshake.query.clientId;
console.log("connected clientId:"+clientId);
});
reference:https://github.com/LearnBoost/socket.io/wiki/Authorizing#global-authorization
If you are serving your app with express, then maybe you can check this out. Remember express uses http to serve your application.
const express = require('express'),
http = require('http'),
socketIo = require('socket.io'),
app = express()
var server = http.createServer(app);
var io = socketIo(server);
io.on('connection', (socket)=>{
// run your code here
})
server.listen(process.env.PORT, ()=> {
console.log('chat-app inintated succesfully')
})