Socket.io is not connecting Node.js server to React.js client - javascript

So what I am trying is to establish a socket.io connection between an express server and a react client. Maybe also mentionable the client is hosted on netlify and the server on heroku.
The actual problem is the client seems to connect and receives the upgrade protocol to websocket messages with a HTTP 101. But nothing else happens afterwards. No "connection" or "connected" event is emitted. Maybe some one spots a mistake in the code or can guide me to the solution.
Server
/**
* App Configuration
*/
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server, {
cors: {
origin: process.env.CLIENT_URL,
methods: ["GET", "POST"],
credentials: true
}
});
/**
* Socket IO
*/
io.on("connection", (socket: any) => {
console.log("Client Connected via Socket");
// Join a conversation
const { deviceId } = socket.handshake.query;
socket.join(deviceId);
// Leave the room if the user closes the socket
socket.on("disconnect", () => {
socket.leave(deviceId);
});
});
/**
* Server Activation
*/
app.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}`);
});
Client
const socketRef = useRef();
useEffect(() => {
// Creates a WebSocket connection
socketRef.current = io(process.env.REACT_APP_API, {
transports: ['websocket'],
query: { deviceId: 'test-id' }
});
socketRef.current.on('connect', () => {
console.log("connected");
});
// Listens for incoming messages
socketRef.current.on("message", (message) => {
console.log(message);
});
// Destroys the socket reference
// when the connection is closed
return () => {
socketRef.current.disconnect();
};
}, []);
Edit:
Adding an image of the recurring http message about the websocket upgrade.
HTTP Message

Change Server activation line to below
server.listen(process.env.PORT, () => {
console.log(`Listening on port ${process.env.PORT}`);
})

Related

Firefox can’t establish a connection to the server at wss://localhost:8000/

I am using nodejs to run the server, there is no log file
This is my server.js
const https = require('https');
const fs = require('fs');
const ws = require('ws');
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
};
const wss = new ws.Server({noServer: true});
function accept(req, res) {
// all incoming requests must be websockets
if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != 'websocket') {
res.end();
return;
}
// can be Connection: keep-alive, Upgrade
if (!req.headers.connection.match(/\bupgrade\b/i)) {
res.end();
return;
}
wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onConnect);
}
function onConnect(ws) {
ws.on('message', function (message) {
let name = message.match(/([\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]+)$/gu) || "Guest";
ws.send(`${name}!`);
//setTimeout(() => ws.close(1000, "Bye!"), 5000);
});
}
https.createServer(options, function (req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
This is my code in react
componentDidMount() {
var connection = new WebSocket('wss://localhost:8000/');
connection.onopen = function(e) {
connection.send("add people");
};
connection.onmessage = function(event) {
// alert(`[message] Data received from server: ${event.data}`);
console.log("output ", event.data);
};
}
While I am trying to connect with web-socket with my jsx file its give me an error which is Firefox can’t establish a connection to the server at wss://localhost:8000/.
Your implementaion needs some changes. In the backend server, you forgot to call the onConnect function. So your ws.on method will never call.
Also, you imported the ws and create a WebSocket server wss, but you add some event listener on ws wrongly, you should add listener on your Websocket instance (wss):
// rest of the codes ...
const was = new ws.Server({noServer: true})
wss.on('connection`) {
// do something here ...
}
// rest of the codes ...
https.createServer(options, () => {
// do something here ...
})
There are some examples of how to create the WebSocket server along with the HTTP server on ws npm page.

Websocket connection error: returns 101, but does not upgrade

I am setting up some websockets using ws library. I am struggling to set up authorisation using a handshake. I have added a route to our server to upgrade to a websocket connection like so:
.get(
'/chat',
authorisationFunction,
upgradeConnection,
),
The websocket server:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3030 });
This is the upgradeConnection function, which will run if authorisation is successful:
const upgradeConnection = (request, socket, head) => {
return wss.handleUpgrade(request, request.socket, head, function done(ws) {
return wss.emit('connection', ws, request);
});
}
I also have a function that listens to messages:
function webSocketsServer() {
wss.on('connection', (ws, request, client) => {
ws.on('message', message => {
ws.send(message);
});
});
}
A connection gets emitted, and from my server I get this response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: QyVvqadEcI1+ALka6j2pLKBkfNQ=
but then immediately on my client I get the error “WebSocket connection to 'ws://localhost:3000/chat’ failed: Invalid frame header”.
But when I bypass the handshake and connect directly to my websocket server, I can send messages successfully. The error is only on the client and not on the backend. What am I missing?
I am not 100% sure it is the only way but might help so I post it.
Based on this answer I would go for a server that uses the same port for http and websocket connections.
You can achieve it like this:
const { createServer } = require('http')
const ws = require('ws')
const express = require('express')
const app = express()
const server = createServer(app)
app.get('/', (req, res) => {
res.send('I am a normal http server response')
})
const wsServer = new ws.Server({
server,
path: '/websocket-path',
})
wsServer.on('connection', (connection) => {
connection.send('I am a websocket response')
})
server.listen(3030, () => {
console.log(`Server is now running on http://localhost:3030`)
console.log(`Websocket is now running on ws://localhost:3030/<websocket-path>`)
})
So your server listens on port 3030 for normal http requests. If it gets a websocket connection request on path '/websocket-path' it is passed to the the ws connection handler and from there you are good to go.

How to Send Streamed UDP Packets to Browser Using SSE in Node JS?

I have a node js server that listens to audio that is streamed on a local network using VBAN protocol. VBAN, basically a protocol that sends audio stream over local network using UDP. You can read more about VBAN and it's applications here
The next step, after receiving the audio from the VBAN, the node js server process the received audio.
Also, the node js server should send the processed packages to the browser, in this case, using EventEmitter, through a GET request:
const EventEmmitter = require("events");
const stream = new EventEmmitter();
app.get("/stream", function (req, res) {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
stream.on("push", function (event, data) {
res.write(`event: ${event}, data: ${JSON.stringify(data)}`);
});
});
In the front-end side, which is an Angular application, receives the data from the server using EventSource
public dataObservable(): Observable<MessageEvent> {
return Observable.create((observer) => {
let eventSource = new EventSource('http://localhost:3000/stream');
eventSource.onmessage = (event) => {
console.log('Received event: ', event);
const json = JSON.parse(event.data);
console.log("audio", json);
observer.next(json);
};
eventSource.onerror = (error) => {
if (eventSource.readyState === 0) {
console.log('The stream has been closed by the server.');
eventSource.close();
observer.complete();
} else {
observer.error('EventSource error: ' + error);
}
};
});
}
From the inspector, in the Network, it shows that the browser is receiving the data from the server, but there is no data being emitted from the EventSource.
I'm not sure whether I'm using the wrong approach or I have the wrong implementation of this approach. I would appreciate your help.
I was able to send the processed packages to the front-end side using Socket IO.
Here's the server-side implementation (node js):
first of all, I installed socket.io dependency into node js app:
npm install socket.io
Then, to initialize the socket.io:
var express = require("express");
var app = express();
const http = require("http").createServer(app);
const io = require("socket.io")(http);
And here I've configured the HTTP get request, and make the app listing to the specified port:
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "http://localhost:4200");
res.header("Access-Control-Allow-Credentials", true);
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.get("/", (req, res) => {
res.send("<h1>Hey Socket.io</h1>");
});
http.listen(3000, () => {
console.log("Server running on port 3000");
});
Next, emitting the audio messages using the socket.io
io.on("connection", (socket) => {
console.log("a user connected");
socket.on("disconnect", () => {
console.log("user disconnected");
});
});
io.on("connection", () => {
let dgram = require("dgram");
let server = dgram.createSocket("udp4");
server.on("listening", () => {
let address = server.address();
console.log("server host", address.address);
console.log("server port", address.port);
});
server.on("message", function (message, remote) {
let audioData = vban.ProcessPacket(message);
io.emit("audio", audioData); // // console.log(`Received packet: ${remote.address}:${remote.port}`)
});
server.bind(PORT, HOST);
});
In the front-end (Angular):
I've added socket.io dependency into the Angular app:
npm install socket.io-client
Finally, I've added a listener for my audio event:
export const environment = {
production: false,
SOCKET_ENDPOINT: 'http://localhost:3000'
};
setupSocketConnection() {
var socket = io(environment.SOCKET_ENDPOINT);
socket.emit('my message', 'Hello there from Angular.');
socket.on('audio', (audio) => {
console.log('audio:' ,audio);
this.playAudio(audio);
});
}
Reference to the source which I've used to implement the socket.io
I still want to know if I can use EventEmitter to send the data to the client app.

how to use http server and socket connection together

I am using http server with socket connection response of http request goes when client responds to socket connection i am currently doing it by storing res in a global object which i think is not a correct method, what is the correct method to do it.
const app = require('express')()
bodyParser = require('body-parser')
app.use(bodyParser.json())
const net = require('net');
var client;
var res1,currentReq;
//----------------------------------------------------------------------------
// http requests listener
//----------------------------------------------------------------------------
app.listen(8001, () => console.log('Http server listening on port 8001'));
//----------------------------------------------------------------------------
// http requests handling
//----------------------------------------------------------------------------
app.post('/makeCall', (req, res) => {
console.log('sd' + req.body)
res1 = res;
currentReq='makeCall';
client.write("something");
});
//----------------------------------------------------------------------------
// Establishing tcp connection for incoming requests
//----------------------------------------------------------------------------
var server = net.createServer(function(connection) {
console.log ('client has connected successfully!');
client = connection;
client.on('data',function(data){
switch(currentReq)
{
case 'makeCall' :
res1.end(data);
break;
}
});
});
//----------------------------------------------------------------------------
// listener for tcp connections
//----------------------------------------------------------------------------
server.listen(8000, function() {
console.log('server for localhost is listening on port 8000');
});
Based on our discussion in comments, here is an example of how one might do this using socket.io.
Using a socket.io set-up, the browser will connect to the HTTP server to make HTTP GET AND POST requests, e.g. to /makeCall. The browser will also connect to the same HTTP server to open the socket. The browser will connect by HTTP initially, then send a special handshake which will convert the connection into a WebSocket.
I still am not sure to understand your use case so perhaps this solution is not appropriate, but here is the general setup of what it might look like.
const app = require('express')()
const bodyParser = require('body-parser')
const server = require('http').Server(app);
const io = require('socket.io')(server);
const cookieSession = require('cookie-session')
app.use(bodyParser.json())
app.use(cookieSession({
keys: ['secret key', 'another one', 'a third one'],
maxAge: 24 * 60 * 60 * 1000 // cookie max-age : 24 hours
}))
//----------------------------------------------------------------------------
// http requests listener
//----------------------------------------------------------------------------
server.listen(8001, () => console.log('Http server listening on port 8001'));
// WARNING: app.listen() will NOT work here! per socket.io docks
const idToSocketMap = new Map();
//----------------------------------------------------------------------------
// http requests handling
//----------------------------------------------------------------------------
app.post('/makeCall', (req, res) => {
console.log('sd' + req.body)
if (!req.session.id) {
req.session.id = generateId();
}
//this may not be necessary, see below
req.session.currentReq = 'makeCall';
const socket = idToSocketMap.get(req.session.id)
if (socket) { //if a socket connected for that id
socket.emit("makeCall", "an argument/parameter", "something");
}
});
//----------------------------------------------------------------------------
// Establishing websocket connection for incoming requests
//----------------------------------------------------------------------------
io.on('connection', (socket) => {
//"socket" is now what would have been called "client" in old code
//get the id from the session cookie of the http request
const id = socket.request.session.id;
//put the socket in the map
idToSocketMap.set(id, socket);
//remove the socket from the map on disconnect
socket.on('disconnect', () => idToSocketMap.delete(id));
console.log('client has connected successfully!');
socket.on('makeCall', (firstParameter, something) => {
// It's still not possible to send anything back to an HTTP connection
// but we may do some things and reply here
const someData = getSomeData();
socket.emit('makeCallResponse', 'someData');
});
});

How to make a socket.io connection between two different interfaces?

I'm actually trying to make a real-time connection between two different apps. I've found a bunch of tutorials about how to make a chat using socket.io, but that doesn't really help me since it's just the same app duplicated in multiple windows.
I'm making a pick & ban overlay for League of Legends in local development. My first thought was to display the empty overlay on one hand and create an interface to manually update it on the other hand. Socket.io seems to be the right thing to use in my case since it can provide new data without having to reload the component.
This is what I wrote in both apps :
const express = require('express');
const socket = require('socket.io');
// App setup
const app = express();
const server = app.listen(4200, function () {
console.log('Listening to requests on port 4200')
});
// Static files
app.use(express.static('public'));
// Socket setup
const io = socket(server);
io.on('connection', function (socket) {
console.log('Made socket connection', socket.id);
socket.on('change', function (data) {
io.sockets.emit('change', data);
});
});
But I fail to connect them as they have to listen to the same port. What am I doing wrong?
(Forgive my bad English and lack of syntax, I'm doing my best here. :p)
I am certainly not an expert on network programming, but as far as I know you need to have one listening app (backend) and another one to connect to it (client). And you define what happens with all the data (messages) that backend recieves (for example sending the messages it recieves to all the clients in the same chat room).
If I am correct to assume you are trying to connect two listening apps?
simple google search of "nodejs socket server client example" revealed this https://www.dev2qa.com/node-js-tcp-socket-client-server-example/ might wanna take your research in this direction
u can try something like this way
var express = require('express');
var socket = require('socket.io');
// App setup
var app = express();
var server = app.listen(8080, () => {
console.log('App started')
})
// Static file
app.use(express.static('public'))
// Socket SetUp
var io = socket(server);
io.on('connection', socket => {
console.log('made the connection')
socket.on('chat',data => {
io.sockets.emit('chat',data)
});
socket.on('typing',data => {
socket.broadcast.emit('typing',data);
});
})
create another file and
var socket = io.connect('http://localhost:8080')
// Elenment
var message = document.getElementById('message');
handle = document.getElementById('handle');
btn = document.getElementById('send');
output = document.getElementById('output');
feedback = document.getElementById('feedback');
// Emit Events
btn.addEventListener('click', () => {
socket.emit('chat', {
message: message.value,
handle: handle.value
})
})
message.addEventListener('keypress', () => {
socket.emit('typing', handle.value)
})
socket.on('chat',data => {
feedback.innerHTML = '';
output.innerHTML += '<p><strong>' + data.handle +': </strong>' +
data.message + '</p>'
})
socket.on('typing', data => {
feedback.innerHTML = '<p><emp>' + data + ' is typing a message... </emp></p>'
})
details are given here node socket chat app
Ok, figured it out. Here's how it works using express and vue together :
First, setup socket.io in your express server js file :
const express = require('express')
const { Server } = require('socket.io')
const http = require('http')
const app = express()
const server = http.createServer(app)
const io = new Server(server, {
cors: {
origin: '*',
methods: ['GET', 'POST', 'REMOVE']
}
})
const PORT = process.env.PORT || 8080
io.on('connection', (socket) => {
console.log('New socket user')
socket.on('SEND_MESSAGE', data => {
console.log('received message in back')
io.emit('MESSAGE', data)
})
})
server.listen(PORT, () => { console.log(`Server started on port : ${PORT}`)})
As you can see we received from the client "SEND_MESSAGE" and we trigger MESSAGE from the server to forward the information to all the clients. The point I was missing is that we bind SEND_MESSAGE on the socked created from the connection but we emit from the io server.
Now you vue part :
import io from 'socket.io-client'
export default {
data() {
return {
messages: [],
inputMessage: null,
socket: io('http://localhost:8080')
}
},
mounted() {
this.socket.on('MESSAGE', data => {
this.messages.push(data)
})
},
methods: {
sendMessage() {
const message = {
senderID: this.myID,
message: this.inputMessage,
sendAt: new Date()
}
this.socket.emit('SEND_MESSAGE', message)
this.inputMessage = null
},
},
}

Categories

Resources