Encoding of socket data not working with Node - javascript

// server.ts
import { Server } from 'net';
const port = 8081;
const hostname = 'localhost';
const server = new Server();
server.on('connection', (socket) => {
socket.setEncoding('utf8');
socket.on('data', (data) => {
console.log(data.toString());
});
});
server.listen({ port, host: hostname }, () => {
console.log('Server running at port', port);
});
When I try to console log the data of the buffer I am getting weird chars. I tried to assign it to a variable and split the data of the first request, but the same is happening. I tried to set 'utf-8' as well and all the other types and no one is giving me a the request properly.
This is the string I am getting when decoded to 'utf8':
��Lϻط�4�)]�3O�X\��M�ʏ����S�mN 9BRg����Vl�^A:�ʃ�"I���r�*��p� ���+�/�,�0̨̩����/5����
**
#
3+)** http/1.1
���]xO����
�[��ˈ�
I�B,}�J��
-+
ih2zz�
My purpose is to get the websocket key of the request. Then I will respond with the hash in base 64 to establish the connection, but that's another thing, only to put in context the situation.
PD: I am using Node 18.7.0, but I tried with 16 and the same happens.
PD2: Okay, I realized the problem. At the client I was setting a wss://localhost:8081. Switching this to ws://localhost:8081 solved the problem, however I am still interested in the solution for a secure connection.
Thank you.

Related

Get 404 error When trying to fetch data from Node.js in React.js While server is running

I am not familiar with back-end and it is the first time to try it out.
Techset is React.js, Node.js, express.js, and mysql.
I want to request result of a query but 404 error occurs.
This is the code.
In React.js
const resident_name = 'sss';
let rates = [];
async function getData(){
const res = await fetch('http://localhost:3001', {
method: 'POST',
mode: 'cors',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({name: {resident_name}}),
});
rates = await res.result;
};
getData();
I want to execute the function as soon as the page that has it shows up.
In Node.js
app.post("/", (req,res)=>{
res.set('Content-Type', 'application/json');
console.log("i got "+req.body.name);
db.query(`SELECT r.*
FROM rate r INNER JOIN resident rs on r.resident_id = rs.resident_id
WHERE rs.name = ${req.body.name}`, (err,result)=>{
// result is an array that contains dictionaries.
if(err) throw err;
result.forEach(r => {
console.log(r)
});
res.send(result)
});
});
I am not sure what causes the error. Can I plz get solution if you guys know some info about it?
Thank you!
Edited: I am running both React.js on port 3000 and Node.js on port 3001. Once I type node index.js, I can see Server is listening to 3001 message on server-side console with this code below.
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
Result on the console:
Server listening on 3001
I'm working on making sure the server is running in the way the first answer suggested.
first, make sure your server's API endpoint is correct and running.
For that, you can use Vs code extension like Thunder client by
passing your port.
And check for any network issue using ping command or firewall
block.
Also, check your header request or use tool like curl. In
react.js while request from server, CORS also block to make request
on the browser (as i have face same issue on past). So, make sure you
check the server logs.
404 error occurs only if there is nothing is listening in the mentioned address:port.
Make sure you are requesting to the correct url with correct port number in which your express app is running.
Express app defaults to port 3000

Why is a connection not created for this simple websocket snippet?

I have a simple snippet on the front end as follows which I can verify is working. I can do this by changing the port to something other than 3000 and it will error.
It is definitely finding the server at that port:
// Create WebSocket connection .. will error if I change the port
const socket = new WebSocket('ws://localhost:3000');
console.log('DEBUG: Web socket is up: ');
// Connection opened
socket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
I am using ws-express on the server side as follows. This was the minimal example given in the NPM docs:
const expressWs = require('express-ws')(app);
app.ws('/echo', (ws, req) => {
ws.on('message', (msg) => {
ws.send(msg);
});
});
However, the open event on the client never fires. I would like to send messages from the client to the server, but I assume, that I need an open event to fire first.

Use express as proxy for websocket

I have a data provider which gives me stock prices via TCP connection. The data provider only allows a static IP to connect to their service.
But since I need to format the data before sending it to my front-end I want to use my express back-end as a proxy.
What that means is:
I need to connect my back-end to my data provider via websocket(socket.io) in order to get the data (back-end acts as client)
I need my back-end to broadcast this received data to my front-end(back-end acts as server)
My question is: Is that possible at all? Is there an easier way to achieve this? Is there a documentation on how to use an express app as websocket server and client at once?
EDIT:
I got this working now. But my current solution kills my AWS EC2 instance because of huge CPU usage. This is how I've implemented it:
const net = require('net');
const app = require('express')();
const httpServer = require('http').createServer(app);
const client = new net.Socket();
const options = {
cors: {
origin: 'http://someorigin.org',
},
};
const io = require('socket.io')(httpServer, options);
client.connect(1337, 'some.ip', () => {
console.info('Connected to some.ip');
});
client.on('data', async (data) => {
// parse data
const parsedData = {
identifier: data.identifier,
someData: data.someData,
};
// broadcast data
io.emit('randomEmitString', parsedData);
});
client.on('close', () => {
console.info('Connection closed');
});
httpServer.listen(8081);
Does anyone have an idea why this causes a huge CPU load? I've tried to profile my code with clinicjs but I couldn't find a apparent problem.
EDIT2: To be more specific: My data provider provides my with stock quotes. So every time a quote changes, I get new data. I then parse this data and emit it via io.emit. Could this be some kind of bottleneck?
This is the profile I get after I run clinicjs:
I don't know how many resources you have on your AWS, but 1,000 clients shouldn't be a problem.
I have personally encountered 2 bottlenecks:
Clients connected with Ajax, not WS (It used to be a common problem with old socket.io)
The socket.io libraries were served by Node, not Nginx / Apache. Node is poor at keeping-alive management.
Check also:
How often do you get data from some.ip? Good idea is aggregate and filter it.
Do you need to notify all customers of everything? Is it enough just to inform interested? (Live zone)
Maybe it is worth moving the serving to serviceWorker.js or Push Events?
As part of the experiment, log yourself events. Receiving data, connecting and disconnecting the client. Observe the server logs.
As part of the debugging process, log events. Receiving data, connecting and disconnecting the client. Observe the server logs.
Or maybe this code is not responsible for the problems, but the data download for the first view. Do you have data in the buffer, or do you read for GET index.html?
To understand what was going on with your situation, I created an elementary TCP server that published JSON messages every 1ms to each client that connects to it. Here is the code for the server:
var net = require('net');
var server = net.createServer(function(socket) {
socket.pipe(socket);
});
server.maxConnections = 10
server.on('close', () => console.log('server closed'))
server.on('error', (err) => console.error(err))
server.on('listening', () => console.log('server is listening'))
server.on('connection', (socket) => {
console.log('- client connected')
socket.setEncoding('utf8')
var intervalId = setInterval(() => socket.readyState === "open" &&
socket.write(JSON.stringify({
id: intervalId,
timestamp: Date.now(),
}) + '\n'), 1)
socket.on('error' , (err) => console.error(err))
socket.on('close' , () => {
clearInterval(intervalId)
console.log('- client closed the connection')
})
})
server.listen(1337, '0.0.0.0');
As you see, we set up a setInterval function that will emit a simple JSON message to each connected client every 1 ms.
For the client, I used something very similar to what you have. At first, I tried pushing every message received by the server to the browser to the WebSocket connection. In my case, it also pushed the CPU to 100%. I don't know exactly why.
Nonetheless, even though your data is being updated every 1 ms, it is doubtful that you need to refresh your webpage at that rate. Most websites work at 60 fps. That would mean updating the data every 16ms. So, a straightforward solution would be to batch the data and send it to the browser every 16 ms. Just this modification greatly increases performance. You can go even further by extending the batch time or filtering some of the sent data.
Here is the code for the client, taking advantage of batch messages. Bear in mind that this is a very naive implementation made to show the idea. A better adjustment would be to work the streams with libraries like RxJS.
// tcp-client.js
const express = require('express');
const http = require('http');
const { Server } = require("socket.io");
const net = require('net')
const app = express();
const server = http.createServer(app);
const io = new Server(server);
const client = new net.Socket()
app.get('/', (req, res) => {
res.setHeader('content-type', 'text/html')
res.send(`
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>TCP - Client</title>
</head>
<body>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
socket.on('msg', (msg) => document.body.textContent = msg);
</script>
</body>
</html>
`);
});
io.on('connection', (socket) => {
console.log('- user connected');
socket.on('disconnect', () => {
console.log('- user disconnected');
});
});
var buffer = []
setInterval(() => {
io.emit("msg", JSON.stringify(buffer))
buffer = []
}, 16)
client.connect(1337, '127.0.0.1', function() {
console.log('- connected to server');
});
client.on('data', function(data) {
buffer.push(data.toString("utf8"))
});
client.on('close', function() {
console.log('- connection to server closed');
});
server.listen(3000, () => {
console.log('listening on 0.0.0.0:3000');
});

How to use socket.io to display realtime data in the client (web browser) from the node.js server?

I want to display the real-time speech to text data in the browser. By real-time what I mean is, "while I am speaking I am getting the text output simultaneously". I have implemented the speech-to-text part in Python using the Google cloud service API. Then I used "child process" to run my python program in the node.js environment. Till now everything is fine. Next, I want to display the real-time text in the browser. In another word, I want to send the real-time text output from the python (which is now running in node.js using the child process) to the web browser. I was trying to do that with socket.io. Here is my server side (node.js) code where socket.io is also applied:
const express = require('express');
//const router = express.Router();
const {spawn} = require('child_process');
const path = require('path');
const app = express();
const http = require('http');
const server = http.createServer(app);
//const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
function runScript(){
return spawn('python3', [
"-u",
path.join(__dirname, 'script.py')
]);
}
const subprocess = runScript()
// print output of the script
app.get('/', (req,res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
subprocess.stdout.on('data', (data) => {
//console.log(`data:${data}`);
socket.on('message', (data) => {
socket.broadcast.emit('message', data);
});
});
});
server.listen(3000, () => {
console.log('listening on *:3000');
});
Above, I am first using the child process to call the python program in node.js and then I am using socket.broadcast.emit to send the text output of the python program to my client side. The client-side code looks like this:
<!DOCTYPE html>
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
var messages = document.getElementById('messages');
//const EventEmitter = require('events');
//const emitter = new EventEmitter()
//emitter.setMaxListeners(50)
socket.on('messages', function(data) {
document.querySelector("#style1".innerHTML = `<p>${data1}</p>`
});
</script>
</head>
<body id="messages">
<h1> This is crazy </h1>
<div id="style1">
</div>
</body>
</html>
Above, I want to display the real-time text output from the python program inside the <p> tag.
The problem is, I am not able to get anything in the web browser.
My objective is, I want to display whatever I am speaking as text in the web browser in real-time.
I don't know much about socket.io. In fact, this is the first time I am using this technology.
Your Node.js server will act as the socket server. As your code shows, it listens on a port for a socket connection, and on connection, creates a socket, which you then send messages too. From a simple cursory review, the server code looks okay.
On your webpage, you are creating the socket, and listening for messages.
However the socket running on the webpage hasn't yet connected to the server, which is why nothing is working yet.
Assuming you're doing this on localhost, just add the socket server address to it's constructor, and then listen for connect.
const socket = io('ws://localhost:3000');
socket.on('connect', () => {
// do any authentication or handshaking here.
console.log('socket connected');
});
More advanced implementations should gracefully handle closing sockets.
Per the following comment:
I added the lines you suggested above. Even now nothing is visible on the webpage but I am getting this warning: (node:14016) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 message listeners added. Use emitter.setMaxListeners() to increase limit
Looking more closely at your server code, I believe this is the root issue
subprocess.stdout.on('data', (data) => {
//console.log(`data:${data}`);
socket.on('message', (data) => {
socket.broadcast.emit('message', data);
});
});
});
Each time you receive data from subprocess.stdout, you are adding a new onmessage event handler to your socket, so after a period of time, you have added too many event handlers.
Re-write your logic so that you only add socket.on('message') once (usually after your create the socket).
It is also worth noting that in the above code, data from stdout is not being used, as that data variable is being redefined in a lower scope by your onmessage function. Since data is being redefined, the output of your Python program is being ignored.
I think this is what you want:
//echo any message you receive from the socket back to the socket
socket.on('message', (data) => {
socket.broadcast.emit('message', data);
});
//send data from std out to the socket.
subprocess.stdout.on('data', (data) => {
//console.log(`data:${data}`);
socket.broadcast.emit('message', data);
});

NodeJS reverse SSH tunnel: unable to bind to serveo.net:80

Context
Not long ago, I discovered a great service called Serveo. It allows me to expose my local apps to the Internet using reverse SSH tunneling.
e.g. Connections to https://abc.serveo.net get forwarded to http://localhost:3000 on my machine.
To do this, they require no client installation, and I can just type this in the command line:
ssh -R 80:localhost:3000 serveo.net
where 80 is the remote port on serveo.net to which I want to bind, and localhost:3000 is the local address for my app.
If I just type 80 on the left-hand side, Serveo will answer Forwarding HTTP traffic from https://xxxx.serveo.net where xxxx is an available subdomain, with https support.
However, if I type another port, like 59000, the app will be available through serveo.net:59000, but without SSL.
Problem
Now, I would like to do this with NodeJS, to automate things in a tool I'm building for my coworkers and my company's partners, so that they don't need to worry about it, nor to have an SSH client on their machine. I'm using the SSH2 Node module.
Here is an example of working code, using the custom port configuration (here, 59000), with an app listening on http://localhost:3000:
/**
* Want to try it out?
* Go to https://github.com/blex41/demo-ssh2-tunnel
*/
const Client = require("ssh2").Client; // To communicate with Serveo
const Socket = require("net").Socket; // To accept forwarded connections (native module)
// Create an SSH client
const conn = new Client();
// Config, just like the second example in my question
const config = {
remoteHost: "",
remotePort: 59000,
localHost: "localhost",
localPort: 3000
};
conn
.on("ready", () => {
// When the connection is ready
console.log("Connection ready");
// Start an interactive shell session
conn.shell((err, stream) => {
if (err) throw err;
// And display the shell output (so I can see how Serveo responds)
stream.on("data", data => {
console.log("SHELL OUTPUT: " + data);
});
});
// Request port forwarding from the remote server
conn.forwardIn(config.remoteHost, config.remotePort, (err, port) => {
if (err) throw err;
conn.emit("forward-in", port);
});
})
// ===== Note: this part is irrelevant to my problem, but here for the demo to work
.on("tcp connection", (info, accept, reject) => {
console.log("Incoming TCP connection", JSON.stringify(info));
let remote;
const srcSocket = new Socket();
srcSocket
.on("error", err => {
if (remote === undefined) reject();
else remote.end();
})
.connect(config.localPort, config.localPort, () => {
remote = accept()
.on("close", () => {
console.log("TCP :: CLOSED");
})
.on("data", data => {
console.log(
"TCP :: DATA: " +
data
.toString()
.split(/\n/g)
.slice(0, 2)
.join("\n")
);
});
console.log("Accept remote connection");
srcSocket.pipe(remote).pipe(srcSocket);
});
})
// ===== End Note
// Connect to Serveo
.connect({
host: "serveo.net",
username: "johndoe",
tryKeyboard: true
});
// Just for the demo, create a server listening on port 3000
// Accessible both on:
// http://localhost:3000
// https://serveo.net:59000
const http = require("http"); // native module
http
.createServer((req, res) => {
res.writeHead(200, {
"Content-Type": "text/plain"
});
res.write("Hello world!");
res.end();
})
.listen(config.localPort);
This works fine, I can access my app on http://serveo.net:59000. But it does not support HTTPS, which is one of my requirements. If I want HTTPS, I need to set the port to 80, and leave the remote host blank just like the plain SSH command given above, so that Servo assigns me an available subdomain:
// equivalent to `ssh -R 80:localhost:3000 serveo.net`
const config = {
remoteHost: "",
remotePort: 80,
localHost: "localhost",
localPort: 3000
};
However, this is throwing an error:
Error: Unable to bind to :80
at C:\workspace\demo-ssh2-tunnel\node_modules\ssh2\lib\client.js:939:21
at SSH2Stream.<anonymous> (C:\workspace\demo-ssh2-tunnel\node_modules\ssh2\lib\client.js:628:24)
at SSH2Stream.emit (events.js:182:13)
at parsePacket (C:\workspace\demo-ssh2-tunnel\node_modules\ssh2-streams\lib\ssh.js:3851:10)
at SSH2Stream._transform (C:\workspace\demo-ssh2-tunnel\node_modules\ssh2-streams\lib\ssh.js:693:13)
at SSH2Stream.Transform._read (_stream_transform.js:190:10)
at SSH2Stream._read (C:\workspace\demo-ssh2-tunnel\node_modules\ssh2-streams\lib\ssh.js:252:15)
at SSH2Stream.Transform._write (_stream_transform.js:178:12)
at doWrite (_stream_writable.js:410:12)
at writeOrBuffer (_stream_writable.js:394:5)
I've tried many things without any success. If anyone has an idea about what might be wrong in my example, I'll be really grateful. Thanks!
OpenSSH defaults to "localhost" for the remote host when it's not specified. You can also verify this by checking the debug output from the OpenSSH client by adding -vvv to the command line. You should see a line like:
debug1: Remote connections from LOCALHOST:80 forwarded to local address localhost:3000
If you mimic this by setting config.remoteHost = 'localhost' in your JS code you should get the same result as the OpenSSH client.

Categories