Open gradddev opened 6 years ago
An event is also needed for each state.
This is needed to handle reconnects.
With this package I have no idea how to: 1) Handle reconnects 2) Know the state of the connection.
@dionjwa What package do you use now?
@AlexeySemigradsky This is my first foray into using flutter. I want to write a cross-platform app, where the core requirement is a persistent websocket connection. It doesn't look like flutter is ready to support this as yet.
@nex3 So what about this feature? Should I wait for its implementation?
I started working on the Socket.IO plugin for the Flutter instead of using web_socket_channel
.
Because WebSocketChannel
needs to work on both the Dart VM and in the browser, the best we can do is expose the subset of APIs supported on both platforms. We can (and probably should) expose a readyState
getter, but because dart:io
's WebSocket
class doesn't provide any events when that state changes there's no way for this package to support them either.
I don't understand how you're supposed to use a websocket package that is meant for mobile devices that has no way to handle disconnects (so you can reconnect). That's not something that you would ever expose in a production ready product. One disconnect because you go under a bridge, and that's it?
The stream will emit a done event when the socket has disconnected--is that not enough?
@nex3 Somehow that wasn't clear. Yes, that would definitely be sufficient. Thank you!
@nex3 , when server interface is going away stream didn't emit a done event! and this is a bug.
@nailgilaziev that sounds like a bug with dart:io
's implementation. I suggest finding a reproduction that just uses dart:io
's WebSocket
class and filing a bug against the dart-lang/sdk repo.
I think that is a lib bug(lack of implementation), because I rewrite my code to use pure dart:io If I use this
done → Future
Return a future which is completed when the StreamSink is finished.
read-only, inherited
https://api.dartlang.org/stable/1.24.3/dart-io/WebSocket-class.html
then error event exist
Can you produce a minimal reproduction, including server code, where dart:io
emits a done event but this package does not?
My server code running on kotlin - ktor. repo But is does not matter. You can use public ehho server. ws://echo.websocket.org What matter is that you need emulate bad network condition. For this I use Network Link Conditioner with 100% loss profile.
Full code listings: 1) channel_example code 2) dart io example code
channel_example code is a tutorial example code from cookbook but with two difference:
1) added pingInterval to connect method pingInterval: Duration(seconds: 10)
2) print lines for debugging in StreamBuilder
dart io example code contains with handy reconnect functionality and other debug stuffs, but main thing is next:
ws.done.then((v) {
setState(() {
ex = new Exception("Connection is done with v=$v");
timeprint("DONE");
});
});
This lambda called when connection is down. Not a lambda for onDone
parameter for `ws.listen()' function.
What the behaviour when we see the difference between channel package and dart:io code: 1) Firstly, we connected to ws server and try to send and receive messages - both works fine. 2) Then we switch on Network Link Conditioner tool with 100% loss profile. 3) wait
And here the difference come. Because we specife in both code that we use pingInterval
after around 20sec dart:io code call done
lambda. But channel example don't call anything, no matter how you wait.
Hi, I am new to websocket and there are really not much flutter websocket articles online. Can anyone please show me how to do reconnection in flutter when the connection is lost somehow (for example, the server machine restarts). When I search the keywords flutter websocket reconnect
, I find nothing related in google except this issue. Thank you very much.
Any recent updates on this request? It is something really required in case you are going after mobile app on production - see #40 as well...
In my case, I do not receive 'onDone' message or anything for that matter if the mobile loses data connection. Will it be possible for me to ask the IOWebSocketChannel if it is still connected every 5 seconds or so? I imagine it will know if it is following WebSocket protocol because it won't be receiving frames. Thanks!
Is possible to reconnect after the websocket is die?
@Allan-Nava; wrap connection initialization in a function and call it every time the onDone event is triggered
@Allan-Nava; wrap connection initialization in a function and call it every time the onDone event is triggered
Can you please give me an example, thanks ❤️
@Allan-Nava I do something like this
WebSocket socket;
void _onDisconnected() {
print('Disconnected');
if (socket != null) {
socket.close();
}
socket = null;
_tryConnect();
}
void _tryConnect() {
WebSocket.connect(url).then((ws) {
socekt = ws;
socket.listen(
(dynamic message) {
print(message)
},
onDone: _onDisconnected,
onError: (dynamic error) => _onDisconnected(),
);
socket.done.then((dynamic _) => _onDisconnected());
});
}
@Allan-Nava I do something like this
WebSocket socket; void _onDisconnected() { print('Disconnected'); if (socket != null) { socket.close(); } socket = null; _tryConnect(); } void _tryConnect() { WebSocket.connect(url).then((ws) { socekt = ws; socket.listen( (dynamic message) { print(message) }, onDone: _onDisconnected, onError: (dynamic error) => _onDisconnected(), ); socket.done.then((dynamic _) => _onDisconnected()); }); }
Thanks
I'm using this solution and works for me. https://gist.github.com/pyzenberg/4037e11627a8cac1c442183cc7cf172a
Any news on this? I implemented a WebSocket sub protocol (WAMP) with dart (+flutter) and it seems impossible to handle reconnects in a nice way. I'll end up to hack something in combination with custom Ping/Pongs and Bad state: No element
errors to make this work with WebSockets:io and Sockets:io.
WebSockets:html works as expected as this is the browser implementation.
The issues I experience (so far) are:
Hi everyone, I got a workaround. And it works (Did not test is very hard). I am using flutter package: https://pub.dev/packages/connectivity
So with this package, I can listen to connection state changes like this :
StreamSubscription<ConnectivityResult> _connectivitySubscription;
_connectivitySubscription = Connectivity()
.onConnectivityChanged.listen((ConnectivityResult result) {
if (result == ConnectivityResult.wifi ||
result == ConnectivityResult.mobile) {
_tryConnect();
}
});
and then as above mentioned _tryConnect Function :
void _tryConnect() {
final channel = IOWebSocketChannel.connect(
"url");
channel.sink.add("connected");
channel.stream.listen(
(message) {
print(message);
},
onError: (dynamic error) => print(error),
);
}
In this way, if connection drops then the _connectivitySubscription will change to none and when back online which will result in wifi or mobile. it will call _tryConnect() and connect. Open to suggestions. (Peace)
I make GUI app for aria2, same problem, when I read this: https://www.didierboelens.com/2018/06/web-sockets-build-a-real-time-game/
so my way is, Singleton handler, check connection befor send message.it works fine
import 'package:flutter/foundation.dart';
import 'package:web_socket_channel/io.dart';
WebSocketsHandler sockets = new WebSocketsHandler();
const String _SERVER_ADDRESS = "ws://127.0.0.1:6800/jsonrpc";
class WebSocketsHandler {
static final WebSocketsHandler _sockets = new WebSocketsHandler._internal();
factory WebSocketsHandler() => _sockets;
WebSocketsHandler._internal();
IOWebSocketChannel _channel;
bool _disconnected = true;
int _times = 0;
ObserverList<Function> _listeners = new ObserverList<Function>();
initCommunication() async {
reset();
try {
_times += 1;
debugPrint("try to connect $_times time(s)");
_channel = new IOWebSocketChannel.connect(_SERVER_ADDRESS);
_disconnected = false;
_channel.stream.listen(
_onReceptionOfMessage,
onError:(error) {/* debugPrint(error); */},
onDone:(){_disconnected = true;});
} catch (e) {/** */}
}
reset() {
if (_channel != null) {
if (_channel.sink != null) {
_channel.sink.close();
_disconnected = true;
}
}
}
send(String message) {
///check connection before send message
if (_disconnected) {
initCommunication();
}
if (_channel != null) {
if (_channel.sink != null && _disconnected == false) {
_channel.sink.add(message);
}
}
}
addListener(Function callback) {
_listeners.add(callback);
}
removeListener(Function callback) {
_listeners.remove(callback);
}
_onReceptionOfMessage(message) {
_disconnected = false;
_listeners.forEach((Function callback) {
callback(message);
});
}
}
People could use the WAMP protocol with my dart client to work with web sockets / sockets to deliver messages. I have implemented all that is needed to handle the states.
@Allan-Nava I do something like this
WebSocket socket; void _onDisconnected() { print('Disconnected'); if (socket != null) { socket.close(); } socket = null; _tryConnect(); } void _tryConnect() { WebSocket.connect(url).then((ws) { socekt = ws; socket.listen( (dynamic message) { print(message) }, onDone: _onDisconnected, onError: (dynamic error) => _onDisconnected(), ); socket.done.then((dynamic _) => _onDisconnected()); }); }
I've recently tried doing something similar. My situation is a little different though - I need to know when a particular machine is turned off, in this case there does not seem to be anything emitted in any particular way (neither the socket or the stream emit onDone or onError) until I turn the machine back on, then the connection is reset and errors happen.
I need to know if the TCP socket is still live without sending a command over the socket to get the machine to respond.
My clients randomly disconnects and i need to find a solution for this.
@Lyuzonggui I'm not sure I follow. I'm using websockets with flutter web, and it works. But I'm seeing the connection drop I think some times if I close my laptop or minimize chrome. Haven't really characterized fully. But I would love a way to check if the stream is there in the flutter web periodically and reconnect if down. I see your reset() function but I don't see any way this checks if it's down?
reset() {
if (_channel != null) {
if (_channel.sink != null) {
_channel.sink.close();
_disconnected = true;
}
}
}
Is checking for sink being null maybe a check for a connection being gone?
2022 and there is no solid way yet ?!)
2022 and there is no solid way yet ?!)
@faganchalabizada seems like this is of very low priority. I guess the problem here is to implement this for all plattforms
I am still having the same issue. catching the connect function is not working. I want to catch the error. For example "Connection failed"
@hogdamian You can implement auto reconnect by modifying the code of @Lyuzonggui slightly because the send
method call initCommunication()
method, this would mostly fail if the send
method is invoked frequently causing a lot of socket recreations.
import 'package:flutter/foundation.dart';
import 'package:web_socket_channel/io.dart';
WebSocketsHandler sockets = new WebSocketsHandler();
const String _SERVER_ADDRESS = "ws://127.0.0.1:6800/jsonrpc";
class WebSocketsHandler {
static final WebSocketsHandler _sockets = new WebSocketsHandler._internal();
factory WebSocketsHandler() => _sockets;
WebSocketsHandler._internal();
IOWebSocketChannel _channel;
bool _disconnected = true;
int _times = 0;
ObserverList<Function> _listeners = new ObserverList<Function>();
initCommunication() async {
reset();
try {
_times += 1;
debugPrint("try to connect $_times time(s)");
_channel = new IOWebSocketChannel.connect(_SERVER_ADDRESS);
_disconnected = false;
_channel.stream.listen(
_onReceptionOfMessage,
onError:(error) {/* debugPrint(error); */},
onDone: () {
debugPrint("Receive done event.");
_disconnected = true;
// add delay here
Future.delayed(const Duration(seconds: 1))
.then((value) => initCommunication());
});
} catch (e) {/** */}
}
reset() {
if (_channel != null) {
if (_channel.sink != null) {
_channel.sink.close();
_disconnected = true;
}
}
}
List<String> failedKeys = [];
send(String message) {
///check connection before send message
if (_disconnected) {
// add to list here
failedKeys.add(message);
}
if (_channel != null) {
if (_channel.sink != null && _disconnected == false) {
_channel.sink.add(message);
}
// add them later
}
}
addListener(Function callback) {
_listeners.add(callback);
}
removeListener(Function callback) {
_listeners.remove(callback);
}
_onReceptionOfMessage(message) {
_disconnected = false;
_listeners.forEach((Function callback) {
callback(message);
});
}
}
And create a timer that will fetch the missing keys:
Timer.periodic(const Duration(seconds: 5), (timer) {
if (failedKeys.isEmpty) return;
if (kDebugMode) print("Update failed key ${failedKeys.length}");
for (var entry in failedKeys) {
channel!.sink.add(entry.websocketData);
}
Just call the initCommunication method in the main.dart method.
Regards,
William
IOWebSocketChannel - innerWebSocket - readyState
/// Possible states of the connection. static const int connecting = 0; static const int open = 1; static const int closing = 2; static const int closed = 3;
@nex3 Are there any plans to ever implement the readyState? It is clearly stated that several people would need this, and I couldn't identify any problem which would block this from being implemented.
Would a pull request help?
Sorry, I'm no longer on the Dart team and no longer a maintainer of this package.
2024 and there is no solid way yet ?!)
I wish the close reason worked as intended... Even when the websocket is closed the close reason remains null
What do you mean by "the state", exactly?