تكرار السؤال: …
الإجابة المختصرة
استخدم exponential backoff مع حد أقصى وjitter وأوقف المحاولات عند dispose أو logout.
الشرح
عند انقطاع الاتصال لا تعد المحاولة فورًا وبلا حد؛ فآلاف العملاء المنقطعين معًا سيطلقون عاصفة اتصالات تُسقط الخادم المتعافي. استخدم exponential backoff (تأخير يتضاعف مع كل محاولة حتى حد أقصى) مع jitter عشوائي لتفريق العملاء، وحدًا أقصى للمحاولات أو مدة كلية. وميّز بين أسباب الإغلاق: انقطاع شبكة (أعد المحاولة)، رفض مصادقة أو إغلاق مقصود من الخادم (لا تعد المحاولة تلقائيًا، حدّث الرمز أو اطلب دخولًا جديدًا)، وإغلاق يدوي عند dispose أو logout (لا تعد أبدًا). وأعد تعيين عداد المحاولات بعد اتصال ناجح مستقر.
مثال بالكود
class ReconnectingSocket {
ReconnectingSocket(this.uri);
final Uri uri;
final _random = Random();
WebSocketChannel? _channel;
Timer? _retryTimer;
int _attempt = 0;
bool _disposed = false;
void connect() {
if (_disposed) return;
_channel = WebSocketChannel.connect(uri);
_channel!.stream.listen(
(_) => _attempt = 0, // a received message proves the connection is healthy
onDone: _scheduleReconnect,
onError: (_) => _scheduleReconnect(),
);
}
void _scheduleReconnect() {
if (_disposed) return;
final cap = min(30000, 500 * pow(2, _attempt).toInt()); // 0.5s, 1s, 2s … capped at 30s
final delay = Duration(milliseconds: _random.nextInt(cap)); // full jitter
_attempt++;
_retryTimer?.cancel();
_retryTimer = Timer(delay, connect);
}
void dispose() {
_disposed = true; // never reconnect after an intentional close
_retryTimer?.cancel();
_channel?.sink.close(status.normalClosure);
}
}أخطاء شائعة
- إعادة الاتصال الفوري بلا تأخير أو حد أقصى، فتنشأ عاصفة اتصالات عند تعافي الخادم.
- إعادة المحاولة بعد فشل المصادقة (401/4401) بنفس الرمز المنتهي بدل تجديده.
- نسيان إيقاف مؤقت إعادة الاتصال عند dispose أو logout فتستمر المحاولات في الخلفية.
أسئلة متابعة
- كيف تتعامل مع ترتيب الرسائل وتكرارها بعد reconnect؟
- ما الذي يحدث لاتصال realtime عند انتهاء الجلسة أو logout؟