Рубрики
Flutter

WebSockets во Flutter: Real-time с нуля до продакшена

Полное руководство по WebSockets во Flutter: реализация, reconnection strategies, backoff, оптимизация трафика.

WebSockets — это технология для real-time双向 связи между клиентом и сервером. В этой статье реализуем WebSocket клиент во Flutter с нуля.

Что такое WebSockets?

WebSocket — это протокол, который предоставляет:

  • Постоянное соединение
  • Двустороннюю связь в реальном времени
  • Низкие накладные расходы
  • Поддержка на всех платформах

Установка

dependencies:
  web_socket_channel: ^2.4.0

Базовая реализация

import 'package:web_socket_channel/web_socket_channel.dart';

class WebSocketService {
  final WebSocketChannel _channel;
  final StreamController<String> _messages = StreamController.broadcast();

  WebSocketService(String url)
      : _channel = WebSocketChannel.connect(url);

  Stream<String> get messages => _messages.stream;

  void send(String message) {
    _channel.sink.add(message);
  }

  void dispose() {
    _channel.sink.close();
    _messages.close();
  }
}

Обработка сообщений

class MyWebSocketWidget extends StatefulWidget {
  @override
  Widget build(BuildContext context) {
    final ws = WebSocketService('ws://example.com');

    return StreamBuilder<String>(
      stream: ws.messages,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Text('Received: ${snapshot.data}');
        }
        return CircularProgressIndicator();
      },
    );
  }
}

Reconnection

class ReconnectingWebSocket {
  final String url;
  WebSocketChannel? _channel;
  Timer? _reconnectTimer;
  int _retryCount = 0;

  static const _maxRetries = 5;
  static const _baseDelay = Duration(seconds: 1);

  void connect() {
    _channel = WebSocketChannel.connect(url);

    _channel!.stream.listen(
      (data) {
        _retryCount = 0; // Сбросить счётчик
        // Обработка данных
      },
      onError: (error) {
        print('WebSocket error: $error');
        _scheduleReconnect();
      },
      onDone: () {
        print('WebSocket closed');
        _scheduleReconnect();
      },
    );
  }

  void _scheduleReconnect() {
    _reconnectTimer?.cancel();

    final delay = _baseDelay * pow(2, _retryCount).toInt();

    _reconnectTimer = Timer(delay, () {
      if (_retryCount < _maxRetries) {
        _retryCount++;
        connect();
      }
    });
  }

  void disconnect() {
    _reconnectTimer?.cancel();
    _channel?.sink.close();
  }
}

Heartbeat

class HeartbeatWebSocket {
  Timer? _heartbeatTimer;

  void connect() {
    _channel = WebSocketChannel.connect(url);

    // Heartbeat каждые 30 секунд
    _heartbeatTimer = Timer.periodic(
      Duration(seconds: 30),
      (_) => _channel?.sink.add('ping'),
    );
  }

  @override
  void dispose() {
    _heartbeatTimer?.cancel();
    super.dispose();
  }
}

Заключение

WebSockets — идеальны для чатов, real-time обновлений и онлайн игр. Реализуйте reconnection и heartbeat для production.