Open SteveAlexander opened 5 years ago
Hi, did you found some solution?
yes, my workaround is to create a WebSocket
directly. Something like:
final socket = await WebSocket
.connect(url.toString())
.timeout(_webSocketConnectionTimeout);
return IOWebSocketChannel(socket);
Then I wrap this in a try-catch, so I can catch SocketException
and TimeoutException
and handle these in a way that makes sense for my app.
I got it. Thanks.
I have the same problem, but I thought that the exception should be handled in socketClient.stream.listen( method)
, this method params receives an Error handler, so i decided put one like this:
socketClient.stream.listen(
(message){
// handling incoming messages
},
onError: //Here a put a Error handling function,
onDone: function(){
// communication has been closed
}
);
But nothing happends.
I have the same problem, but I thought that the exception should be handled in
socketClient.stream.listen( method)
, this method params receives an Error handler, so i decided put one like this:socketClient.stream.listen( (message){ // handling incoming messages }, onError: //Here a put a Error handling function, onDone: function(){ // communication has been closed } );
But nothing happends.
@roger357, the following way works for me:
stream = widget.webSocketChannel.stream;
streamSubscription = stream.listen(
onData,
onError: (error) {
// method calls and what not here
},
cancelOnError: true);
}
I don't know if it is working because I have a StreamSubscription or whether it simply is an additional step.
I was facing same issue turned out, unhandled Socket execption was thrown during connect method. not by the stream.
I believe IOWenSocketChannel would have similar approach to await and catch error
WebSocketChannel channel = WebSocketChannel.connect(uri );
try {
await channel.ready;
} catch (e) {
// handle exception here
print("WebsocketChannel was unable to establishconnection");
}
Stream stream = channel.stream;
stream.listen((event) {
print('Event from Stream: $event');
},onError: (e){
// handle stream error
},
onDone: (() {
// stream on done callback...
}),
cancelOnError: true
);
While @adityabansalx solution works, I find it weird that adding await channel.ready
makes it catch the error. Can someone please explain this behavior? 🤔
Hey @Jalmoud2, what I understood is, calling connect gives a channel immediately, and initiates the connection to the socket server which may fail. e.g. request timeout. compare it to async Http request. You can await channel.ready to succeed before calling for a stream. In the answer I have called stream even if channel fails, so stream calls onError if connection didnt succeed, this helps me put error handling in one place.. I guess its better to not do so..
The point is channel connection and stream are different things. you can get a Sink or a Stream from the channel and they can have errors during their own functioning
@Jalmoud2, I think putting it this way, would help understand the behavior.
WebSocketChannel channel = WebSocketChannel.connect(Uri());
channel.ready.then((_) {
channel.stream.listen((event) {
print('Event from Stream: $event');
},onError: (e){
// handle stream error
},
onDone: (() {
// stream on done callback...
}),
cancelOnError: true
);
}).onError((error, stackTrace) {
print("WebsocketChannel was unable to establishconnection");
});
Thanks to you guys comments, I found what I was looking for and here it is:
EDIT: Added reconnect duration each time it failed, until a maximum of 60 seconds (import 'dart:math' as math;)... Reset the duration to 3 on connection succes.
// Flag to indicate whether a WebSocket connection attempt is currently in progress.
// This is used to prevent multiple simultaneous attempts to connect to the WebSocket,
// which could lead to unexpected behavior or resource issues.
bool websocketConnecting = false;
// Reconnect duration starts at 3 seconds
int reconnectDuration = 3;
// This function is responsible for establishing a connection to a WebSocket server.
void connectWebsockets() async {
// Check if a connection attempt is already in progress. If so, exit the function
// early to prevent another concurrent attempt. This is important for maintaining
// control over the connection process and avoiding unnecessary network traffic.
if (websocketConnecting) {
return;
}
// Set the flag to true, indicating that a connection attempt is now underway.
websocketConnecting = true;
log("Attempting to connect to WebSocket");
// Parse the WebSocket URL from a predefined constant. This URL is where the client
// will attempt to establish the WebSocket connection.
final wsUrl = Uri.parse(ApiConstants.WSOCKET);
// Connect to the WebSocket server at the specified URL.
// The pingInterval is set to send a "ping" message every 15 seconds to keep the
// connection alive and check its health. The connectTimeout is set to 20 seconds,
// meaning the connection attempt will time out if not established within this duration.
channel = IOWebSocketChannel.connect(
wsUrl,
pingInterval: Duration(seconds: 15),
connectTimeout: Duration(seconds: 20)
);
try {
// Await the ready property of the channel, which is a Future that completes when
// the WebSocket connection is successfully established. This ensures that the
// following code only runs after a successful connection.
await channel!.ready;
print('WebSocket channel is ready');
// Once the connection is established, set up the stream listeners to handle
// incoming messages, errors, and connection closures.
setupWebSocketListeners();
} catch (e) {
// If an error occurs during the connection attempt or while waiting for the
// connection to become ready, log the error and perform cleanup.
print('WebSocket connection failed or stream error occurred: $e');
// Set the channel to null to clean up and indicate that there is no active
// WebSocket connection.
channel = null;
// Reset the websocketConnecting flag to allow future connection attempts.
websocketConnecting = false;
// Retry connecting to the WebSocket. This creates resilience in the face of
// network issues or temporary server unavailability.
Future.delayed(Duration(seconds: reconnectDuration), () {
connectWebsockets();
// Increment reconnectDuration by 3, up to a maximum of 60 seconds
reconnectDuration = math.min(reconnectDuration + 3, 60);
print("reconnectDuration: $reconnectDuration");
});
}
}
Copied from https://github.com/flutter/flutter/issues/21076
IOWebSocketChannel.connect
suffers from the problem described here:https://www.dartlang.org/guides/libraries/futures-error-handling#potential-problem-failing-to-register-error-handlers-early
When I use
connect
with a URL that can't be resolved (e.g. by having my wifi turned off), then I get an exception that can't be caught bychannel.stream.handleError(onError)
.All exceptions should be handleable by
stream.handleError
, particularly as this is what the docs promise: "If there's an error connecting, the channel's stream emits a WebSocketChannelException wrapping that error and then closes."ref: https://docs.flutter.io/flutter/web_socket_channel.io/IOWebSocketChannel/IOWebSocketChannel.connect.html