I have difficulties detecting connection errors with MQTT.js.
I'm trying to use it in an Angular service and connecting and communicating with the server seems to work fine when the server is running but client.on('error', ...) never fires.
I managed to detect errors when connecting by attaching the error callback to client.stream instead, but stream seems to be a private property since it isn't exposed in the official TypeScript definitions, so not sure if this really is the correct way to go.
But what I still don't get to work are errors when the connection is lost, none of the error handlers fire. Instead the browser console logs unhandled websocket errors.
Sample code:
import { Injectable } from '#angular/core';
import { connect, MqttClient } from 'mqtt';
import * as msgpack5 from 'msgpack5';
import { environment } from '../../environments/environment';
#Injectable()
export class MqttService {
private client: MqttClient;
private msgpack: msgpack5.MessagePack;
constructor() {
this.msgpack = msgpack5();
this.client = connect(`ws://${environment.host}:8001/mqtt`);
this.client.on('connect', () => {
console.log('connected');
this.client.subscribe('foo');
});
this.client['stream'].on('error', (error) => {
// fires when connection can't be established
// but not when an established connection is lost
console.log('stream', error);
this.client.end();
});
this.client.on('error', (error) => {
console.log('client', error); // never fires
});
this.client.on('message', (topic, payload) => {
console.log(topic, this.msgpack.decode(payload));
});
}
}
I had a very similar issue with client.on('error'.. didnt trigger on WebSocket connection error, the solution was:
client.stream.on('error', (err) => {
console.log('error', err);
client.end()
});
Related
I'm trying to implement a series of transport providers following a provider architecture model. Therefore, I created an abstract transport provider, which is then extended by specific providers, such as mqtt, nats, etc.
However, I'm having trouble with initializing an mqtt client. The code for initialising, connecting to and then interacting with the client, when put outside the handler, functions correctly. However, the same exact code inside a provider method connect() does not function as intended (it seems to initialize the client when watching with debugger and then skips through all the client.on functions).
This is the abstract provider, which simply outlines the methods that need to be used:
class abTransProvider {
constructor() {
this.client;
this.host;
this.username = "guest";
this.password = "guest";
if (this.constructor == abTransProvider) {
throw new Error("Abstract classes cannot be instantiated.");
};
};
init() {
throw new Error("Abstract method is not implemented");
};
connect(host) {
throw new Error("Abstract method is not implemented");
};
};
module.exports = {
abTransProvider: abTransProvider
};
This is the code for the mqtt handler itself. Note that this is just a basic skeleton, since I'm only testing the connection method now (for which there are only return values for connection and error):
const mqtt = require("mqtt");
const abTransProvider = require("../abTransProvider.js");
class specProvider extends abTransProvider.abTransProvider {
init() {
}
connect() {
this.host = "mqtt://localhost:1883";
this.client = mqtt.connect(this.host, {
username: this.username,
password: this.password,
});
// Mqtt error callback
this.client.on("error", (err) => {
console.log(err);
this.client.end();
return 1;
});
// Connection callback
this.client.on("connect", () => {
console.log(`MQTT client connected`);
return 0;
});
// MQTT subscriptions
this.client.subscribe("value", { qos: 0 });
// When a message arrives, log it
this.client.on("message", function (topic, message) {
console.log(message.toString());
});
this.client.on("close", () => {
console.log(`MQTT client disconnected`);
});
}
}
module.exports = {
specProvider: specProvider,
};
This is a simple testing program that calls the connect method:
const trProv = require("mqtt.js");
const transport = new trProv.specProvider();
const trCheck = transport.connect();
if (trCheck !== 0) {
console.log("Error when connecting to the provider")
process.exit(1)
} else {
console.log("Connection successful")
}
When the testing program calls the mqtt handler method connect(), it seems to instantiate the client but the connected flag is false. However, instead of logging an error (if there is one), the program simply skips through the client.on functions and, therefore, the return value is undefined. I would highly appreciate any feedback/comments/advice on how to resolve this! Thank you!
This code really doesn't do what you think is does.
All the return statements are in the lambda functions you have passed to the event handlers, so they will have no effect on the connect() method. They just set the return value of the lambda (which will be ignored because event callbacks don't return anything).
And as a resultconnect() ends without returning anything hence the undrfinded.
Probably the best option here is to have connect() return a promise that can be resolved in the on('connected',...) handler or rejected in the on('error',...) handler (but error can get called later so you need to be careful there).
I tried to use rxjs webSocket to connect server. When I use .complete() function, the closingObserver is triggered immediately. But I received CloseEvent is almost 1 minute later.
So, how can I get the CloseEvent immediately?
Here is an example.
class SomeService {
private wsSource$ = webSocket({
url: "ws://xxxxxx.com/api/v1/xxx",
openObserver: this.openObserver,
closingObserver: this.closingObserver,
});
connect() {
this.wsSource$.subscribe({
next: (e) => console.log(e),
error: (err) => console.log(err),
complete: () => console.log("complete!!!")
});
}
disconnect() {
this.wsSource$.complete();
}
}
After connect(), Chrome will log the CloseEvent after almost 1 minute.
It is not logged by observer complete function, it is logged by error function.
How can I keep attempting to connect to signalR in a safe way that allows a few hundred milliseconds (at least) in between attempts until the connection is established and if so, could someone provide a better way pf dealing with the various stages of connectivity to the web-sockets?
We are using signalR on a .NET backend and I am trying to use it to display real-time push notifications in an Angular 4 UI. It works great when it connects, but there is a problem where sometimes it takes 2-3 times to connect. I also get an error this.startSignalRListener is not a function if I put it inside the catch block to try to reconnect that way.
Any suggestions would be greatly appreciated.
I am using "#aspnet/signalr-client": "^1.0.0-alpha2-final" in package.json
Here is some of my code from my service class...
import { HubConnection } from '#aspnet/signalr-client';
#Injectable()
export class RealTimeService implements OnInit {
private hubConnection: HubConnection;
constructor() {}
ngOnInit() {
this.startSignalRListener();
}
public onConnection(): void {
const domain = this.state.domain;
this.hubConnection
.invoke('WatchSomething', [ 'abc.com' ])
.catch(err => console.error('signalR error: ', err));
this.hubConnection.on('some_thing', (res: any) => {
console.log('do stuff...', res);
});
}
public startSignalRListener() {
const connection = `www.realtimeapi.com`;
this.hubConnection = new HubConnection(connection);
this.hubConnection
.start()
.then(() => {
this.onConnection();
})
.catch(err => {
console.log('Error while establishing connection...');
});
}
}
How can I best reconnect when a connection fails? Any suggestion would really help me out as it fails often on the first try.
You can try to use the setTimeout function to delay your reconnects:
.catch(err => {
console.log('Error while establishing connection... Retrying...');
setTimeout(() => this.startSignalRListener(), 3000);
});
In this case I suggest you to move the this.hubConnection.on('domain_bid' out of startSignalRListener to bind this stuff only once.
Below is my connection request code :
doLogin(this.login).then(response => {
var token = response.data.token;
localStorage.setItem('AUTH_TOKEN', JSON.stringify(token));
this.$router.push({name: 'Devices'});
});
}).catch(error => {
console.log(error.response.data.message);
});
the catch() part works fine for http errors (like 400, 404, 403..etc). But when my server is offline this script just throws net::ERR_CONNECTION_REFUSED. Is there any way to handle this error and let the front-end user know that the server is currently offline.?
Here is the doLogin() function just in case,
function doLogin(data) {
const url = 'http://localhost:3000/authenticate';
return axios.post(url,data);
}
You can try this in the catch part:
catch(error => {
if (!error.response) {
// network error
this.errorStatus = 'Error: Network Error';
} else {
this.errorStatus = error.response.data.message;
}
})
You may use interceptors:
axios.interceptors.response.use(
response => {
return response
},
error => {
if (!error.response) {
console.log("Please check your internet connection.");
}
return Promise.reject(error)
}
)
You should do the same validation that #chithra pointed out in the .then() because i'm having a weird issue when testing requests with my servers down, the "response" comes as if it was a success.
Also, on the .then() do it with response.status instead of response.error
By using npm; a standard package manager for the JavaScript runtime environment Node.js.
With npm:
npm i axios
Next, you should import Axios in your src/App.vue file
import axios from 'axios';
you will need to call it on a lifecycle hook. Here we will use the beforeCreated() lifecycle hook, this is because we will be able to retrieve sensitive data and events that are active with the beforeCreated hook.
data() {
return {
network: false,
}; },
beforeCreate() {
axios
.get("http://localhost:13172/api/product/getproducts")
.then((response) => {
console.log(response);
this.network = true;
})
.catch((error) => {
console.log(error), (this.network = false);
}); }
When my application loses the internet connection, I get an error coming from the adapter and I'm not sure why it does that.
Error: Adapter operation failed
at new Error (native)
at Error.EmberError (http://localhost:5966/assets/vendor.js:25883:21)
at Error.ember$data$lib$adapters$errors$$AdapterError (http://localhost:5966/assets/vendor.js:66151:50)
at ember$data$lib$system$adapter$$default.extend.handleResponse (http://localhost:5966/assets/vendor.js:67455:16)
My application adapter looks like this:
export default DS.JSONAPIAdapter.extend(DataAdapterMixin, {
host: config.apiUrl,
handleResponse(status, headers, payload) {
if (status === 422 && payload.errors) {
return new DS.InvalidError(payload.errors);
}
return this._super(...arguments);
}
});
The error action in my application route never gets triggered.
export default Ember.Route.extend({
actions: {
error(error, transition) {
console.log(error, transition); //Never displayed
}
}
});
I'm making the call to the store in my controller.
export default Ember.Controller.extend({
actions: {
getUsers() {
this.get('store').findAll('user').then((users) => {
this.set('users', users);
});
}
}
});
Any idea how to fix this error and trigger the error hook in my route?
Thanks
I think you have to catch the error yourself so it doesn't get caught by the ember data implementation.
getUsers() {
this.get('store').findAll('user').then((users) => {
this.set('users', users);
}).catch((error) => {
// Add some custom error handling or do nothing to prevent the exception from getting thrown
});
}
Also your error hook in the route will only get fired when a promise in a transition (for example in one of the model hooks) rejects. If you have a promise in a controller you have to trigger the action/event yourself.