SignalR Start()'s task is continuing even if connection is not possible - signalr

I have a C# SignalR client and I want to do some actions upon success/failure of the connection to my server. Here is my code :
this.connection.Start().ContinueWith(task =>
{
if (task.IsFaulted)
{
this.OnRaiseServerConnectionClosedEvent();
}
else
{
this.JoinGroup();
this.StopTimer();
this.OnRaiseServerConnectionOpenedEvent();
}
});
}
The else block is always executed, not caring if a server is here or not...
I have also tried with await or with Wait() but same scenario.
I understand .net tasks correctly I think but here I am stuck.
Right now my code looks like
try
{
this.connection.Start().Wait();
if (this.connection.State == ConnectionState.Connected)
{
this.JoinGroup();
this.StopTimer();
this.OnRaiseServerConnectionOpenedEvent();
}
}
catch (AggregateException)
{
this.OnRaiseServerConnectionClosedEvent();
}
catch (InvalidOperationException)
{
this.OnRaiseServerConnectionClosedEvent();
}
When no server is present, the task created by the Start() method returns without fault and with status connecting. You have to check the state of the connection if you want to follow with some actions or retry connecting.

The task you are receiving from Connection.Start is likely cancelled due to a timeout instead of being faulted. This should be an easy fix:
this.connection.Start().ContinueWith(task =>
{
if (task.IsFaulted || task.IsCanceled)
{
this.OnRaiseServerConnectionClosedEvent();
}
else
{
this.JoinGroup();
this.StopTimer();
this.OnRaiseServerConnectionOpenedEvent();
}
});
If you use Wait() instead of ContinueWith, an AggregateException containing an OperationCanceledException in its InnerExceptions collection will be thrown when the task is canceled.

Related

Catch Socket Exception [duplicate]

This is probably a noob question, but how do I make my response throw an exception if the user does not have an internet connection or if it takes too long to fetch the data?
Future<TransactionModel> getDetailedTransaction(String crypto) async {
//TODO Make it return an error if there is no internet or takes too long!
http.Response response = await http.get(crypto);
return parsedJson(response);
}
You should surround it with try catch block, like so:
import 'package:http/http.dart' as http;
int timeout = 5;
try {
http.Response response = await http.get('someUrl').
timeout(Duration(seconds: timeout));
if (response.statusCode == 200) {
// do something
} else {
// handle it
}
} on TimeoutException catch (e) {
print('Timeout Error: $e');
} on SocketException catch (e) {
print('Socket Error: $e');
} on Error catch (e) {
print('General Error: $e');
}
Socket exception will be raised immediately if the phone is aware that there is no connectivity (like both WiFi and Data connection are turned off).
Timeout exception will be raised after the given timeout, like if the server takes too long to reply or users connection is very poor etc.
Also don't forget to handle the situation if the response code isn't = 200.
You don't need to use http to check the connectivity yourself, simply use connectivity library
You can use this plugin https://pub.dev/packages/data_connection_checker
So you can check prior if you have the connection, if not give a alert to the user that no internet connection. And if you have the internet connection then just proceed to your fetching part.
I will just link some resources below where it has been explained perfectly:
https://www.youtube.com/watch?v=u_Xyqo6lhFE
This is all things will be done prior to making an http call, but what if while making an http call the internet goes off then you can use the try catch block which #uros has mentioned.
Let me know if it works.
This is my approach to check internet connection to check internet connection throughout full app
i create a common class called "connectivity" & use it everywhere in app to check connectivity.i use connectivity package by flutter.
My connectivity class
Future<bool> check() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
return true;
} else if (connectivityResult == ConnectivityResult.wifi) {
return true;
}
return false;
}
then i use this class like this:
#override
void initState() {
super.initState();
checkInternetConnection().then((internet) {
if (internet != null && internet) {
// Internet Present Case
// do your task;
} else {
// No-Internet Case
showAlertDialog(context);
}
});
}

How to handle socket exception when net is not there?

I want to display an error screen if net is not there. I am not using connectivity package because I don't want continuous check. I just want to handle exception while calling backend api and display the screen. I am unable to catch the exception.
I found this issue and this question about socket exceptions but none seem to help me.
This is how I call my backend api -
callBackendApi() async {
try {
http.Response response = await Future.value(/*api call here*/)
.timeout(Duration(seconds: 90), onTimeout: () {
print('TIME OUT HAPPENED');
});
} catch (exception) {
Fluttertoast.showToast(msg: 'Check internet connection.');
print('Error occurred' + exception.toString());
}
}
I use dio like this:
try {
var formData = FormData.from(Map<String, dynamic>.from(data));
var response = await dio.post(
uri,
data: formData,
);
jsonResponse = json.decode(response.data);
} on DioError catch (e) {
if (DioErrorType.RECEIVE_TIMEOUT == e.type ||
DioErrorType.CONNECT_TIMEOUT == e.type) {
throw CommunicationTimeoutException(
"Server is not reachable. Please verify your internet connection and try again");
} else if (DioErrorType.RESPONSE == e.type) {
// 4xx 5xx response
// throw exception...
} else if (DioErrorType.DEFAULT == e.type) {
if (e.message.contains('SocketException')) {
throw CommunicationTimeoutException('blabla');
}
} else {
throw CommunicationException("Problem connecting to the server. Please try again.");
}
}
My solution is to import 'dart.io' in order to catch SocketException from try block:
import 'package:http/http.dart' as http;
import 'dart:io';
try{
//Handle you network call code block in here
}on SocketException catch(_){
//To handle Socket Exception in case network connection is not available during initiating your network call
}
Well i don't know if my answer will be solving your question but days ago i had a problem little bit likely yours but in my case was using firebase realtime database. I was asking to myself how can i protect my app from network fails like no internet connection available? Well i am not using connectivity package too so i solve this problem with an approach that you already has been try using a timeout for network operations. I will share two snipets with differents approaches that i had implemented to handle this kind of problem adding some comments trying explain the differences between them.
Approach 1 - Setting timeout outside from network request method
Well the snipet below is a simple firebase database request where _viewsRef is a DatabaseReference and the once method do the request and returns me a Future with or without data.
// get users visualization from realtime database and returns a future
static Future<DataSnapshot> getUserVisualizations({#required String uid}) async {
return _viewsRef.child(uid).limitToLast(50).once();
}
In my BLoC component class i am calling the method below and setting a timeout to the future that is returned.
myBlocComponentMethod(){
//.. some work and finally the call
FirebaseUserViewsHelper.getUserVisualizations(uid: _currentUid)
.then(
(dataSnapshot){
if (dataSnapshot.value == null) {
// do some things to handle no data
}
else {
/// handle your data here
});
}
} // setting timeout here is an important point
).timeout( Duration(seconds: Constants.NETWORK_TIMEOUT_SECONDS),
onTimeout: (){
// method to handle a timeout exception and tell to view layer that
// network operation fails
// if we do not implement onTimeout callback the framework will throw a TimeoutException
} );
}
Well what is the point here? In this case if the timeout expires and future is not completed yet onTimeout callback is called and there i can tell to the view layer that network operation fails and show to the user some widget about it. But even with timeout expired the request to firebase database stays happening again and again, it's like the async event of request the database stays on dart event queue. I think this behavior is bad for performance aspects but if you're building your UI using a StreamBuilder with a little logic and code your requested data will be available right when you internet connection is back and with BLoC pattern the UI can respond easily to this event and we don't need provide a refresh button by example to user make the request again. I don't know if this is the right approach to implement this behavior but it works.
Approach 2 - Setting timeout inside from network request method
Below another firebase database request method
static Future<DataSnapshot> readUserNode( {#required String uid} ) async
=> USERS_REFERENCE.child(uid).once()
.timeout( Duration(seconds: Constants.NETWORK_TIMEOUT_SECONDS ) );
//note: Without timeout callback this line will throw a TimeoutException if the time expires
The usage in another BLoc component:
myBlocComponentMethod2(){
for( String uid in iterable ){
FirebaseUserHelper.readUserNode(uid: uid)
.then( (userSnapshot){
if (userSnapshot.value == null){
// do your stuffs
}
else {
// more stuffs to do
}
}).catchError( (error){
// when timeout expired we will catch the TimeoutException HERE and handling telling
// the UI what we need
} );
}
}
The big difference here that i get was in the behavior. In this second case since i put the timeout inside the request method when the timeout expires the request event do not run anymore, it's like that request event is removed from dart event queue. This can be good from performance perspective but now we need provide a refresh button in UI for user do the data again to get data from internet again.
I don't know if this workaround will solve your problem because you tell about SocketException what is not the case that i has described and i don't know what api you are using to make your requests. Anyway i hope that the concepts described in this post helps you implement a solution in your problem.

Catch the Process Timeout RuntimeException

In my project, I use the ProcessCompoment and I use a timeout of 120 seconds.
$process->setTimeout(120);
$process->run();
// executes after the command finishes
if (!$process->isSuccessful()) {
$job->setResult('error');
} else {
$job->setResult($process->getOutput());
}
The problem is, when the timeout is reached Symfony return a RuntimeException, but I need to know when this command is aborted, to store an error in place of the result in my database.
Someone have an idea ?
Finally I just use a simple try-catch...
$process->setTimeout(120);
try {
$process->run();
} catch (RuntimeException $exception) {
$job->setResult('error');
}

How to handle timeout in queries with Firebase

I noticed that if I execute a query in Firebase and the database server is not reachable, the callback waits just forever (or until the server is reachable again).
Where this behavior is quite natural for the asynchronous approach used, it would nevertheless be useful to have an easy way to specify a timeout so you could inform the user about the status.
Is there such an option and I just missed it - or it really missing?
Or how would you solve this problem?
you can manage yourself a timer controller that after x seconds remove the listener to you firebase reference. It's very simple, just one line of code in android for example.
You can see the code for the web (Detaching Callbacks section):
https://www.firebase.com/docs/web/guide/retrieving-data.html
or for android (Detaching Callbacks section):
https://www.firebase.com/docs/android/guide/retrieving-data.html#section-detaching
same section for IOS ;)
As per today there is no timeout concept on those listeners. One option is to manage the timeout yourself.
This is how I do it when I also want to display a progress dialog while loading the content.
private void showProgressDialog(boolean show, long time) {
try {
if (progressDialog != null) {
if (show) {
progressDialog.setMessage("Cargando...");
progressDialog.show();
new Handler().postDelayed(new Runnable() {
public void run() {
if(progressDialog!=null && progressDialog.isShowing()) {
progressDialog.dismiss();
Toast.makeText(ActPreguntas.this, "Couldn't connect, please try again later.", Toast.LENGTH_LONG).show();
}
}
}, time);
} else {
progressDialog.dismiss();
}
}
}catch(IllegalArgumentException e){
}catch(Exception e){
}
}
So when you make a request to Firebase you call showProgressDialog(true,5000) and after 5 seconds if the dialog stills there is because it could not connect and you then do what you have to as per the timeout.
On the callback of the Firebase listener you do this showProgressDialog(false,0)
Hope it helps.
Here's my solution for the Firebase iOS SDK, this may be helpful for others:
extension DatabaseReference {
func observe(_ eventType: DataEventType, timeout: TimeInterval, with block: #escaping (DataSnapshot?) -> Void) -> UInt {
var handle: UInt!
let timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { (_) in
self.removeObserver(withHandle: handle)
block(nil)
}
handle = observe(eventType) { (snapshot) in
timer.invalidate()
block(snapshot)
}
return handle
}
}
Usage:
database.child("users").observe(.value, timeout: 30) { (snapshot) in
guard let snapshot = snapshot else {
// Timeout!
return
}
// We got data within the timeout, so do something with snapshot.value
}
I would suggest simply using a thread?
Allow yourself to assign your call to Firebase from within a thread instance, then in the rare event that the write to Firebase takes too long you can just cancel the thread?
let thread = NSThread(target:self, selector:#selector(uploadToFirebase), object:nil)
. . .
func uploadToFirebase(data: Dictionary) {
// Do what you need to here. Just an example
db.collection("posts").document("some unique post id").setData([
"name": "John",
"likes": 0
]) { err in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Document successfully written!")
}
}
}
Then just create a timer that cancels the thread if the timer fires. If not, just cancel the timer.
If you're using the Firebase SDK v6.5.0 and above, you can use FirebaseOptions's setConnectTimeout (https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/FirebaseOptions.Builder.html#setConnectTimeout(int)).
Sample:
Integer connectTimeoutinMillis = 6000; //6 seconds
FirebaseOptions firebaseOptions = FirebaseOptions.builder()
.setCredentials(credentials)
.setDatabaseUrl(Application.firebaseSDKDatabaseUrl)
.setConnectTimeout(connectTimeoutinMillis)
.build();
FirebaseApp.initializeApp(firebaseOptions);

Is there a way to specify the wait time of retrying a message?

Is there a way to specify the wait time of retrying a message for a particular exception?
E.g. If object is in SomethingInProgress status, throws an SomethignInProgressException and I want to the message to be retry after 40m. Or is it more appropriate to raise a SomethingInProgressEvent and use bus.defer?
This is part of the reason why Rebus does not have the concept of second-level retries - I've simply not seen any way that this function could be created in a way that was generic and still flexible enough.
To answer your question shortly: No, there's no (built-in) way of varying the time between retries for a particular exception. In fact, there's no way to configure a wait time between retries at all - failing messages will be retried as fast as possibly, and then moved to the error queue if they keep failing to avoid "clogging up the pipes".
In your case, I suggest you do something like this:
public void Handle(MyMessage message) {
var headers = MessageContext.GetCurrent().Headers;
var deliveryAttempt = headers.ContainsKey("attempt_no")
? Convert.ToInt(headers["attempt_no"])
: 0;
try {
DoWhateverWithThe(message);
} catch(OneKindOfException e) {
if (deliveryAttempt > 5) {
bus.Advanced.Routing.ForwardCurrentMessage("error");
return;
}
bus.AttachHeader(message, "attempt_no", deliveryAttempt + 1);
bus.Defer(TimeSpan.FromSeconds(20), message);
} catch(AnotherKindOfException e) {
if (deliveryAttempt > 5) {
bus.Advanced.Routing.ForwardCurrentMessage("error");
return;
}
bus.AttachHeader(message, "attempt_no", deliveryAttempt + 1);
bus.Defer(TimeSpan.FromMinutes(2), message);
}
}
which I just wrote off the top of my head without being 100% certain that it actually compiles ... but the gist of it is that we track how many delivery attempts we've made in a custom header on the message, bus.Deferring the message an appropriate time span for each failed delivery attempt, immediately forwarding the message to the error queue when our max # of delivery attempts has been exceeded.
I hope that makes sense :)
A more recent example of how to do this is:
public async Task Handle(IFailed<MyMessage> message)
{
var maxAttempts = 10;
var optionalHeaders = new Dictionary<string, string>();
if (message.Headers != null && message.Headers.ContainsKey("attemptNumber"))
{
// increment the attempt number
var attemptNumber = int.Parse(message.Headers["attemptNumber"]);
attemptNumber++;
optionalHeaders.Add("attemptNumber", attemptNumber.ToString());
if (attemptNumber > maxAttempts)
{
// log I give up message, message will move to dead queue
return;
}
}
else
optionalHeaders.Add("attemptNumber", "1");
// if message failed to process, defer processing for 5 minutes and try again
await Bus.Defer(TimeSpan.FromMinutes(5), message.Message, optionalHeaders);
}

Resources