Skip to content

Commit 42ad127

Browse files
Improve reconnection events, add RoomAttemptReconnectEvent. (#439)
* try to fix issue #438. * Improve reconnection events, add RoomAttemptReconnectEvent. * update. * cleanup. * update. * update. * Update lib/src/core/signal_client.dart Co-authored-by: David Zhao <[email protected]> * Update signal_client.dart --------- Co-authored-by: David Zhao <[email protected]>
1 parent 7d93eca commit 42ad127

File tree

8 files changed

+112
-33
lines changed

8 files changed

+112
-33
lines changed

example/lib/pages/room.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ class _RoomPageState extends State<RoomPage> {
9494
..on<RoomRecordingStatusChanged>((event) {
9595
context.showRecordingStatusChangedDialog(event.activeRecording);
9696
})
97+
..on<RoomAttemptReconnectEvent>((event) {
98+
print(
99+
'Attempting to reconnect ${event.attempt}/${event.maxAttemptsRetry}, '
100+
'(${event.nextRetryDelaysInMs}ms delay until next attempt)');
101+
})
97102
..on<LocalTrackPublishedEvent>((_) => _sortParticipants())
98103
..on<LocalTrackUnpublishedEvent>((_) => _sortParticipants())
99104
..on<TrackSubscribedEvent>((_) => _sortParticipants())

lib/src/core/engine.dart

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
228228
await signalClient.cleanUp();
229229

230230
fullReconnectOnNext = false;
231+
attemptingReconnect = false;
231232

232233
clearPendingReconnect();
233234
}
@@ -625,8 +626,25 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
625626
reconnectStart = DateTime.now();
626627
}
627628

629+
if (reconnectAttempts! >= _reconnectCount) {
630+
logger.fine('reconnectAttempts exceeded, disconnecting...');
631+
_isClosed = true;
632+
await cleanUp();
633+
634+
events.emit(EngineDisconnectedEvent(
635+
reason: DisconnectReason.reconnectAttemptsExceeded,
636+
));
637+
return;
638+
}
639+
628640
var delay = defaultRetryDelaysInMs[reconnectAttempts!];
629641

642+
events.emit(EngineAttemptReconnectEvent(
643+
attempt: reconnectAttempts! + 1,
644+
maxAttempts: _reconnectCount,
645+
nextRetryDelaysInMs: delay,
646+
));
647+
630648
clearReconnectTimeout();
631649
logger.fine(
632650
'WebSocket reconnecting in $delay ms, retry times $reconnectAttempts');
@@ -656,19 +674,10 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
656674
fullReconnectOnNext = true;
657675
}
658676

659-
if (reconnectAttempts! >= _reconnectCount) {
660-
logger.fine('reconnectAttempts exceeded, disconnecting...');
661-
events.emit(EngineDisconnectedEvent(
662-
reason: DisconnectReason.connectionClosed,
663-
));
664-
await cleanUp();
665-
return;
666-
}
667-
668677
try {
669678
attemptingReconnect = true;
670679

671-
if (await signalClient.checkInternetConnection() == false) {
680+
if (await signalClient.networkIsAvailable() == false) {
672681
logger.fine('no internet connection, waiting...');
673682
await signalClient.events.waitFor<SignalConnectivityChangedEvent>(
674683
duration: connectOptions.timeouts.connection * 10,
@@ -688,14 +697,12 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
688697
} catch (e) {
689698
reconnectAttempts = reconnectAttempts! + 1;
690699
bool recoverable = true;
691-
if (e is WebSocketException ||
692-
e is ConnectException ||
693-
e is MediaConnectException) {
700+
if (e is WebSocketException || e is MediaConnectException) {
694701
// cannot resume connection, need to do full reconnect
695702
fullReconnectOnNext = true;
696-
} else if (e is TimeoutException) {
697-
fullReconnectOnNext = false;
698-
} else {
703+
}
704+
705+
if (e is UnexpectedConnectionState) {
699706
recoverable = false;
700707
}
701708

@@ -704,7 +711,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
704711
} else {
705712
logger.fine('attemptReconnect: disconnecting...');
706713
events.emit(EngineDisconnectedEvent(
707-
reason: DisconnectReason.connectionClosed,
714+
reason: DisconnectReason.disconnected,
708715
));
709716
await cleanUp();
710717
}
@@ -835,7 +842,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
835842
}
836843

837844
void _setUpEngineListeners() =>
838-
events.on<EngineReconnectingEvent>((event) async {
845+
events.on<SignalReconnectedEvent>((event) async {
839846
// send queued requests if engine re-connected
840847
signalClient.sendQueuedRequests();
841848
});
@@ -907,14 +914,15 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
907914
})
908915
..on<SignalConnectingEvent>((event) async {
909916
logger.fine('Signal connecting');
917+
events.emit(const EngineConnectingEvent());
910918
})
911919
..on<SignalReconnectingEvent>((event) async {
912920
logger.fine('Signal reconnecting');
913921
events.emit(const EngineReconnectingEvent());
914922
})
915923
..on<SignalDisconnectedEvent>((event) async {
916924
logger.fine('Signal disconnected ${event.reason}');
917-
if (event.reason == DisconnectReason.connectionClosed && !_isClosed) {
925+
if (event.reason == DisconnectReason.disconnected && !_isClosed) {
918926
await handleDisconnect(ClientDisconnectReason.signal);
919927
} else {
920928
events.emit(EngineDisconnectedEvent(

lib/src/core/room.dart

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
359359
events.emit(const RoomReconnectedEvent());
360360
// re-send tracks permissions
361361
localParticipant?.sendTrackSubscriptionPermissions();
362+
notifyListeners();
362363
})
363364
..on<EngineFullRestartingEvent>((event) async {
364365
events.emit(const RoomRestartingEvent());
@@ -379,6 +380,7 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
379380
events.emit(ParticipantDisconnectedEvent(participant: participant));
380381
await participant.dispose();
381382
}
383+
notifyListeners();
382384
})
383385
..on<EngineRestartedEvent>((event) async {
384386
events.emit(const RoomRestartedEvent());
@@ -393,14 +395,32 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
393395
}
394396
}
395397
}
398+
notifyListeners();
396399
})
397400
..on<EngineReconnectingEvent>((event) async {
398401
events.emit(const RoomReconnectingEvent());
399402
await _sendSyncState();
403+
notifyListeners();
404+
})
405+
..on<EngineAttemptReconnectEvent>((event) async {
406+
events.emit(RoomAttemptReconnectEvent(
407+
attempt: event.attempt,
408+
maxAttemptsRetry: event.maxAttempts,
409+
nextRetryDelaysInMs: event.nextRetryDelaysInMs,
410+
));
411+
notifyListeners();
400412
})
401413
..on<EngineDisconnectedEvent>((event) async {
402-
await _cleanUp();
403-
events.emit(RoomDisconnectedEvent(reason: event.reason));
414+
if (!engine.fullReconnectOnNext &&
415+
![
416+
DisconnectReason.signalingConnectionFailure,
417+
DisconnectReason.joinFailure,
418+
DisconnectReason.noInternetConnection
419+
].contains(event.reason)) {
420+
await _cleanUp();
421+
events.emit(RoomDisconnectedEvent(reason: event.reason));
422+
notifyListeners();
423+
}
404424
})
405425
..on<EngineActiveSpeakersUpdateEvent>(
406426
(event) => _onEngineActiveSpeakersUpdateEvent(event.speakers))

lib/src/core/signal_client.dart

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
6161
ConnectivityResult? _connectivityResult;
6262
StreamSubscription<ConnectivityResult>? connectivitySubscription;
6363

64-
Future<bool> checkInternetConnection() async {
65-
if (!kIsWeb && !lkPlatformIsTest()) {
64+
Future<bool> networkIsAvailable() async {
65+
// Skip check for web or flutter test
66+
if (kIsWeb || lkPlatformIsTest()) {
6667
return true;
6768
}
6869
_connectivityResult = await Connectivity().checkConnectivity();
@@ -98,15 +99,19 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
9899
.onConnectivityChanged
99100
.listen((ConnectivityResult result) {
100101
if (_connectivityResult != result) {
101-
_connectivityResult = result;
102102
if (result == ConnectivityResult.none) {
103-
logger.warning('lost internet connection');
103+
logger.warning('lost connectivity');
104104
} else {
105-
logger.info('internet connection restored');
106-
events.emit(SignalConnectivityChangedEvent(
107-
state: result,
108-
));
105+
logger.info(
106+
'Connectivity changed, ${_connectivityResult!.name} => ${result.name}');
109107
}
108+
109+
events.emit(SignalConnectivityChangedEvent(
110+
oldState: _connectivityResult!,
111+
state: result,
112+
));
113+
114+
_connectivityResult = result;
110115
}
111116
});
112117

@@ -140,14 +145,16 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
140145
// Clean up existing socket
141146
await cleanUp();
142147
// Attempt to connect
143-
_ws = await _wsConnector(
148+
var future = _wsConnector(
144149
rtcUri,
145150
WebSocketEventHandlers(
146151
onData: _onSocketData,
147152
onDispose: _onSocketDispose,
148153
onError: _onSocketError,
149154
),
150155
);
156+
future = future.timeout(connectOptions.timeouts.connection);
157+
_ws = await future;
151158
// Successful connection
152159
_connectionState = ConnectionState.connected;
153160
events.emit(const SignalConnectedEvent());
@@ -340,8 +347,7 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
340347
return;
341348
}
342349
_connectionState = ConnectionState.disconnected;
343-
events.emit(
344-
SignalDisconnectedEvent(reason: DisconnectReason.connectionClosed));
350+
events.emit(SignalDisconnectedEvent(reason: DisconnectReason.disconnected));
345351
}
346352

347353
void _sendPing() {

lib/src/events.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,21 @@ class RoomReconnectingEvent with RoomEvent {
6666
String toString() => '${runtimeType}()';
6767
}
6868

69+
/// report the number of attempts to reconnect to the room.
70+
class RoomAttemptReconnectEvent with RoomEvent {
71+
final int attempt;
72+
final int maxAttemptsRetry;
73+
final int nextRetryDelaysInMs;
74+
const RoomAttemptReconnectEvent({
75+
required this.attempt,
76+
required this.maxAttemptsRetry,
77+
required this.nextRetryDelaysInMs,
78+
});
79+
80+
@override
81+
String toString() => '${runtimeType}()';
82+
}
83+
6984
/// Connection to room is re-established. All existing state is preserved.
7085
/// Emitted by [Room].
7186
class RoomReconnectedEvent with RoomEvent {

lib/src/exceptions.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,8 @@ class LiveKitE2EEException extends LiveKitException {
8888
@override
8989
String toString() => 'E2EE Exception: [$runtimeType] $message';
9090
}
91+
92+
class UnexpectedConnectionState extends LiveKitException {
93+
UnexpectedConnectionState([String msg = 'Unexpected connection state'])
94+
: super._(msg);
95+
}

lib/src/internal/events.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,19 @@ class SignalReconnectResponseEvent with SignalEvent, InternalEvent {
143143

144144
@internal
145145
class SignalConnectivityChangedEvent with SignalEvent, InternalEvent {
146+
final ConnectivityResult oldState;
146147
final ConnectivityResult state;
147148
const SignalConnectivityChangedEvent({
149+
required this.oldState,
148150
required this.state,
149151
});
150152
}
151153

154+
@internal
155+
class EngineConnectingEvent with InternalEvent, EngineEvent {
156+
const EngineConnectingEvent();
157+
}
158+
152159
@internal
153160
class EngineConnectedEvent with InternalEvent, SignalEvent, EngineEvent {
154161
const EngineConnectedEvent();
@@ -167,6 +174,18 @@ class EngineFullRestartingEvent with InternalEvent, EngineEvent {
167174
const EngineFullRestartingEvent();
168175
}
169176

177+
@internal
178+
class EngineAttemptReconnectEvent with InternalEvent, EngineEvent {
179+
int attempt;
180+
int maxAttempts;
181+
int nextRetryDelaysInMs;
182+
EngineAttemptReconnectEvent({
183+
required this.attempt,
184+
required this.maxAttempts,
185+
required this.nextRetryDelaysInMs,
186+
});
187+
}
188+
170189
@internal
171190
class EngineRestartedEvent with InternalEvent, EngineEvent {
172191
const EngineRestartedEvent();

lib/src/types/other.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,10 @@ enum DisconnectReason {
8484
roomDeleted,
8585
stateMismatch,
8686
joinFailure,
87-
connectionClosed,
87+
disconnected,
8888
signalingConnectionFailure,
8989
noInternetConnection,
90+
reconnectAttemptsExceeded,
9091
}
9192

9293
/// The reason why a track failed to publish.

0 commit comments

Comments
 (0)