connection.dart 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import 'dart:async';
  2. import 'package:fis_common/event/event_type.dart';
  3. import 'package:fis_common/logger/logger.dart';
  4. import 'package:web_socket_channel/web_socket_channel.dart';
  5. import 'package:web_socket_channel/status.dart' as statuses;
  6. import 'core/interface/connection.dart';
  7. class WsConnectionException implements Exception {
  8. final dynamic message;
  9. WsConnectionException(this.message);
  10. }
  11. /// WebScoket连接
  12. class WsConnection implements IConnection {
  13. /// 错误断开重试次数
  14. static const C_ERR_RETRY_LIMIT = 5;
  15. /// 连接请求超时时间,10s
  16. static const C_CONNECT_REQ_TIMEOUT = 10 * 1000;
  17. ConnectionStatus _status = ConnectionStatus.connectedClosed;
  18. int _errorRetryCount = 0;
  19. bool _keepAlive = false;
  20. bool _hasError = false;
  21. /// 主机地址
  22. final String host;
  23. WebSocketChannel? _channel;
  24. Timer? _timer;
  25. WsConnection(this.host) {
  26. exceptionOccurred = FEventHandler<Exception>();
  27. statusChanged = FEventHandler<ConnectionStatus>();
  28. messageReceived = FEventHandler<dynamic>();
  29. }
  30. /// 连接状态
  31. ConnectionStatus get status => _status;
  32. /// 是否已连接
  33. bool get isConnected =>
  34. _channel != null && status == ConnectionStatus.connected;
  35. bool get isKeepAlive => _keepAlive;
  36. @override
  37. late final FEventHandler<Exception> exceptionOccurred;
  38. @override
  39. late final FEventHandler<dynamic> messageReceived;
  40. @override
  41. late final FEventHandler<ConnectionStatus> statusChanged;
  42. @override
  43. Future<bool> connect() async {
  44. logger.i("WsConnection is trying connect...");
  45. _hasError = false;
  46. _keepAlive = true;
  47. try {
  48. final uri = Uri.parse(host);
  49. _channel = WebSocketChannel.connect(uri);
  50. final connectResult = await _waitConnectWithTimeout(_channel!);
  51. if (!connectResult) {
  52. _updateStatus(ConnectionStatus.connectFailed);
  53. return false;
  54. }
  55. _updateStatus(ConnectionStatus.connected);
  56. logger.i("The ws connection is connected.");
  57. _startCheckConnection();
  58. _channel!.stream.listen(
  59. (data) {
  60. messageReceived.emit(this, data);
  61. },
  62. onError: (error) {
  63. _hasError = true;
  64. _handleCancelOnError(error);
  65. },
  66. cancelOnError: true,
  67. onDone: () {},
  68. );
  69. } catch (e) {
  70. logger.e("WsConnection connect error.", e);
  71. _updateStatus(ConnectionStatus.connectFailed);
  72. _handleCancelOnError(e);
  73. return false;
  74. }
  75. return true;
  76. }
  77. Future<bool> _waitConnectWithTimeout(
  78. WebSocketChannel channel, {
  79. Duration timeLimit = const Duration(seconds: 3),
  80. }) async {
  81. Future<bool> wrapperFn() async {
  82. await channel.ready;
  83. return true;
  84. }
  85. final result = await wrapperFn().timeout(timeLimit, onTimeout: () => false);
  86. return result;
  87. }
  88. @override
  89. Future<bool> close() async {
  90. logger.i("WsConnection is closing...");
  91. _keepAlive = false;
  92. _stopCheckConnection();
  93. if (_channel != null) {
  94. if (_channel!.closeCode == null) {
  95. await _channel!.sink.close(statuses.normalClosure);
  96. }
  97. _channel = null;
  98. }
  99. _updateStatus(ConnectionStatus.connectedClosed);
  100. logger.i("The ws connection is disconnected.");
  101. return true;
  102. }
  103. void _updateStatus(ConnectionStatus value) {
  104. try {
  105. if (value != _status) {
  106. _status = value;
  107. statusChanged?.emit(this, value);
  108. }
  109. } catch (e) {
  110. //
  111. }
  112. }
  113. void _startCheckConnection() {
  114. _timer?.cancel();
  115. _timer = Timer.periodic(
  116. const Duration(milliseconds: 500),
  117. (timer) {
  118. if (isConnected) {
  119. if (_channel!.closeCode != null) {
  120. _onDisconnected();
  121. }
  122. }
  123. },
  124. );
  125. }
  126. void _stopCheckConnection() {
  127. _timer?.cancel();
  128. _timer = null;
  129. }
  130. void _onDisconnected() {
  131. _updateStatus(ConnectionStatus.connectedClosed);
  132. _channel = null;
  133. logger.i("WsConnection done, the ws connection is disconnected.");
  134. _tryReconnect();
  135. }
  136. /// 处理因错误导致的连接中断
  137. void _handleCancelOnError(error) async {
  138. exceptionOccurred?.emit(this, WsConnectionException(error));
  139. _updateStatus(ConnectionStatus.connectedClosed);
  140. logger.e("WsConnection error, the ws connection is disconnected.", error);
  141. _tryReconnect();
  142. }
  143. Future<void> _tryReconnect() async {
  144. _stopCheckConnection();
  145. if (_keepAlive) {
  146. // 延迟1000ms后尝试再次连接
  147. await Future.delayed(const Duration(milliseconds: 1000));
  148. logger.i(
  149. "WsConnection is now keeping alive and trying to reconnect for $_errorRetryCount time.");
  150. connect();
  151. _errorRetryCount++;
  152. }
  153. }
  154. }