I have a simple client-side script like this:
function connect() {
const { contextBridge } = require('electron');
var ws = new WebSocket('ws://localhost:3000');
ws.onerror = (error) => {
console.error(`Lost connection to server. Reason: ${error.message}`);
console.error('Attempting to reconnect...');
ws.close();
}
ws.onclose = (e) => {
setTimeout({
connect();
}, 500);
}
ws.addEventListener('open', () => {
console.log('Connected to server!');
});
// Some other stuff to call functions via the browser console
const API = {
ws_isOpen: () => { return ws.readyState === ws.OPEN }
}
contextBridge.exposeInMainWorld('api', API);
function send_msg(msg) {
// Process some data...
ws.send(msg);
}
}
connect();
It works normally when the server is running and it's trying to connect, or when the server is rebooting and it's trying to connect for the first time, but not while it's connected. What I mean is that, if I were to suddenly shut the server down while the client is being connected to it, it attempts to try to reconnect as usual and the success message does pop up. However, if I type in window.api.ws_isOpen() in the browser console, it returns false. When I try to send a message, an error pops up saying something like Websocket is already in CLOSING or CLOSED stage. I tried changing the ws variable type to let and const but it doesn't work.
Turns out the answer is really simple. For some reason, when I put the ws variable outside the connect() function and modify it in the function, it works. I'm guessing it kinda re-declares/re-new the ws variable. It looks something like this:
var ws = null;
function connect() {
ws = new WebSocket('ws://localhost:3000');
// the exact same as above here....
}
connect();
After rebooting the server and letting it reconnect:
>> window.api.ws_isOpen()
true
I feel like I'm supposed to know how this works...
Related
I have an app with divided code (client / server). On the client side, I'd like socket io to attempt multiple URLs (one at a time) until it connects successfully.
Here's my code:
const BAD_HOST = "http://localhost:8081";
const LOCAL_HOST = "http://localhost:8080";
const SOCKET_CONFIG = {
upgrade: false,
transports: ["websocket"],
auth: { ... }, // Trimmed for brevity
extraHeaders: { ... }, // Trimmed for brevity
};
let socket = io(BAD_HOST, SOCKET_CONFIG); // This connects fine when I use LOCAL_HOST
socket.on("connect_error", (err) => {
console.log(err);
socket = io(LOCAL_HOST, SOCKET_CONFIG); // DOES NOT WORK
});
socket.on("connect", () => { ... } // Trimmed for brevity
In short, when I try to reassign the value for socket to a new io connection, it seems to retain the old, failed connection. My browser continues to throw 'connect_error' messages from the bad url:
WebSocket connection to 'ws://localhost:8081/socket.io/?EIO=4&transport=websocket' failed:
I checked but couldn't find any official documentation on this question.
I think an approach is already discussed here:
https://stackoverflow.com/a/22722710/656708
Essentially you have an array of URLs, which in your case would be:
const socketServerURLs = ["http://localhost:8081","http://localhost:8080"];
and then iterate over them, trying to initiate a socket connection, like this:
// something along these lines
socketServerURLs.forEach((url) => {
// ...
socket.connect(url, socketConfiguration, (client) => {});
// ...
}
Then again, I don't know what a BAD_HOST entails. Assuming that you mean that a connection to that host failed, how would you know that without actually trying to connect to it?
I am using Azure Pub-Sub Service for Chatting module in a ReactApplication, I am creating this connection using Websocket.
let ws = new WebSocket(token.url);
ws.onmessage = (data) => {
//Messages Logic
}
when i am in other tabs, or in the sametab for longer time(more than 40-45 mins). I am not receiving messages, but when i refresh the page and websocket initialization code gets executed again and then i receive messages again. Any Suggestions?
Use this technique :
function connect() {
var ws = new WebSocket('ws://localhost:8080');
ws.onopen = function() {
// subscribe to some channels
ws.send(JSON.stringify({
//.... some message the I must send when I connect ....
}));
};
ws.onclose = function(e) {
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
setTimeout(function() {
connect();
}, 1000);
};
I'm fairly new to Javascript and am trying to wrap my head around async, promises, etc.
I have an application running a TCP API (non-HTTP) on the localhost. I'm building an Electron app to interact with this API. I need to send a single request to the API every second and retrieve a single JSON object it returns.
I'm able to do this successfully (for while) by running something like this:
const net = require('net');
function apiCall() {
if (running) {
setTimeout(() => {
// Send the request
request = '{"id":1,"jsonrpc":"2.0","method":"getdetails"}'
socketClient = net.connect({host:'localhost', port:8888}, () => {
socketClient.write(request + '\r\n');
});
// Listen for the response
var response;
socketClient.on('data', (data) => {
response = JSON.parse(data).result;
updateUI(response);
socketClient.end();
});
// On disconnect
socketClient.on('end', () => {
console.log('Disconnected from API');
});
apiCall();
}, refreshRate)
}
}
After running this for an extended amount of time, it appears that the API server is crashing:
Error: connect ECONNREFUSED 127.0.0.1:8888
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1146)
Unfortunately, I have no control over the API server or its source code. I'd like some clarification on whether my client might be causing the API server to crash by sending requests this way.
Should I be opening and closing the connection for each request or keep it open and send requests only every second?
If I should be keeping the connection open, how can I do this, and do I need to worry about keep-alive?
It looks like that every time you call apiCall you are creating a new socket client and you are not removing the old socket client instances. This is a memory leak and it will cause the application to crash after running for some time
You can keep a running connection instead like below
const net = require("net");
const { once } = require("events");
let socketClient;
function apiCall() {
if (running) {
setTimeout(async () => {
const request = '{"id":1,"jsonrpc":"2.0","method":"getdetails"}';
// Create the socket client if it was not already created
if (!socketClient) {
socketClient = net.connect({ host: "localhost", port: 8888 });
// On disconnect
socketClient.on("end", () => {
console.log("Disconnected from API");
socketClient.destroy();
socketClient = null;
});
// Wait until connection is established
await once(socketClient, "connect");
}
// Send the request
socketClient.write(request + "\r\n");
// Listen for the response
const data = await once(socketClient, "data");
const response = JSON.parse(data).result;
updateUI(response);
apiCall();
}, refreshRate);
}
}
So I have designed a basic Publisher-Subscriber model using rhea in JS that takes an API request for saving data in DB and then publishes it to a queue.
From there a subscriber(code added below) picks it up and tries to save it in a DB. Now my issue is that this DB instance goes through a lot of changes during development period and can result in errors during insert operations.
So now when the subscriber tries to push to this DB and it results in an error, the data is lost since it was dequeued. I'm a total novice in JS so is there a way to make sure that a message isn't dequeued unless we are sure that it is saved properly without having to publish it again on error?
The code for my subscriber:
const Receiver = require("rhea");
const config = {
PORT: 5672,
host: "localhost"
};
let receiveClient;
function connectReceiver() {
const receiverConnection = Receiver.connect(config);
const receiver = receiverConnection.open_receiver("send_message");
receiver.on("connection_open", function () {
console.log("Subscriber connected through AMQP");
});
receiver.on("error", function (err) {
console.log("Error with Subscriber:", err);
});
receiver.on("message", function (element) {
if (element.message.body === 'detach') {
element.receiver.detach();
}
else if (element.message.body === 'close') {
element.receiver.close();
}
else {
//save in DB
}
}
receiveClient = receiver;
return receiveClient;
}
You can use code like this to explicitly accept the message or release it back to the sender:
try {
save_in_db(event.message);
event.delivery.accept();
} catch {
event.delivery.release();
}
See the delivery docs for more info.
Here's my methods to open/close socket connection:
methods: {
connect () {
this.$socket.onopen = () => {
this.status = 'connected'
this.$socket.onmessage = ({data}) => {
this.$socket.send(this.message)
console.log({ event: "Recieved message", data })
}
}
this.$socket.onclose = (e) => {
console.log('Socket is closed. Reconnect will be attempted in 1 second.')
setTimeout(() => {
this.connect()
}, 1000)
}
},
disconnect () {
this.$socket.close()
this.status = 'disconnected'
}
}
I'm not using socket.io etc, just built in Websocket object.
When i manually call disconnect () method - it closes the connection as expected, but when i send message again - it says that connection is closed. Since i call connect () in a mounted, then it won't reconnect if i don't refresh the page.
I've tried using watcher:
watch: {
'$socket': 'connect'
}
But no effect. Is there a way to watch for websocket connection status? If it's closed - call connect (), if it's error'ed - call connect () to reconnect it.
Your connect() method does nothing to "reconnect" to the WebSocket server. So calling this.connect() simply rewrites your onopen handler.
You have to take the steps necessary to reconnect to the WebSocket server. The most excellent answer to this question does a great job of explaining a great structure for your code:
vue: emitting global events from websocket listener
Unfortunately, it doesn't answer your specific question. So I've forked the sandbox from that answer and added the modified code below that allows you to achieve your goal.
import Vue from "vue";
const url = "wss://echo.websocket.org";
let socket;
const emitter = new Vue({
methods: {
send(message) {
if (1 === socket.readyState) socket.send(message);
},
close() {
if (1 === socket.readyState) {
emitter.$emit("message", "Closing Socket.");
socket.close();
socket = null; // prevent memory leak
}
},
connect() {
socket = new WebSocket(url);
socket.onmessage = function(msg) {
emitter.$emit("message", msg.data);
};
socket.onerror = function(err) {
emitter.$emit("error", err);
};
emitter.$emit("message", "Openning Socket.");
}
}
});
emitter.connect();
export default emitter;
To see how this service is used, check out index.js in the running sample is here:
https://codesandbox.io/s/ry4993q654
You should check the close status code before reconnecting.
e.code === 1e3 || e.code === 1001 || e.code === 1005