Flutter websocket自动重新连接到颤振&;河豚?
1。目标 我希望在遇到网络问题或WebSocket服务器遇到问题时,能够自动重新建立我的自定义WebSocket服务器(API)和我的Flatter应用程序之间的连接Flutter websocket自动重新连接到颤振&;河豚?,flutter,dart,websocket,state-management,riverpod,Flutter,Dart,Websocket,State Management,Riverpod,1。目标 我希望在遇到网络问题或WebSocket服务器遇到问题时,能够自动重新建立我的自定义WebSocket服务器(API)和我的Flatter应用程序之间的连接 用例1:wifi停止并突然恢复 用例2:API没有启动,而是突然重新启动 约束:我使用Riverpod作为状态管理库(我想保留它:)。 我之所以强调状态管理库,是因为我在StreamProvider中创建了WS连接(参见Riverpod) 2。没有自动重新连接的初始设置 我创建了一个StreamProvider,如下所示:
- 用例1:wifi停止并突然恢复
- 用例2:API没有启动,而是突然重新启动
- 约束:我使用Riverpod作为状态管理库(我想保留它:)。 我之所以强调状态管理库,是因为我在StreamProvider中创建了WS连接(参见Riverpod)
- 我创建了一个StreamProvider,如下所示:
最终主机提供程序=
StreamProvider.autoDispose.family((ref,ip)异步*{
//打开连接
connectWs('ws://$ip:$port/v1/path')。然后((值)异步*{
最终通道=IOWebSocketChannel(值);
参考onDispose(){
返回channel.sink.close();
});
等待(channel.stream中的最终json){
最终jsonStr=jsonDecode(json作为字符串);
产生Host.fromJson(jsonStr作为映射);
}
});
});
未来的connectWs(字符串路径)异步{
试一试{
返回等待WebSocket.connect(路径);
}捕获(e){
打印(“错误!”+e.toString());
等待未来。延迟(持续时间(毫秒:2000));
返回等待连接(路径);
}
}
//在hostProvider中使用
参考容器刷新(连接提供程序(ip))
最终连接提供程序=
提供者。家庭((参考,ip){
//插座打开
返回IOWebSocketChannel.connect('ws://$ip:$port/v1/path');
});
提前感谢您的帮助。我是riverpod的初学者,但在我看来,您希望使用更高级别的redux/bloc样式流在每次失败时重新创建提供程序 当连接成功时,这个更高级别的bloc会创建提供程序,当连接失败时,您会向bloc发送一个事件,告诉它重新连接并重新创建提供程序 这是我的想法,但再说一次,我是这个软件包的初学者。谢谢,@Dewey 最后,我找到了一个适用于我的用例的变通方法: 我的提供商:channelProvider和streamProvider
static final channelProvider = Provider.autoDispose
.family<IOWebSocketChannel, HttpParam>((ref, httpParam) {
log.i('channelProvider | Metrics - $httpParam');
return IOWebSocketChannel.connect(
'ws://${httpParam.ip}:$port/v1/${httpParam.path}');
});
static final streamProvider =
StreamProvider.autoDispose.family<dynamic, HttpParam>((ref, httpParam) {
log.i('streamProvider | Metrics - $httpParam');
log.i('streamProvider | Metrics - socket ${httpParam.path} opened');
var bStream = ref
.watch(channelProvider(httpParam))
.stream
.asBroadcastStream(onCancel: (sub) => sub.cancel());
var isSubControlError = false;
final sub = bStream.listen(
(data) {
ref
.watch(channelProvider(httpParam))
.sink
?.add('> sink add ${httpParam.path}');
},
onError: (_, stack) => null,
onDone: () async {
isSubControlError = true;
await Future.delayed(Duration(seconds: 10));
ref.container.refresh(channelProvider(httpParam));
},
);
ref.onDispose(() {
log.i('streamProvider | Metrics - socket ${httpParam.path} closed');
sub.cancel();
if (isSubControlError == false)
ref.watch(channelProvider(httpParam)).sink?.close(1001);
bStream = null;
});
return bStream;
});
useProvider(hostProvider(ip)).when(
data: (data) => show the result
loading: () => show progress bar
error: (error, _) => show error
);
final hostProvider =
StreamProvider.autoDispose.family<Host, String>((ref, ip) async* {
// Open the connection
connectWs('ws://$ip:$port/v1/path').then((value) async* {
final channel = IOWebSocketChannel(value);
ref.onDispose(() {
return channel.sink.close();
});
await for (final json in channel.stream) {
final jsonStr = jsonDecode(json as String);
yield Host.fromJson(jsonStr as Map<String, dynamic>);
}
});
});
Future<WebSocket> connectWs(String path) async {
try {
return await WebSocket.connect(path);
} catch (e) {
print("Error! " + e.toString());
await Future.delayed(Duration(milliseconds: 2000));
return await connectWs(path);
}
}
// used in hostProvider
ref.container.refresh(connectProvider(ip))
final connectProvider =
Provider.family<Host, String>((ref, ip) {
//SOCKET OPEN
return IOWebSocketChannel.connect('ws://$ip:$port/v1/path');
});
static final channelProvider = Provider.autoDispose
.family<IOWebSocketChannel, HttpParam>((ref, httpParam) {
log.i('channelProvider | Metrics - $httpParam');
return IOWebSocketChannel.connect(
'ws://${httpParam.ip}:$port/v1/${httpParam.path}');
});
static final streamProvider =
StreamProvider.autoDispose.family<dynamic, HttpParam>((ref, httpParam) {
log.i('streamProvider | Metrics - $httpParam');
log.i('streamProvider | Metrics - socket ${httpParam.path} opened');
var bStream = ref
.watch(channelProvider(httpParam))
.stream
.asBroadcastStream(onCancel: (sub) => sub.cancel());
var isSubControlError = false;
final sub = bStream.listen(
(data) {
ref
.watch(channelProvider(httpParam))
.sink
?.add('> sink add ${httpParam.path}');
},
onError: (_, stack) => null,
onDone: () async {
isSubControlError = true;
await Future.delayed(Duration(seconds: 10));
ref.container.refresh(channelProvider(httpParam));
},
);
ref.onDispose(() {
log.i('streamProvider | Metrics - socket ${httpParam.path} closed');
sub.cancel();
if (isSubControlError == false)
ref.watch(channelProvider(httpParam)).sink?.close(1001);
bStream = null;
});
return bStream;
});
return useProvider(MetricsWsRepository.streamProvider(HttpParam(
ip: ip,
path: 'dummy-path',
))).when(
data: (data) => deserialize & doSomething1,
loading:() => doSomething2,
error: (_, stack) => doSomething3
)