Open nailgilaziev opened 5 years ago
Any progress on this? 👀
What i was doing is create a native socket and wrap it in IOWebSocketChannel
Like this
WebSocket.connect(_SERVER_ADDRESS)
.timeout(Duration(seconds: 20))
.then((ws) {
try {
_channel = new IOWebSocketChannel(ws);
} catch (e) {
print(
'Error happened when opening a new websocket connection. ${e.toString()}');
}
@kavinda1995 I got an error. The WebSocket that comes from connect is from flutter/bin/cache/pkg/sky_engine/lib/_http/websocket.dart
while the IOWebSocketChannel requires a WebSocket from flutter/bin/cache/pkg/sky_engine/lib/html/html_dart2js.dart
.
@zeidReg - No. You must import WebSocket from dart:io. Not from any other package
@kavinda1995 I don't really get it. I imported it from dart:io but when i press Ctrl + click on the class name i go to the file flutter/bin/cache/pkg/sky_engine/lib/_http/websocket.dart
@ziedReg please provide your code snippet here. So we can elaborate :smiley:
These are the imports
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:web_socket_channel/html.dart';
import 'package:my_project/constants/variables.dart';
import 'package:web_socket_channel/io.dart';
this is the method where i connect to the socket
void fetchImages() {
if (channel == null) {
// copying your code
WebSocket.connect("ws://"+settings.host+":"+settings.port.toString()).timeout(Duration(seconds: 5)).then((ws) {
try {
channel = new HtmlWebSocketChannel(ws); // there is an error at "ws"
} catch (e) {
print('Error happened when opening a new websocket connection. ${e.toString()}');
}
});
// what i'm working with right now
channel = HtmlWebSocketChannel.connect("ws://"+settings.host+":"+settings.port.toString());
}
channel.stream.listen((message) async{
bytes = base64Decode(message);
image = Image.memory(bytes);
await precacheImage(image.image, context);
setState(() {});
});
}
The error is : The argument type 'WebSocket (where WebSocket is defined in ~/flutter/bin/cache/pkg/sky_engine/lib/_http/websocket.dart)' can't be assigned to the parameter type 'WebSocket (where WebSocket is defined in ~/flutter/bin/cache/pkg/sky_engine/lib/html/html_dart2js.dart)'. (argument_type_not_assignable at [my_project] lib/views/control/control.dart:44)
I am using HtmlWebSocketChannel
because i'm working on web but i tried IOWebSocketChannel
and got the same error
Oh 😞. Couldn't see any wrong in this
@ziedReg I'm probably too late, but the issue here is that you are using HtmlWebSocketChannel (for web) while @kavinda1995 is using IOWebSocketChannel (for mobile). As noted, the WebSocket class in use is from the dart:io package, which cannot be used for web development. The two websocket implementations are not interchangeable.
So there really isn't a good way to do this, particularly since the dart:html package only offers a constructor that doesn't return a future
What i was doing is create a native socket and wrap it in IOWebSocketChannel
Like this
WebSocket.connect(_SERVER_ADDRESS) .timeout(Duration(seconds: 20)) .then((ws) { try { _channel = new IOWebSocketChannel(ws); } catch (e) { print( 'Error happened when opening a new websocket connection. ${e.toString()}'); }
@kavinda1995 is your approach safe? Consider the following scenario:
1- By running WebSocket.connect
a web socket connection will be established but due to network issues, it can be established after 30 seconds
2- timeout(Duration(seconds: 20))
will completeError
the feature with TimeoutException
after 20 seconds
3- We have a reconnect logic in our code in the catch
and do same thing for connection
4- By running WebSocket.connect
again, a new web socket connection will be established without timeout
5- after 30 seconds, the first connection is established, So we have 2 web socked connection, and there is no way to close the first
@sm2017 is correct. This can lead to resource leaks. The timeout passed to the WebSocket
is for the communication Stream
, not the establishment of the WebSocket
.
It seems to me that a viable solution could be to pass a connectionTimeout
parameter to the WebSocket
constructor which would be passed on to the HttpClient
when establishing a connection. This could also trickle up to IOWebSocketChannel.connect
making establishing web sockets much easier/safer. Unless there is a reason unbeknown to me why this is should not be.
Currently, I believe the safest way to handle timeouts when establishing a web socket in Dart is to use an HttpClient
and manually upgrade to a WebSocket
. Then you can create the IOWebSocketChannel
with the WebSocket
.
final r = Random();
final key = base64.encode(List<int>.generate(8, (_) => r.nextInt(255)));
final client = HttpClient();
client
.getUrl(Uri.parse(SERVER_ADDRESS))
.timeout(Duration(seconds: 20))
.then((request) {
request.headers.add('Connection', 'upgrade');
request.headers.add('Upgrade', 'websocket');
request.headers.add('sec-websocket-version', '13');
request.headers.add('sec-websocket-key', key);
request.close().then((response) {
response.detachSocket().then((socket) {
final webSocket = WebSocket.fromUpgradedSocket(socket, serverSide: false);
_channel = IOWebSocketChannel(webSocket);
});
});
}).catchError((error) {
// handle error
return null;
});
Thanks, works great! @theLee3 Any solution for Web though?
@julien100000 For the web, we can simply check the status of the WebSocket
after the desired timeout period, and if the connection has not been established, close the socket and handle the timeout according to your needs.
// WebSocket class from `html` library
final webSocket = WebSocket(SERVER_ADDRESS);
Future.delayed(Duration(seconds: 20), () {
if (webSocket.readyState == WebSocket.CONNECTING) {
webSocket.close();
// handle the timeout here
}
});
_channel = HtmlWebSocketChannel(webSocket);
I think that I found not ideal but technically correct solution:
final httpClient = HttpClient();
httpClient.connectionTimeout = Duration(seconds: 20);
final webSocket = await WebSocket.connect(
'some url',
customClient: httpClient,
);
final channel = IOWebSocketChannel(webSocket);
@ycherniavskyi This is a good solution. I would also suggest setting httpClient.idleTimeout
and/or webSocket.pingInterval
according to your needs.
I think that I found not ideal but technically correct solution:
final httpClient = HttpClient(); httpClient.connectionTimeout = Duration(seconds: 20); final webSocket = await WebSocket.connect( 'some url', customClient: httpClient, ); final channel = IOWebSocketChannel(webSocket);
This is a viable workaround, but dart:io
is not allowed in web
Maybe something has changed since this was originally posted, but you can set a timeout like this:
WebSocketChannel webSocket = WebSocketChannel.connect(Uri.parse(url));
await webSocket.ready.timeout(Duration(seconds: 10));
It then throws a TimeoutException
On mobile connection quality can vary depending on the environment conditions. if connection is bad for the moment sometimes connecting to a websocket servers can't be established now, and in this situation ConnectionTimeOut exception must thrown. Actually this exception throws:
WebSocketChannelException: WebSocketChannelException: SocketException: OS Error: Connection timed out, errno = 110, address = 192.168.2.2, port = 56472
But it take around a 2min before happen.This is too long for creating a realtime applications with websockets, and sometimes we need to fail faster.
Additionally, this 2 min behaviour is annoying because of issue #21 that closed because issue #25 and https://github.com/flutter/flutter/issues/18204 created. (connecting and already connected states still not differentiable)
How this connection timeout can be configured in this package?
One year ago in issue #22 nex3 answered me, that dart io implementation doesn't expose this API. But it is a superficial answer, because: 1) HttpClient has this property https://api.dartlang.org/stable/2.4.0/dart-io/HttpClient/connectionTimeout.html
2) if we use dart.io or html implementation directly we can use
.timeout
function on futures:But when using this package I can't manipulate with connection timeout. If connection doesn't establish in acceptable time I can't fail gracefully.