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
Related
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.
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);
});
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?
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);
}
});
})
I'm working on a chat application using angular 5 in the front end and nodeJS as back end and mongoDB to store the data. I integrated socket io to emit messages in real time
The following is the code which I'm using to develop the app. Now when a user fires a message in the text box I'm emitting the message only to the user to whom the message is sent. For example, if user1 is sending the message to user2, only user2 gets the socket event and not all the other connected users. So I found a way to emit to a particular user using the socket.id
I'm not using the same route to render all the users, I'm using different routes to different users, say "John" is the logged in user, the user can chat with other users in /message/ABC, /message/XYZ and so on.
CLIENT SIDE :
chat.component.ts
ngOnInit() {
this.loggedInUser = this.authService.getLoggedInUser();
this.route.params
.subscribe(params => {
this.username = params['username'];
this.receiveMessage();
});
}
sendMessage(message) {
const messageDetails = {
fromUsername : this.loggedInUser.username,
message : (message).trim(),
toUsername : this.username
};
this.socketService
.sendMessage(messageDetails);
}
receiveMessage() {
this.socketService
.receiveMessage()
.subscribe(
(message : Message) => {
console.log(message);
});
}
socket.service.client.ts
import * as io from 'socket.io-client';
private BASE_URL = AppConstants.ApiEndPoint.socketUrl;
private socket;
constructor() {
this.socket = io("http://localhost:3000/");
}
sendMessage(messageDetails) {
this.socket.emit('addMessage', messageDetails);
}
receiveMessage() {
return new Observable(observer => {
this.socket.on('addMessageResponse', function (message) {
observer.next(message);
});
return () => {
this.socket.disconnect();
};
});
}
SERVER SIDE :
server.js
I'm using express as middleware and passing the server instance to my socket in the server side
const http = require('http');
const app = express();
const server = http.createServer(app);
require("./server/services/socket.service.server")(server);
server.listen(port);
Socket.service.server.js
On socket event addMessage , I'm adding the message to my db and then rendering the socketId of the toUser from my user model and therefore emitting only to the intended user.
module.exports = function (server) {
var socketIo = require('socket.io');
var userModel = require("../models/user/user.model.server");
var messageModel = require("../models/message/message.model.server");
const io = socketIo(server);
io.on('connection', function(socket) {
socket.on('addMessage', function (messageDetails) {
messageModel
.createMessage(messageDetails)
.then(function (message) {
userModel
.findUserByUsername(message.toUsername)
.then(function (user) {
var socketId = user.socketId;
io.to(socketId).emit('addMessageResponse', message);
});
});
})
});
};
Now the message is emitted to the client side to the particular user. This works fine but I'm getting the message the number of times the user is connected to the client.
For example, I logged in with a user PO I'm on route /chat/ok where ok is a connected user, ok sends a message to po. PO receives the message in the console as below
screenshot 1
Now I'm navigating to chat with another user and I'm on route /chat/kk where kk is another connected user.
Now if kk sends a message to po, I'm receiving the message twice. As you can see below, I navigated to /chat/kk , I'm receiving the message twice. And similarly if I navigate to another user, I'm getting the message thrice and so on. Socket io emits a message to the user the number of times the user is getting connected to the socket.
Screenshot 2
I just want the normal workflow for the chat application and not emitting the message as many times the user is connected to the client. Is there a workaround ? I guess I'm making the mistake in the client side callback when I receive the response or I'm connecting multiple times for the same client. Can someone please help me out.
Thanks !
When you describe it as a global variable, it is incorrect at socket.io
Reason of your error is the code
constructor() {
this.socket = io("http://localhost:3000/");
}
You remove and define inside of receiveMessage function.It must work correctly.