Node.js socket.io peer to peer communication for data sharing - node.js

I'm trying to build a simple peer to peer data sharing platform using node-fetch and socket.io.
Given the sender and receiver's ip, the sender transmits data to the receiver's computer using node-fetch.
The receiver captures the data using socket.io and can reply using the 'req.host' ip.
The code runs fine but the message sent is not received at the /receive endpoint.
What could be the problem?
Is there a better way to do this?
const fetch = require('node-fetch'); // Data fetch feature
const socket = require('socket.io');
const client = require('socket.io-client');
const express = require('express');
var ip = require("ip");
var senderIP =`${ip.address()}; // Test IP
// Express configurations
const app = express()
const port = 60553 // Processing Port
// Send parameters
var pcURL=`http://${senderIP}:${port}/receive`;
app.get('/send', async (req, res) => {
var msg ='Hello from Vakindu!';
// Send Message
let sendMsg = await fetch(pcURL, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({data: msg})
})
.then(() => {
return msg;
})
.catch(err => console.log(err));
res.end(sendMsg);
});
// Receive message
app.get('/receive', (req, res) => {
let newMsg='';
// Notification center
client(pcURL).on('', (msg) => {
newMsg=msg
});
res.end('New Message:'+newMsg);
});
// Port session listener
const server = app.listen(port, () => {
console.log(`P2P Network started on port ${port} ...!`)
})
// Socket setup
const io = socket(server);

Related

Express and Websocket to run on the same port on the same file

I'm running two apps that sends real-time messages to each other using websocket and also generate a random link using express.js, now i hosted the server with both react apps to my vps host and want to make the websocket connection secure (wss://) but i realize i'll have to get the express server on the same port too, so the ssl/tsl works for both - so how do i do that?
Here is my full code, all on the same file:
const webSocketServerPort = 8000;
const webSocketServer = require('websocket').server;
const http = require('http');
const server = http.createServer(); server.listen(webSocketServerPort); console.log('Listening on port 8000');
const wsServer = new webSocketServer({ httpServer: server })
//GEERTOOOO
const express = require('express'); const cors = require('cors'); const fs = require('fs'); const app = express();
app.use(cors({ origin: '*' }));
app.get('/', (req, res) => { // Generate a random 6-character string const linkId = Math.random().toString(36).substr(2, 6);
// Save the link in the lex.json file fs.readFile('lex.json', (err, data) => { if (err) { console.error(err); res.status(500).send('Error generating link'); return; }
const links = JSON.parse(data);
links[linkId] = {
destination: 'http://localhost:4000/',
expires: Date.now() + 1000 * 60 * 5 // expires in 5 minutes
};
fs.writeFile('lex.json', JSON.stringify(links), (err) => {
if (err) {
console.error(err);
res.status(500).send('Error generating link');
return;
}
// Send the link back to the client
res.send(`http://localhost:3000/${linkId}`);
});
}); });
app.get('/:linkId', (req, res) => {
fs.readFile('lex.json', (err, data) => {
if (err) { console.error(err); res.status(500).send('Error retrieving link');
return;
}
const links = JSON.parse(data);
const link = links[req.params.linkId];
if (!link) {
res.status(404).send('Link not found');
return;
}
// Check if the link has expired
if (link.expires < Date.now()) {
res.status(410).send('Link has expired');
return;
}
// Redirect to the destination
res.redirect(link.destination);
}); });
app.listen(3000, () => { console.log('Server listening on port 3000'); });
//GEERTOOOO
const clients = {};
const getUniqueID = () => { const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
return s4() + s4() + '-' + s4(); }
wsServer.on('request', (request) => { var userID = getUniqueID();
const connection = request.accept(null, request.origin); clients[userID] = connection;
connection.on('message', (message) => {
if (message.type === 'utf8') {
for(var key in clients) {
if (clients[key] !== clients[userID]) {
clients[key].sendUTF(message.utf8Data);
console.log(`Sent Message to: ${clients[key]}`);
}
}
}
}) })
Note: the express server is on port 3000 and the websocket server runs on port 8000.
I,ve tried just changing the port to same thing but i get an error when trying to use the websocket server for messages.
THE PURPOSE OF ALL THIS IS JUST TO MAKE THE WEBSOCKET CONNECTION AND EXPRESS CONNECCTION SECURE SO MY APPS (with letsencrypt ssl) can connect to the servers
It is not possible to create two separate server instances, both listening on the same port. But, specifically for a webSocket, you can share one server instance between Express and the webSocket server code. This is possible because a webSocket connection always starts with an http request (thus it can be listened for using your Express http server. And, because these http requests that initiate a webSocket all contain identifying headers they can be separated out from the regular http requests for Express by looking at the headers. The webSocket server code already knows how to do that for you.
To do that, first capture the Express server instance:
const server = app.listen(3000, () => { console.log('Server listening on port 3000'); });
Then, use that server instance when you create your webSocket server.
const wsServer = new webSocketServer({ httpServer: server });
Then, remove this code because you don't want to create yet another http server instance for the webSocket server:
const server = http.createServer();
server.listen(webSocketServerPort);
console.log('Listening on port 8000');

How to send websocket message to client from the serverside

Hello I have two backends laravel and Nodejs
and There will be one frontend.
So if the front end requests something on laravel and laravel requests to node and Node has to send a message to the client through WebSocket.
How to do It?
Index.js
const app = require('express')();
const http = require('http');
const WebSocket = require('ws')
//initialize a simple http server
const server = http.createServer(app);
app.get('/', function(req, res) {
res.sendFile(__dirname + '/index.html');
});
let sendNotification;
//initialize the WebSocket server instance
const wss = new WebSocket.Server({ server });
let socketapi = require('./socketapi')
socketapi.start(wss)
//start our server
server.listen(process.env.PORT || 5555, () => {
console.log(`Server started on port ${server.address().port} :)`);
});
socketapi.js
module.exports ={
start: (wss) => {
wss.on('connection', (ws) => {
console.log('connected!!!');
console.log(socketIds)
//connection is up, let's add a simple simple event
// triggerMessage('data');
ws.id=uuidv4()
ws.on('message', (message) => {
console.log('received: %s', message);
// ws.send(`Hello, you sent -> ${message}`);
});
}
}
}
Now I want to use this socket in the controller file and send it as I get a request.
When I export and import socket it logs as undefined.
I have stored and ws in array with particular Id and that Id was associated with data in DB so I can use that Id to get ws and send through that ws on function calling

How can a TCP/IP server write data on a HTTP server?

Here is my tcp/ip server that listens to IoT sensors sending it data:
const net = require('net')
const server = net.createServer(socket => {
socket.on('data', data => {
console.log(data.toString())
})
}).listen(1234)
I receive the data fine. A line of data received looks like 'DATA,zoneA,20'.
Now I want to write that stream of data to this http server:
const express = require('express')
const morgan = require('morgan')
const app = express()
const bodyParser = require('body-parser')
const {PORT} = 3000
app.use(morgan('tiny'))
app.use(express.json())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: true}))
const catchTCP = async dataLine => {
dataLine = 'DATA,zoneA,20'
try {
if (dataLine.indexOf("DATA") >= 0) {
var stream = dataLine.split(",")
var streamData = {
zone: stream[1],
battery: stream[2]
}
console.log(streamData)
}
} catch(err) {
console.log("error:", err)
}
}
catchTCP()
app.listen(PORT, () => console.log(`App listening at http://localhost:${PORT}`))
I would like to replace the 'dataLine' object by an entry point for this TCP/IP data. What would be the best way to forward TCP/IP data to the HTTP server's port?
Both servers run on the same network, so I've been told socket.io would not be the best fit.
Replace the catchTCP function on your HTTP server with a middleware route:
app.post("/receive", express.text(), function(req, res) {
var dataLine = req.body;
/* Your code from catchTCP */
res.end(); /* finish the HTTP response */
});
Then your TCP/IP server can send an HTTP request with the following code:
var s = net.createConnection(PORT, "localhost", function() {
s.end(`POST /receive HTTP/1.1
Content-Type: text/plain
Content-Length: ${data.toString().length}
${data}`);
});
This is the TCP/IP-level representation of the HTTP request. But I would not do it that way, I would rather use a proper HTTP client. Which is not a problem if your TCP/IP server is programmed in Node.js anyway:
const http = require("http");
http.request("http://localhost:${PORT}/receive", {
method: "POST",
headers: {"Content-Type": "text/plain"}
}).end(data.toString());

req.body is empty when sending data in server, in local works

I'm trying to send data to index.js, in localhost it's works correctly but when I deploy to my server it's not working. I see problem seems similar :
here. but it's not work for me
client.js :
sending data to index.js /subscribeA
await fetch("https://example.com:30000/subscribeA", {
method: "post",
body: JSON.stringify({ dataSubscribe: subscription, dataNotif: dataNotif}),
headers: {
"Content-Type": "application/json"
},
mode : 'no-cors'
});
console.log("Push Sent...");
}
then in index.js :
var express = require('express');
const port = process.env.PORT || 30000;
const app = express();
const http = require('http');
const https = require('https');
const fs = require('fs');
var server = http.createServer(app);
const privateKey = fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem', 'utf8');
const certificate = fs.readFileSync('/etc/letsencrypt/live/example.com/cert.pem', 'utf8');
const credintials = { key: privateKey, cert: certificate };
server = https.createServer(credintials, app);
server.listen(port, () => console.log(`Server started on port ${port}`));
const io = require("socket.io")(server, {
// allowEIO3 = true,
cors: {
credentials:true,
origin: '*',
methods: ["GET", "POST"]
}
})
const webpush = require("web-push");
const bodyParser = require("body-parser");
const path = require("path");
// Set static path
app.use(express.static(path.join(__dirname)));
app.use(bodyParser.json());
const publicVapidKey =
"BJthRQ5maDga7OSXsPC1ftGw-n16F7zQBEN7EUD6XxcfTTvrLGWSIG7y_JxiWtVlCFua0S8MTB5rPziBqNx1qIo";
const privateVapidKey = "3KsvdasAdSoCdsd0dIG_o9B0Ozvl1XDwI63JRKNIWBM";
webpush.setVapidDetails(
"mailto:test#test.com",
publicVapidKey,
privateVapidKey
);
// Subscribe Route
app.post("/subscribeA", (req, res) => {
console.log(req.body);
// Get pushSubscription object
// console.log(req.body.dataSubscribe);
const subscription = req.body.dataSubscribe;
const dataNotif = req.body.dataNotif;
if(dataNotif == null){
console.log('kosong rek');
}else{
console.log(dataNotif);
}
// Send 201 - resource created
res.status(201).json({});
// Create payload
const payload = JSON.stringify({ head: "yobro", content: "kontennnya bro"});
// Pass object into sendNotification
webpush
.sendNotification(subscription, payload)
.catch(err => console.error(err));
});
io.on('connection', function (socket) {
socket.on('notifikasi', function (data) {
io.sockets.emit('notifikasi', {
isi_notif: data.isi_notif});
});
});
Request Payload
{dataSubscribe: {,…}, dataNotif: {head: "#username", content: "yo"}}
dataNotif: {head: "#username", content: "yo"}
dataSubscribe: {,…}
So, it's sending data correctly, but in index.js when I use console.log(req.body) it return empty array {}.
Using no-cors in your fetch() means that the content-type can only be one of the following: "application/x-www-form-urlencoded", "multipart/form-data", or "text/plain" (some more explanation here). So, your "application/json" is not allowed and thus the server doesn't properly read/parse the body.
Only simple headers are allowed with no-cors which you can read about here.
You will have to stop using no-cors with your fetch to use JSON or change the content to use one of the allowed content-types.
FYI, in cases like this, it's generally helpful to log out the important expected aspects of the incoming request. In this case, you could log the content-type and see what you're getting. You could also look at the request as it's being sent from the browser in the network tab of the Chrome debugger. Either would probably show you something related to the content-type.

Forward request to ws client and wait for response Express

I'm trying to build an endpoint that will receive a request, emit the request data to a WebSocket client, wait for an event, then send back the response using express + socketio. This question is similar to it: Wait for socketio event inside express route
1) Receive request at http://localhost:3000/endpoint
2) Emit the event to web sockets as 'req'
3) Wait for 'res' event from ws
4) Send the received events details as the response of express.
Here is how I'm implemented:
server.js
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
var socket;
io.on('connection', function (s) {
socket = s;
});
http.listen(3000);
app.get('/endpoint', function (req, res) {
console.log('new request')
io.emit('req', { data: 'hello' });
socket.on('res', function (data) {
res.status(200).json(data);
});
});
index.html
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
socket.on('req', (data) => {
console.log(data)
socket.emit('res', data);
});
</script>
The script works fine for the first request on /endpoint. But if i hit the url again, it says
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
Please note that:
socket.on('res', function (data) {
res.status(200).json(data);
});
Is being called each time a socket is sending a response, thus showing the above error. You should unbind the listener inside the callback function.
Keep an array of express responses and set an id to each request. So it can be used later and delete if needed.
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var timeout = require('connect-timeout');
var uuid = require('uuidv4');
var _ = require('lodash');
app.use(timeout('10s'));
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
let responses = []
io.on('connection', (socket) => {
socket.on('res', (e) => {
var obj = _.find(responses, r => r.id === e.id);
obj.res.send(e)
_.remove(responses, r => r.id === e.id);
})
})
app.get('/endpoint', (req, res) => {
const id = uuid()
io.emit('req', { id, ip: req.ip, header: req.headers, method: req.method });
responses.push({ id, res })
});
http.listen(3000);
You're trying to do two different async tasks for the same data.
First, take your socket.on('res'...) out of the app.get().
Send back res.status(200) immediately with express to say you received the request and it is processing. Then send the socket message to the client using socket.io when it's complete. You'll want to save the connected users socket client ID and use io.to(socketId).emit(...data...) to do this
the other option is what I always do (assuming it's not a crazy large payload of data you're sending) Just use socket.io for the whole process.
client
function makeRequest () {
socket.on('data-complete--error', function ( error ) {
// ... message to user :(
// also remove these handlers when finished
socket.off('data-complete--error');
socket.off('data-complete--success');
});
socket.on('data-complete--success', function ( data ) {
// ... message to user :)
// ... handle data
// also remove these handlers when finished
socket.off('data-complete--error');
socket.off('data-complete--success');
});
socket.emit('request-data');
}
makeRequest();
server
move your stuff out and handle without using express at all

Resources