我正在使用gRPC开发Flutter应用程序,并且一切正常,直到我决定看看如果没有Internet连接会发生什么情况。
这样做并发出请求后,出现以下错误:
E / flutter(26480):gRPC错误(14,调用时出错:状态错误:http / 2连接不再有效,因此不能用于创建新的流。)
问题在于,即使重新启用连接后,仍然会发生错误。
我必须重新创建clientChannel吗?
const String serverUrl = 'theaddress.com';
const int serverPort = 50051;
final ClientChannel defaultClientChannel = ClientChannel(
serverUrl,
port: serverPort,
options: const ChannelOptions(
credentials: const ChannelCredentials.insecure(),
),
);
我只是想抛出一些错误,但是一旦互联网连接恢复正常,就可以正常工作。
答案 0 :(得分:2)
我想你是少数尝试它的人之一。
GRPC连接需要花费一些时间来创建新的连接,不仅是dart,而且是所有其他语言。如果需要,可以将捕获侦听器放在错误代码14上,然后手动终止连接并重新连接。还有idleTimeout
个频道选项可能会对您有所帮助,默认选项是在grpc-dart中为5分钟
有一个针对未解决的崩溃问题https://github.com/grpc/grpc-dart/issues/131的修复程序,因此请尝试更新依赖项(grpc-dart
)以防止崩溃,但是网络上的重新连接问题仍然存在。
此修复程序之后,崩溃已经停止,但是对于我来说,过时的连接问题仍然存在。我求助于显示小吃店,上面写着“无法连接服务器,请过几分钟再试”。
答案 1 :(得分:2)
基于@Ishaan的建议,我使用了Connectivity程序包创建了一个客户端,该客户端在互联网备份后会重新连接。到目前为止,它似乎一直有效。
import 'dart:async';
import 'package:connectivity/connectivity.dart';
import 'package:flutter_worker_app/generated/api.pbgrpc.dart';
import 'package:grpc/grpc.dart';
import 'package:rxdart/rxdart.dart';
class ConnectiveClient extends ApiClient {
final CallOptions _options;
final Connectivity _connectivity;
ClientChannel _channel;
bool hasRecentlyFailed = false;
ConnectiveClient(this._connectivity, this._channel, {CallOptions options})
: _options = options ?? CallOptions(),
super(_channel) {
//TODO: Cancel connectivity subscription
_connectivity.onConnectivityChanged.listen((result) {
if (hasRecentlyFailed && result != ConnectivityResult.none) {
_restoreChannel();
}
});
}
///Create new channel from original channel
_restoreChannel() {
_channel = ClientChannel(_channel.host,
port: _channel.port, options: _channel.options);
hasRecentlyFailed = false;
}
@override
ClientCall<Q, R> $createCall<Q, R>(
ClientMethod<Q, R> method, Stream<Q> requests,
{CallOptions options}) {
//create call
BroadcastCall<Q, R> call = createChannelCall(
method,
requests,
_options.mergedWith(options),
);
//listen if there was an error
call.response.listen((_) {}, onError: (Object error) async {
//Cannot connect - we assume it's internet problem
if (error is GrpcError && error.code == StatusCode.unavailable) {
//check connection
_connectivity.checkConnectivity().then((result) {
if (result != ConnectivityResult.none) {
_restoreChannel();
}
});
hasRecentlyFailed = true;
}
});
//return original call
return call;
}
/// Initiates a new RPC on this connection.
/// This is copy of [ClientChannel.createCall]
/// The only difference is that it creates [BroadcastCall] instead of [ClientCall]
ClientCall<Q, R> createChannelCall<Q, R>(
ClientMethod<Q, R> method, Stream<Q> requests, CallOptions options) {
final call = new BroadcastCall(method, requests, options);
_channel.getConnection().then((connection) {
if (call.isCancelled) return;
connection.dispatchCall(call);
}, onError: call.onConnectionError);
return call;
}
}
///A ClientCall that can be listened multiple times
class BroadcastCall<Q, R> extends ClientCall<Q, R> {
///I wanted to use super.response.asBroadcastStream(), but it didn't work.
///I don't know why...
BehaviorSubject<R> subject = BehaviorSubject<R>();
BroadcastCall(
ClientMethod<Q, R> method, Stream<Q> requests, CallOptions options)
: super(method, requests, options) {
super.response.listen(
(data) => subject.add(data),
onError: (error) => subject.addError(error),
onDone: () => subject.close(),
);
}
@override
Stream<R> get response => subject.stream;
}
答案 2 :(得分:1)
直到今天我一直没有使用gRPC。
由于我花了时间尝试模拟此错误,因此将答案发布在这里,但是我所有的intel都是我赞成的 @ishann 答案,这应该是接受了。
我刚刚尝试过dart hello world example。
我的计算机上正在运行server
,而Flutter应用程序中是client
。
当我不运行服务器时,出现错误
gRPC Error (14, Error connecting: SocketException:
但是一旦服务器启动,所有设备都将按预期方式工作,但是后来我意识到我每次都在重新创建通道,所以这不是OP方案。
那是我的拳头Flutter代码:
void _foo() async {
final channel = new ClientChannel('192.168.xxx.xxx',
port: 50051,
options: const ChannelOptions(
credentials: const ChannelCredentials.insecure()));
final stub = new GreeterClient(channel);
final name = 'world';
var _waitHelloMessage = true;
while (_waitHelloMessage) {
try {
final response = await stub.sayHello(new HelloRequest()..name = name);
print('Greeter client received: ${response.message}');
_waitHelloMessage = false;
} catch (e) {
print('Caught error: $e');
sleep(Duration(seconds: 1));
}
}
print('exiting');
await channel.shutdown();
}
如果将设备置于 airplain模式,然后又切换回正常的wifi / lte连接,则行为相同。
在另一个游乐场项目中,我还是转载了
Caught error: gRPC Error (14, Error making call: Bad state: The http/2 connection is no longer active and can therefore not be used to make new streams.)
在不重新创建频道的情况下您将无法出现,并且
Caught error: gRPC Error (14, Error connecting: SocketException: OS Error: Connection refused, errno = 111, address = 192.168.1.58, port = 38120)
(例如关闭服务器),您可以从中重新启动而无需重新创建频道。
以前的错误代码不太容易获得,因为似乎wifi和LTE连接之间的通道限制。
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_test_grpc/grpc/generated/helloworld.pbgrpc.dart';
import 'package:grpc/grpc.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
ClientChannel _channel;
@override
void dispose() {
_shutdown();
super.dispose();
}
void _shutdown() async {
if (null != _channel) {
print('shutting down...');
await _channel.shutdown();
print('shut down');
_channel = null;
} else {
print ('connect first');
}
}
void _connect() {
print('connecting...');
_channel = new ClientChannel('192.168.xxx.xxx',
port: 50051,
options: const ChannelOptions(
credentials: const ChannelCredentials.insecure()));
print('connected');
}
void _sayHello() async {
if (_channel != null) {
final stub = new GreeterClient(_channel);
final name = 'world';
try {
final response = await stub.sayHello(new HelloRequest()..name = name);
print('Greeter client received: ${response.message}');
} catch (e) {
print('Caught error: $e');
//sleep(Duration(seconds: 2));
}
//print('exiting');
//await channel.shutdown();
} else {
print('connect first!');
}
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: Padding(
padding: const EdgeInsets.only(left: 36.0),
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: _connect,
tooltip: 'Increment',
child: Icon(Icons.wifi),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: _sayHello,
tooltip: 'Increment',
child: Icon(Icons.send),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton(
onPressed: _shutdown,
tooltip: 'Increment',
child: Icon(Icons.close),
),
),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
如果可以的话,那就是我的flutter doctor -v
:
$ flutter doctor -v
[✓] Flutter (Channel beta, v1.0.0, on Mac OS X 10.14.1 18B75, locale en-IT)
• Flutter version 1.0.0 at /Users/shadowsheep/flutter/flutter
• Framework revision 5391447fae (6 weeks ago), 2018-11-29 19:41:26 -0800
• Engine revision 7375a0f414
• Dart version 2.1.0 (build 2.1.0-dev.9.4 f9ebf21297)
[✓] Android toolchain - develop for Android devices (Android SDK 28.0.3)
• Android SDK at /Users/shadowsheep/Library/Android/sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-28, build-tools 28.0.3
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1248-b01)
• All Android licenses accepted.
[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 10.1, Build version 10B61
• ios-deploy 1.9.4
• CocoaPods version 1.5.3
[✓] Android Studio (version 3.3)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin version 31.3.3
• Dart plugin version 182.5124
• Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1248-b01)
[✓] VS Code (version 1.30.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 2.21.1
[✓] Connected device (1 available)
[...]
• No issues found!