I'm trying to create a websocket server that receives a message and then closes. From there, the script should behave as if the websocket server never existed...
I can't seem to figure why the websocket server keeps hanging.
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8080 })
wss.on('connection', ws => {
ws.on('message', message => {
console.log(`Received message => ${message}`);
ws.send('Hello! Message From Server!!');
});
ws.on("close", ws => {
console.log("closed!");
ws.close();
//ws.terminate();
});
});
Both ws.close and ws.terminate leave the script hanging after the message is received. How do I truly close the server and let the script act is if the websocket server never existed? I want to process the information I received from the client.
This answer (https://stackoverflow.com/a/49791634/5527786) suggests how to close the server but it doesn't work. Not even process.exit stops it!
Edit: It looks like I was supposed to do wss.close(). How can I now process the message that I received from the websocket server? I'm assuming I need to return a promise that I resolve somewhere?
Related
I'm definitely a newbie with JS and node. I have telescope management software called SkyX Pro, and it has the ability to run a TCP Server on port 3040. I can connect to it using Netcat and hand it a Javascript starting with //* Javascript *// this works and allows me to startup cameras and other equipment and send commands for taking pictures etc. The issue is it needs to be run from a batch file which makes getting any information back to an HTML page tough (Like Camera, focuser and filter wheel status and temperatures).
The NC call looks like "NC localhost 3040 < Javascript-file.js
To get around the browser to local machine security issues I want to run this from node.js with maybe socket.io-client if possible, but I don't know the proper syntax for it.
I have seen plenty of client syntax sending hello's etc. but nothing send javascript and allowing for two-way connectivity that I can understand.
I have tried using:
var socket = io.connect('http://localhost');`enter code here`
socket.on('httpServer', function (data) {
console.log(data);
document.write(data + "\r\n");
socket.emit('tcp', "For TCP");
});
const net = require('net');
const client = new net.Socket();
client.connect({ port: 3040, host: process.argv[2] });
client.on('data', (data) => {
console.log(data.toString('utf-8'));
But I do not understand it well enough to troubleshoot why it's not working.
Any help would be wonderful, and please treat me like a baby that needs its step by step.
Cheer
Peter
Reading [1], We can assume socket-io isn't the perfect fit for you, because that Server you have sound like a typical tcp-socket server, not a socket.io server ( which requires special headers ) or a web-socket server.
So you only needs "net" library to do the job.
const net = require('net');
// module to send a message to TCP-socket server and wait for the response from socket-server
const sendAndReceive = async (client, message) => {
client.write(message);
let response = null
await ( new Promise( (resolve, reject) => {
client.on('data', function(data) {
response = data;
resolve()
});
}))
return response;
}
// send a single message to the socket-server and print the response
const sendJSCode = (message) => {
// create socket-client
const client = new net.Socket();
client.connect(3040, 'localhost', async function() {
console.log('Connected');
// send message and receive response
const response = await sendAndReceive(client, message)
// parse and print repsonse string
const stringifiedResponse = Buffer.from(response).toString()
console.log('from server: ', stringifiedResponse)
// clean up connection
client.destroy()
});
}
sendJSCode('var Out; \n Out="TheSky Build=" + Application.build \n\r')
This script will:
Initiate a socket client
on connection successfully, client sends a message
client receives back response from that message
client prints response to terminal
Note that TheSkyX has a limitation of 4096 bytes for each message[2], any more than that and we will need to chunk the message. So you may want to keep the js-code short and precise.
that snippet I gave is minimal, it doesn't handle errors from server. If you want, you can add client.on("error", .. ) to handle it.
Your point of connecting to the socket server directly from browser is very intriguing, unfortunately it is not allowed by modern browsers natively due to security concerns 3
[1] https://socket.io/docs/#What-Socket-IO-is-not:~:text=That%20is%20why%20a%20WebSocket%20client,to%20a%20plain%20WebSocket%20server%20either.
[2] https://www.bisque.com/wp-content/scripttheskyx/scriptOverSocket.html#MSearchField:~:text=set%20to%204096%20bytes
so i am connecting a client to a server using websockets (ws). i successfully send msgs to the server and send it back to the client.Problem is when I try to write the received message to a file the server disconnects the client. The message is successfully written but i end up disconnecting client. Looks like something about the write functions disconnect my client. I am using fs.writFile(), I already tried fs.createWriteStream(). Reading the file however does not disconnect it.
const http = require('http');
const WebSocket = require('ws')
const fs = require('fs');
let counts = [0,0]
const server = http.createServer((req,res)=>{
console.log(' Received request for ' + request.url);
});
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection',(ws)=>{
console.log("serving...")
ws.on('message',(message)=>{
console.log("Received:"+message)
if(message ==='1'){
counts[0]=parseInt(counts[0])+1
fs.writeFile('votecnts.txt',`${counts[0].toString()} ${counts[1].toString()}`,(err) =>{
if(err) throw err
})
}
else if (message==='2'){
counts[1]=parseInt(counts[1])+1
fs.writeFile('votecnts.txt',`${counts[0].toString()} ${counts[1].toString()}`,(err) =>{
if(err) throw err
})
}
else{console.log(typeof(message))}
ws.send("cand_one: "+counts[0].toString()+"\n cand_two: "+counts[1].toString())
})
ws.on('close',function(){
console.log("lost client")
})
})
So I figured it out. I was running both server and client on localhost while developing. And therefore the file directories are the same. I later found out that it is impossible to write to file in javascript at the client side because of security reasons. So all I did was to change the url of the server to a remote machine and I was able to write to file using server code. specifically with the writeFile() function in the code above. I actually did not have to touch my code. It was just about configuration and set up.
I know it sound strange but the situation is strange. I have been trying to send second request to the server(over the same connection) before the first response. What I get is response to the first request and the second one is ignored by the server. The server keeps the connection alive:
server.on("connection", socket => {
socket.setTimeout(50 * 1000);
socket.on("timeout", () => {
console.log("socket timeout");
socket.end();
});
socket.on("close", hadError => {
console.log(
hadError
? "Socket closed due to ERROR during transmission"
: "Socket closed"
);
});
});
This is the route that handles the request:
app.post("/enddev", (req, res) => {
console.log("REQUEST TO ", req.route.path);
console.log("Req Header ", req.headers);
res.write("request\r\n");
setInterval(() => {
res.write("ack\r\n");
}, 2000);
});
I think that the HTTP protocol does not allow this, but may be there is some workaround.
Websockets are not solutions because the client is (not a browser) an embedded device.
And here is an image of the test (I use a TCP client terminal program ti simulate the request message from the client):
The right terminal (black one) is the node server. The white one is the terminal. On the terminal in pink is is what the client sent. In black is the response from the server. You can see that the server sends "request" message right after the header. But upon second request no response appears.
I don't think you're ever calling res.end(). Without that, node.js thinks your response is not over yet.
i have a simple js script that "try" to connect to listening server (SocketTest) as shown in the image below. It's really simple, just one line :
var exampleSocket = new WebSocket('ws://127.0.0.1:6601');
So, yes, it is connected but it stay in pending mode finishing by "failed: WebSocket opening handshake timed out"
Maybe i'm wrong somewhere but i don't see it... and it's really simple.
If someone got an idea....
Thanks in advance.
Without implementing open event do not expect any answer from the server :)
Try this:
const WebSocket = require('ws');
const ws = new WebSocket('ws://127.0.0.1:6601');
ws.on('open', function open() {
ws.send('something');
});
Edit: The nodejs tag fooled me I thought it is server side.
The same true on client side, you need to handle onopen event:
// Connection opened
exampleSocket.addEventListener('open', function (event) {
socket.send('Hello Server!');
});
// Listen for messages
exampleSocket.addEventListener('message', function (event) {
console.log('Message from server ', event.data);
});
Is it possible to terminate a websocket connection from server without closing the entire server? If it is then, how can I achieve it?
Note: I'm using NodeJS as back-end and 'ws' websocket module.
So because of some sort of omission in the documentation regarding ws.close() and ws.terminate() I think the solutions in provided answers won't close the sockets gracefully in some cases, thus keeping them hanging in the Event Loop.
Compare the next two methods of ws package:
ws.close():
Initializes close handshake, sending close frame to the peer and awaiting to receive close frame from the peer, after that sending FIN packet in attempt to perform a clean socket close. When answer received, the socket is destroyed. However, there is a closeTimeout that will destroy socket only as a worst case scenario, and it potentially could keep socket for additional 30 seconds, preventing the graceful exit with your custom timeout:
// ws/lib/WebSocket.js:21
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
ws.terminate():
Forcibly destroys the socket without closing frames or fin packets exchange, and does it instantly, without any timeout.
Hard shutdown
Considering all of the above, the "hard landing" scenario would be as follows:
wss.clients.forEach((socket) => {
// Soft close
socket.close();
process.nextTick(() => {
if ([socket.OPEN, socket.CLOSING].includes(socket.readyState)) {
// Socket still hangs, hard close
socket.terminate();
}
});
});
Soft shutdown
You can give your clients some time to respond, if you could allow yourself to wait for a while (but not 30 seconds):
// First sweep, soft close
wss.clients.forEach((socket) => {
socket.close();
});
setTimeout(() => {
// Second sweep, hard close
// for everyone who's left
wss.clients.forEach((socket) => {
if ([socket.OPEN, socket.CLOSING].includes(socket.readyState)) {
socket.terminate();
}
});
}, 10000);
Important: proper execution of close() method will emit 1000 close code for close event, while terminate() will signal abnormal close with 1006 (MDN WebSocket Close event).
If you want to kick ALL clients without closing the server you can do this:
for(const client of wss.clients)
{
client.close();
}
you can also filter wss.clients too if you want to look for one in particular. If you want to kick a client as part of the connection logic (i.e. it sends bad data etc), you can do this:
let WebSocketServer = require("ws").Server;
let wss = new WebSocketServer ({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.send('something');
ws.close(); // <- this closes the connection from the server
});
and with a basic client
"use strict";
const WebSocket = require("ws");
let ws = new WebSocket("ws://localhost:8080");
ws.onopen = () => {
console.log("opened");
};
ws.onmessage = (m) => {
console.log(m.data);
};
ws.onclose = () => {
console.log("closed");
};
you'll get:
d:/example/node client
opened
something
closed
According to the ws documentation, you need to call websocket.close() to terminate a connection.
let server = new WebSocketServer(options);
server.on('connection', ws => {
ws.close(); //terminate this connection
});
Just use ws.close() in this way.
var socketServer = new WebSocketServer();
socketServer.on('connection', function (ws) {
ws.close(); //Close connecton for connected client ws
});
If you use var client = net.createConnection() to create the socket you can use client.destroy() to destroy it.
With ws it should be:
var server = new WebSocketServer();
server.on('connection', function (socket) {
// Do something and then
socket.close(); //quit this connection
});