Binding socket before send with dgram in client - javascript

I am developing a node.js program that connects over UDP with another program running on the same machine. Currently I am sending data like this:
import dgram = require("dgram");
const client = dgram.createSocket("udp4");
//Some code to structure the message how the server wants it
const message = Buffer.alloc(413);
message.write("TEST\0");
client.send(message, 0, message.length, 49000, '127.0.0.1', (err) => {
if (err) {
this.client.close();
console.error(err);
}
});
This works fine, however, I want to do the code in two steps. First open the connection, and then send the message. This is the code that I wrote (skipping some repeted things):
//const message same as above
this.client.bind(49000, '127.0.0.1', (err) => {
if (err) {
this.client.close();
console.error(err);
} else {
this.client.send(message, 0, message.length, (err) => {
if (err) {
this.client.close();
console.error(err);
}
});
}
});
This throws the error: throw new ERR_SOCKET_BAD_PORT(name, port, allowZero); RangeError [ERR_SOCKET_BAD_PORT]: Port should be > 0 and < 65536. Received 0.
Edit:
Thanks leitning! .connect() is exactly what I needed.
I now have another issue. When I do .send() directly without calling .connect() before I can receive incoming datagrams by creating a .on('message') listener on the client. But when I connect and then send, the listener doesn't receive any incoming messages. I tested this with Wireshark and there are incoming messages.
The code is:
import dgram = require("dgram");
const client = dgram.createSocket("udp4");
const message = Buffer.alloc(413);
message.write("TEST\0");
client.connect(49000,'127.0.0.1',err => {
if (err) console.error(err);
client.send(message, 0, message.length, 49000, '127.0.0.1', (err) => {
if (err) {
this.client.close();
console.error(err);
}
});
});
client.on('message',(data) => {
console.log(data);
});
Is there something I missed from the docs as to how to receive messages after doing .connect() ?

When you bind a port you are claiming that port from the operating system for the purposes of sending from and receiving to. It does not have anything to do with where your messages will be sent to.
By binding to localhost:49000, packets that you send will declare that they from localhost port 49000. You can also then listen to the bound socket for incoming messages to localhost port 49000.
You still need to declare a recipient for messages that you are trying to send. You get an invalid port error because the function is interpreting your 0 argument as the destination port. dgram.send docs
It looks like the functionality you are trying to use is covered in the dgram.connect method.

Related

Socket.io emit an event with data will disconnect the client with "parse error"

I built a Socket.io server which handle connection and event :
import { createServer } from "http";
import { Server } from "socket.io";
const httpServer = createServer();
const io = new Server(httpServer);
io.on("connection", (socket) => {
console.log('New connection');
socket.on('join', data => {
console.log(data);
});
...
});
I'm trying to built a Socker.io client using npm module socket.io-client
import { io } from 'socket.io-client';
const socket = io(servHost);
socket.on('connect', () => {
console.log(`Connected : ${socket.id}`);
socket.emit('join', "Some data");
socket.emit('otherEvent');
});
socket.on('disconnect', (err) => {
console.log(err);
console.log("Disconnected");
});
Server log :
New connection
New connection
...
Client log :
Connected : IyR4Gr0Gvao16UD6AAAD
parse error
Disconnected
Connected : m8aSe6HI67i6VIBNAAAF
parse error
Disconnected
...
Note : If I comment socket.emit('join', "Some data"); everything works fine, even the otherEvent is emited and client can handle the expected event in return (not shown on the code). So I guess the error comes from the data inside join event.
some data can be string / array or object (I tried everything) I have the same error.
I'm using :
"socket.io": "^4.5.2",
"socket.io-client": "^4.5.2"
Looks like .emit() expects object as 2nd argument.
I had exactly the same issue, fixed by passing an object:
socket.emit('join', {text: "Some data"});

Adding information about the client on the initial Websocket handshake

I'm currently creating a distributed chat application. Everything works fine, meaning it's possible to send messages between the clients and the server and have it broadcasted appropiately.
However, at the moment only the actual message is sent to the server. I would like to add information about the user sending the message aswell.
I could add this information whenever I send a new message, but I would prefer if I could add this information during the initial handshake and then save this information on the backend.
I've thought about sending some information in the URL, but as I only instantiate the websocket once, this does not seem like the way to go. Similarly, I thought about adding the information as the body of the request, but I read that having a body on a GET request is usually not recommended.
So my question is, am I trying to do something that I should not be going for? Should I just send information about the client on each new message that is sent to the server?
Currently, my client looks like this:
const socket = new WebSocket("ws://localhost:8080/ws");
const connect = (cb) => {
console.log("Attempting Connection...")
socket.onopen = () => {
console.log("Successfully Connected");
}
socket.onmessage = (msg) => {
console.log(msg)
cb(msg);
}
socket.onclose = (event) => {
console.log("Socket Closed Connection: ", event);
}
socket.onerror = (error) => {
console.log("Socket Error: ", error);
}
};
const sendMsg = (msg) => {
console.log("Sending msg: ", msg);
socket.send(msg);
}
And the initial connection on the backend is handled by the following:
func serveWs(pool *websocket.Pool, w http.ResponseWriter, r *http.Request) {
fmt.Println("WebSocket Endpoint Hit")
conn, err := websocket.Upgrade(w, r)
if err != nil {
fmt.Fprintf(w, "%+V\n", err)
}
client := &websocket.Client{
Name: "?????", // Obviously, I would like the actual name to be here.
Conn: conn,
Pool: pool,
}
pool.Register <- client
client.Read()
}
The normal thing to do here as selbie pointed out is to expect from the client a special first message through WebSocket. If that message is not recived or does not meet the requirements the server ends the WebSocket conn.
Using socket.onopen is very common for this task.
More on why you cant put headers:
HTTP headers in Websockets client API

Nodejs proxy server Cannot set headers after they are sent to the client

I am trying to build a Nodejs server that takes data from another server that contain the data then send it to the client, I am using a proxy structure to handle multiple types of connection.
I am using an HTTP Express server to handle HTTP request and it works fine for the first request after the first request I have an Express error Cannot set headers after they are sent to the
client
_http_outgoing.js:526
throw new ERR_HTTP_HEADERS_SENT('set');
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the
client
at ServerResponse.setHeader (_http_outgoing.js:526:11)
at ServerResponse.header (E:\Web\Projects\Color\server\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (E:\Web\Projects\Color\server\node_modules\express\lib\response.js:170:12)
at EventEmitter.<anonymous> (E:\Web\Projects\Color\server\server.js:35:13)
at EventEmitter.emit (events.js:323:22)
at Socket.<anonymous> (E:\Web\Projects\Color\server\server.js:30:22)
at Socket.emit (events.js:323:22)
at addChunk (_stream_readable.js:294:12)
at readableAddChunk (_stream_readable.js:275:11)
at Socket.Readable.push (_stream_readable.js:209:10) {
code: 'ERR_HTTP_HEADERS_SENT'
}
My Express server code:
const express = require("express");
const net = require("net");
const http = require("http");
const login = require("./routes/auth");
const auth = require("./middlewares/verfication");
const info = require("./routes/info");
const events = require("events");
const eventEminter = new events.EventEmitter();
const app = express();
let clientSocket;
app.setClientSocket = (socket) => {
clientSocket = socket;
return true;
};
app.use(express.json());
app.use("/login", login);
app.use("/info", auth, info);
app.get("/", (req, res) => {
let clientData;
console.log("request has been made");
clientSocket.write("GET:/");
clientSocket.on("data", (data) => {
clientData = data.toString();
eventEminter.emit("ed");
console.log(clientData);
});
eventEminter.on("ed", () => {
res.send(clientData);
});
});
module.exports = app;
The clientSocket variable represents the connection with the data server .
Finally here is my server code:
const net = require("net");
const httpServer = require("./server");
//const clientServer = require("./client");
const dotenv = require("dotenv");
dotenv.config();
let clientSocket;
let registeredClient = false;
const proxyServer = net.createServer((socket) => {
socket.on("data", (data) => {
if (!data) {
socket.write("Error in request");
throw new Error("Request message is empty");
}
let request;
try {
request = data.toString();
} catch (error) {
console.log(
new Error("Request message can not be conveted to String")
);
throw error;
}
if (request.includes("HTTP")) {
const httpSocket = new net.Socket();
if (!registeredClient) {
registeredClient = httpServer.setClientSocket(clientSocket);
console.log("Client registered");
}
httpSocket.connect(4444, () => {
console.log("Proxy Connected to http server");
});
httpSocket.on("error", (err) => {
console.error("Proxy error: Could not connect to http server");
throw err;
});
const flushed = httpSocket.write(data, (err) => {
if (err) {
console.error(
"Proxy error :Could not send data to http server"
);
throw err;
}
});
// if (flushed) httpSocket.end();
let response;
httpSocket.on("data", (httpData) => {
if (!httpData) {
console.error(
"Proxy error: unable to retrive data from http server"
);
return;
}
socket.write(httpData.toString());
});
// httpSocket.on("end", () => {
// if (!response) {
// console.error(
// "Proxy error: unable to send response or empty response message"
// );
// return;
// }
// socket.write(response);
// });
} else {
if (!clientSocket) clientSocket = socket;
}
});
socket.on("error", (err) => {
console.error("Proxy error: could not connect with client");
});
});
const port = process.env.PORT || 4000;
proxyServer.listen(port, () => {
console.log(`Proxy Server is running on port ${port}`);
});
httpServer.listen(4444, () => {
console.log("Http server is running on port 4444");
});
thank you for helping.
You are calling res.send() on every "ed" event and you're emitting an "ed" event on every clientSocket.on('data', ...) event. So, as soon as you get a second data event you'll be trying to call res.send() for the second time on the same response. You only get one response per http request. You can't call res.send() more than once for a given http response.
It's unclear how this code is supposed to work since I don't know what you're really trying accomplish here. Perhaps you need to accumulate all the data from the data events and then send one response and then unhook all the listeners do you don't get any more data events for this request. Or, if you want to send the response on the first data event, then just unhook the data and ed listeners after you send the response.
Keep in mind that on a regular socket, you have no control over what data comes in a data event. TCP is a stream protocol and data can come in any size chunks and the chunks it arrives in may not be the exact same as the chunks it was sent in. You would typically have to be looking for some sort of complete packet yourself and be able to assemble or split data events into full packets you can do something with.
Generally this error "Cannot set headers after they are sent to the client" occurs when you are sending multiple response to the server. In your case,
eventEminter.on("ed", () => {
res.send(clientData);
});
As you are using this, you are sending multiple response to server. You should send the response only once. You can use this code instead
eventEminter.once('ed', () => {
res.send(clientData);
});

How can I work around the promises that serialport nodejs package works with

I've started a project that requires communication between an arduino and a local nodejs server (unrelated the data will be sent via an http request or a socket to the actual remote server later on). I'm using the node package serialport. At the beginning of the serial communication, the server needs to "find" the arduino. I've decided on the following negotiation codex:
1) the server sends a "c" character (as in connect) which the arduino is listening for
2) the arduino replies to all "c"s with another "c" which the server will be listening for
in other words when both sides receive a "c" that means the serial connection works
However, due to serialport using promises I can't go through all available ports and check if there's an arduino (which replies with "c") there.
Here's what I've come up with so far:
var SerialPort = require('serialport');
var Readline = require('#serialport/parser-readline');
async function tryPort(path) {
var port = new SerialPort(path, {
baudRate: 9600
});
port.on('error', function (err) {
console.log(err);
});
port.pipe(new Readline({ delimiter: '\n' })).on('data', (data)=>{
console.log(port);
console.log(data);
if (data == 'c') {
return port;
}
port.close();
});
port.write("c", function (err) {
if (err) console.log(err);
});
}
async function connect() {
var connection, ports = await SerialPort.list();
for(i=0;i<ports.length;i++){
connection = await tryPort(ports[i].path);
}
setTimeout(() => {
if (!connection) {
console.log("no port/response found");
}else{
console.log(connection);
}
}, 3000);
}
connect();
I went with the assumption the variable 'connection' will be assigned the value of the port that responded correctly last because that port will take the longest to finish. Unfortunately, it seems this won't work with promises... So I'm wondering if there's any other way to accomplish it?

WebSocket stops working in Vue/Node application

I have a Node/Vue application. I am consuming a WebSocket from Binance, a crypto exchange. I can see the quotes on the server console as I log them, I can send them to the browser for a short period of time before the client stops logging them.
Browser just using WebSocket API
Node using ws library
Node code, this I am running as it's own service as its just this.
'use strict';
const WebSocket = require('ws');
const binanceWS = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt#trade')
const server = new WebSocket.Server({ port: 5002 });
//websocket connection event will return a socket you can later use
binanceWS.on("open", function() {
console.log("connected to Binance");
});
binanceWS.on('message', function(data){
console.log(data);
server.on('connection', function connection(ws){
console.log("Connected a new client");
ws.send(data);
});
server.on('closed', function (id){
console.log("connection closed");
console.log(id);
});
server.on('error', function (err){
console.log(err)
})
})
On the Client side I am using Vue and in the app.js file I have this on the created hook.
let socket = new WebSocket("ws://localhost:5002")
socket.addEventListener('message', function(event){
let quotes = JSON.parse(event.data);
console.log(quotes.p)
});
socket.addEventListener('error', function(event){
console.log("closing because " + event);
})
Right now I am only listening to the consoles in the above app.vue file.
What I see in the browser console is a lot of quotes, then they stop after a second or 2. There can be over a thousand quotes in some times. Then on occasion I see a console.log('created') that I have in a child component of app.vue. In many cases this is the last thing in the console after hundreds of quotes.
In the console.log for the server I see a lot of sessions being created with one page refresh. So much that it fills my console.
So I'm not sure I am creating the connections correcly, I am not sure if Vue is somehow stopping the console.log's?
I don't see any errors anywhere and the entire time in my server console the Binance API continues streaming.
you have to write server event listener outside binance on message handler;
then you can pass messages from binance to the server by emitting new event to the server
on receiving message from binance you can send data to all connection on the server
Or Try this code I think it will work :
'use strict';
const WebSocket = require('ws');
const binanceWS = new WebSocket('wss://stream.binance.com:9443/ws/btcusdt#trade')
const server = new WebSocket.Server({ port: 5002 });
server.on('connection', function connection(ws){
console.log("Connected a new client");
});
server.on('closed', function (id){
console.log("connection closed");
console.log(id);
});
server.on('error', function (err){
console.log(err)
})
//websocket connection event will return a socket you can later use
binanceWS.on("open", function() {
console.log("connected to Binance");
});
binanceWS.on('message', function(data){
console.log(data);
server.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
})

Categories

Resources