Browse Source

handle ws timeout

Melon 2 years ago
parent
commit
4d8bdbcef9

+ 5 - 2
lib/client_base.dart

@@ -79,8 +79,11 @@ class JsonRpcClientBase {
 
   Future<dynamic> _postRequest(String package) async {
     try {
-      final httpClient = jrpcHttpPool.getClient(this.host,
-          timeout: this.timeout, headers: this.headers);
+      final httpClient = jrpcHttpPool.getClient(
+        this.host,
+        timeout: this.timeout,
+        headers: this.headers,
+      );
 
       var response =
           await httpClient.post('/${this.serviceName}', data: package);

+ 2 - 0
lib/notifications/center.dart

@@ -43,6 +43,8 @@ class WsNotificationCenter extends NotificationCenter {
 
   @override
   void stop() {
+    if (_connection == null) return;
+
     logger.i("Stop WsNotificationCenter");
     super.stop();
   }

+ 66 - 20
lib/notifications/connection.dart

@@ -14,8 +14,18 @@ class WsConnectionException implements Exception {
 
 /// WebScoket连接
 class WsConnection implements IConnection {
+  /// 错误断开重试次数
+  static const C_ERR_RETRY_LIMIT = 5;
+
+  /// 连接请求超时时间,10s
+  static const C_CONNECT_REQ_TIMEOUT = 10 * 1000;
+
   ConnectionStatus _status = ConnectionStatus.init;
 
+  int _errorRetryCount = 0;
+
+  Timer? _timer;
+
   /// 主机地址
   final String host;
 
@@ -50,45 +60,32 @@ class WsConnection implements IConnection {
     final uri = Uri.parse(host);
     _channel = WebSocketChannel.connect(uri);
     _updateStatus(ConnectionStatus.connecting);
-    void fn() async {
-      final result = await _checkIsConnected();
-      _updateStatus(
-        result ? ConnectionStatus.disconnected : ConnectionStatus.connected,
-      );
-    }
 
     _channel!.stream.listen(
       (data) {
         messageReceived.emit(this, data);
       },
       onError: (error) {
-        fn();
-        if (exceptionOccurred != null) {
-          exceptionOccurred!.emit(this, WsConnectionException(error));
-        }
+        _handleCancelOnError();
+        exceptionOccurred?.emit(this, WsConnectionException(error));
       },
+      cancelOnError: true,
     );
-    fn();
-    if (completer.isCompleted == false) {
-      completer.complete(true);
-    }
-    // const Future.delayed(const Duration(milliseconds: 100), () {
-    //   if (completer.isCompleted == false) {
-    //     completer.complete(result);
-    //   }
-    // });
+    _waitConnectResult(completer);
     return completer.future;
   }
 
   @override
   Future<bool> close() async {
+    _updateStatus(ConnectionStatus.disconnected);
     if (_channel != null) {
       if (_channel!.closeCode == null) {
         await _channel!.sink.close(statuses.normalClosure);
       }
       _channel = null;
     }
-    _updateStatus(ConnectionStatus.disconnected);
+    _timer?.cancel();
+    _timer = null;
     return true;
   }
 
@@ -107,4 +104,53 @@ class WsConnection implements IConnection {
       statusChanged?.emit(this, value);
     }
   }
+
+  /// 等待请求连接结果
+  void _waitConnectResult(Completer completer) {
+    const onceDuration = 100;
+    const limit = C_CONNECT_REQ_TIMEOUT / onceDuration;
+    int count = 0;
+    _timer = Timer.periodic(
+      const Duration(milliseconds: onceDuration),
+      (timer) async {
+        if (count >= limit) {
+          // 等待连接超时
+          _updateStatus(ConnectionStatus.disconnected);
+          exceptionOccurred?.emit(
+              this, WsConnectionException("WsConnectionTimeout"));
+          completer.complete(false);
+          timer.cancel();
+          _timer = null;
+          return;
+        }
+
+        final result = await _checkIsConnected();
+        if (result) {
+          if (!completer.isCompleted) {
+            _updateStatus(ConnectionStatus.connected);
+            completer.complete(true);
+            timer.cancel();
+            _timer = null;
+          }
+        }
+      },
+    );
+    //C_CONNECT_REQ_TIMEOUT
+  }
+
+  /// 处理因错误导致的连接中断
+  void _handleCancelOnError() {
+    if (!isConnected) return; // 非连接状态,不处理
+
+    _updateStatus(ConnectionStatus.disconnected);
+
+    if (_errorRetryCount >= C_ERR_RETRY_LIMIT) {
+      // 超过重试次数,不处理
+      exceptionOccurred?.emit(
+          this, WsConnectionException("OutOfErrorRetryLimit"));
+      return;
+    }
+    _errorRetryCount++;
+    connect();
+  }
 }

+ 76 - 72
lib/services/notificationdecoder.dart

@@ -1,109 +1,113 @@
 import 'notification.m.dart';
 
 typedef _ModelBuilder<T> = T Function(Map<String, dynamic> map);
+
 class NotificationDecoder {
-	NotificationDecoder._();
+  NotificationDecoder._();
 
-	static final _builders = _ModelBuilderCollection();
+  static final _builders = _ModelBuilderCollection();
 
-	static T decode<T>(Map<String, dynamic> map) {
-		final type = decodeType(map);
-		final builder = _builders.find(type);
-		final model = builder.call(map) as T;
-		return model;
-	}
-	static NotificationTypeEnum decodeType(Map<String, dynamic> map) {
-		final typeInt = map['notificationType'] as int;
-		return NotificationTypeEnum.values[typeInt];
-	}
+  static T decode<T>(Map<String, dynamic> map) {
+    final type = decodeType(map);
+    final builder = _builders.find(type);
+    final model = builder.call(map) as T;
+    return model;
+  }
 
-	static void setup() {
-		/** Register notification model builders here */
-		_builders.add(NotificationTypeEnum.ChatMsgNotification,
-				(map) => ChatMsgNotification.fromJson(map));
+  static NotificationTypeEnum decodeType(Map<String, dynamic> map) {
+    final typeInt = map['NotificationType'] as int;
+    return NotificationTypeEnum.values[typeInt];
+  }
 
-		_builders.add(NotificationTypeEnum.ConnectionNotification,
-				(map) => ConnectionNotification.fromJson(map));
+  static void setup() {
+    /** Register notification model builders here */
+    _builders.add(NotificationTypeEnum.ChatMsgNotification,
+        (map) => ChatMsgNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.DisconnectNotification,
-				(map) => DisconnectNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.ConnectionNotification,
+        (map) => ConnectionNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.ExamRecordsFinishedNotification,
-				(map) => ExamRecordsFinishedNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.DisconnectNotification,
+        (map) => DisconnectNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.PasswordExpiredWarningNotification,
-				(map) => PasswordExpiredWarningNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.ExamRecordsFinishedNotification,
+        (map) => ExamRecordsFinishedNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.TokenReplacedNotification,
-				(map) => TokenReplacedNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.PasswordExpiredWarningNotification,
+        (map) => PasswordExpiredWarningNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.ApplyConsultationNotification,
-				(map) => ApplyConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.TokenReplacedNotification,
+        (map) => TokenReplacedNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.ApprovalApplyConsultationNotification,
-				(map) => ApprovalApplyConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.ApplyConsultationNotification,
+        (map) => ApplyConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.ConsultationRemindNotification,
-				(map) => ConsultationRemindNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.ApprovalApplyConsultationNotification,
+        (map) => ApprovalApplyConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.InviteeApproveApplyConsultationNotification,
-				(map) => InviteeApproveApplyConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.ConsultationRemindNotification,
+        (map) => ConsultationRemindNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.InviteeConsultationNotification,
-				(map) => InviteeConsultationNotification.fromJson(map));
+    _builders.add(
+        NotificationTypeEnum.InviteeApproveApplyConsultationNotification,
+        (map) => InviteeApproveApplyConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.InviteeRejectApplyConsultationNotification,
-				(map) => InviteeRejectApplyConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.InviteeConsultationNotification,
+        (map) => InviteeConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.RejectApplyConsultationNotification,
-				(map) => RejectApplyConsultationNotification.fromJson(map));
+    _builders.add(
+        NotificationTypeEnum.InviteeRejectApplyConsultationNotification,
+        (map) => InviteeRejectApplyConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.AcceptLiveConsultationNotification,
-				(map) => AcceptLiveConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.RejectApplyConsultationNotification,
+        (map) => RejectApplyConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.CancelInvitingInLiveConsultationNotification,
-				(map) => CancelInvitingInLiveConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.AcceptLiveConsultationNotification,
+        (map) => AcceptLiveConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.CancelLiveConsultationNotification,
-				(map) => CancelLiveConsultationNotification.fromJson(map));
+    _builders.add(
+        NotificationTypeEnum.CancelInvitingInLiveConsultationNotification,
+        (map) => CancelInvitingInLiveConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.CloseLiveConsultationNotification,
-				(map) => CloseLiveConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.CancelLiveConsultationNotification,
+        (map) => CancelLiveConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.InviteLiveConsultationNotification,
-				(map) => InviteLiveConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.CloseLiveConsultationNotification,
+        (map) => CloseLiveConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.InviteInLiveConsultationNotification,
-				(map) => InviteInLiveConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.InviteLiveConsultationNotification,
+        (map) => InviteLiveConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.InviteLiveConsultationToDeviceNotification,
-				(map) => InviteLiveConsultationToDeviceNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.InviteInLiveConsultationNotification,
+        (map) => InviteInLiveConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.JoinLiveConsultationNotification,
-				(map) => JoinLiveConsultationNotification.fromJson(map));
+    _builders.add(
+        NotificationTypeEnum.InviteLiveConsultationToDeviceNotification,
+        (map) => InviteLiveConsultationToDeviceNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.LeaveConsultationNotification,
-				(map) => LeaveLiveConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.JoinLiveConsultationNotification,
+        (map) => JoinLiveConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.NetworkErrConsultationNotification,
-				(map) => NetworkErrConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.LeaveConsultationNotification,
+        (map) => LeaveLiveConsultationNotification.fromJson(map));
 
-		_builders.add(NotificationTypeEnum.RejectLiveConsultationNotification,
-				(map) => RejectLiveConsultationNotification.fromJson(map));
+    _builders.add(NotificationTypeEnum.NetworkErrConsultationNotification,
+        (map) => NetworkErrConsultationNotification.fromJson(map));
 
-	}
+    _builders.add(NotificationTypeEnum.RejectLiveConsultationNotification,
+        (map) => RejectLiveConsultationNotification.fromJson(map));
+  }
 }
 
 class _ModelBuilderCollection {
-	final Map<NotificationTypeEnum, _ModelBuilder> _source = {};
+  final Map<NotificationTypeEnum, _ModelBuilder> _source = {};
 
-	void add(NotificationTypeEnum type, _ModelBuilder builder) {
-		_source[type] = builder;
-	}
+  void add(NotificationTypeEnum type, _ModelBuilder builder) {
+    _source[type] = builder;
+  }
 
-	_ModelBuilder find(NotificationTypeEnum type) {
-		final builder = _source[type];
-		return builder!;
-	}
+  _ModelBuilder find(NotificationTypeEnum type) {
+    final builder = _source[type];
+    return builder!;
+  }
 }
-