# WebSocket Ping/Pong механизм

Данный документ описывает механизм поддержания WebSocket соединений и мониторинга их состояния через ping/pong обмен.

## Обзор функциональности

SDK поддерживает автоматический механизм отправки ping-сообщений и обработки pong-ответов для:

1. **Проверки активности соединения** - обнаружение разрывов соединения даже в отсутствие активности
2. **Измерения времени отклика (Round Trip Time, RTT)** - мониторинг качества соединения
3. **Сбора статистики соединения** - для диагностики и отладки
4. **Автоматического обнаружения проблем** - уведомление о потере соединения

## Включение механизма ping/pong

```typescript
import { CodeSolverSDK, WebSocketNamespace } from 'solver-sdk';

// Создаем экземпляр SDK
const sdk = new CodeSolverSDK({
  baseURL: 'https://api.example.com',
  apiKey: 'your-api-key'
});

// Получаем WebSocket клиент
const wsClient = sdk.getWebSocketClient();

// Подключаемся к пространствам имен
await wsClient.connectToReasoning();
await wsClient.connectToDependencies();
await wsClient.connectToIndexing();

// Включаем автоматический механизм ping/pong
// параметры: интервал отправки в мс (по умолчанию 30000) и порог таймаута (по умолчанию 3)
wsClient.enablePingPong(10000, 3);

// Регистрируем обработчик для события таймаута соединения
wsClient.onPingPongEvent('connection_timeout', (data) => {
  console.log(`Соединение потеряно для namespace ${data.namespace}`);
  console.log(`Socket ID: ${data.socketId}`);
  console.log(`Количество пропущенных pong: ${data.timeouts}`);
  
  // Здесь можно добавить логику переподключения или уведомления пользователя
});
```

## Отключение механизма ping/pong

```typescript
// Отключение для всех пространств имен
wsClient.disablePingPong();

// Отключение для конкретного пространства имен
wsClient.disablePingPong(WebSocketNamespace.REASONING);
```

## Получение статистики ping/pong

```typescript
// Получение статистики для всех пространств имен
const allStats = wsClient.getPingStats();
console.log('Статистика для всех соединений:', allStats);

// Получение статистики для конкретного пространства имен
const reasoningStats = wsClient.getPingStats(WebSocketNamespace.REASONING);
console.log('Статистика для reasoning:', reasoningStats);

// Пример содержимого статистики:
// {
//   namespace: '/reasoning',
//   socketId: 'socket-id-123',
//   pingSent: 10,           // Количество отправленных ping
//   pongReceived: 10,       // Количество полученных pong
//   averageRtt: 15.5,       // Среднее время отклика (мс)
//   minRtt: 5,              // Минимальное время отклика (мс)
//   maxRtt: 45,             // Максимальное время отклика (мс)
//   lastRtt: 12,            // Последнее измеренное время отклика (мс)
//   lastPongTimestamp: 1712345678901, // Timestamp последнего полученного pong
//   isConnected: true       // Текущий статус соединения
// }
```

## Ручная отправка ping/pong

Хотя SDK обеспечивает автоматический механизм ping/pong, вы также можете вручную отправлять ping-сообщения:

```typescript
// Отправка ping и получение pong
wsClient.send(WebSocketNamespace.REASONING, 'connection_ping', { 
  timestamp: Date.now() 
});

// Добавление обработчика для pong-ответов
wsClient.on('connection_pong', (data) => {
  const rtt = Date.now() - data.echo;
  console.log(`Получен pong, RTT: ${rtt}ms`);
});

// Альтернативно, можно использовать готовый обработчик
wsClient.on('connection_pong', wsClient.getPongHandler());
```

## Обработка отключений и переподключение

```typescript
// Обработка события таймаута соединения
wsClient.onPingPongEvent('connection_timeout', async (data) => {
  console.log(`Соединение потеряно для ${data.namespace}`);
  
  // Попытка переподключения
  try {
    // Отключаемся от проблемного пространства имен
    wsClient.disconnect(data.namespace);
    
    // Пауза перед повторным подключением
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    // Переподключаемся
    switch (data.namespace) {
      case WebSocketNamespace.REASONING:
        await wsClient.connectToReasoning();
        break;
      case WebSocketNamespace.DEPENDENCIES:
        await wsClient.connectToDependencies();
        break;
      case WebSocketNamespace.INDEXING:
        await wsClient.connectToIndexing();
        break;
    }
    
    console.log(`Успешно переподключились к ${data.namespace}`);
  } catch (error) {
    console.error(`Ошибка при переподключении к ${data.namespace}:`, error);
  }
});
```

## Рекомендации по использованию

1. **Интервал ping/pong**: Рекомендуемый интервал - 30 секунд для продакшн и 5-10 секунд для отладки.

2. **Порог таймаута**: Значение 2-3 пропущенных ping/pong позволяет избежать ложных срабатываний при временных задержках сети.

3. **Экономия трафика**: При частой передаче данных через WebSocket можно увеличить интервал ping/pong, так как регулярный обмен данными уже поддерживает соединение активным.

4. **Мониторинг RTT**: Используйте statsPingStats для мониторинга задержек сети и раннего обнаружения проблем с соединением.

5. **Отключение перед закрытием приложения**: Вызывайте disablePingPong() и затем disconnectAll() при закрытии приложения для корректного освобождения ресурсов.

## Пример комплексного использования

```typescript
async function setupWebSocketWithHealthMonitoring() {
  const sdk = new CodeSolverSDK({
    baseURL: 'https://api.example.com',
    apiKey: 'your-api-key',
    websocket: {
      reconnect: true,
      reconnectAttempts: 5,
      reconnectDelay: 2000
    }
  });
  
  const wsClient = sdk.getWebSocketClient();
  
  // Подключаемся ко всем пространствам имен
  await wsClient.connectToReasoning();
  await wsClient.connectToDependencies();
  await wsClient.connectToIndexing();
  
  // Включаем механизм ping/pong для всех соединений
  wsClient.enablePingPong(20000, 3);
  
  // Регистрируем обработчик для события таймаута
  wsClient.onPingPongEvent('connection_timeout', handleConnectionTimeout);
  
  // Периодически проверяем статистику соединений
  const statsInterval = setInterval(() => {
    const stats = wsClient.getPingStats();
    
    // Анализируем статистику
    for (const stat of stats) {
      if (stat.averageRtt > 500) {
        console.warn(`Высокая задержка для ${stat.namespace}: ${stat.averageRtt}ms`);
      }
    }
  }, 60000); // Проверка каждую минуту
  
  // Функция для обработки таймаута соединения
  async function handleConnectionTimeout(data) {
    console.error(`Соединение потеряно для ${data.namespace}`);
    
    // Логика переподключения...
  }
  
  // Функция для корректного закрытия соединений
  function cleanup() {
    clearInterval(statsInterval);
    wsClient.disablePingPong();
    wsClient.disconnectAll();
  }
  
  // Возвращаем функцию очистки
  return cleanup;
}

// Использование
const cleanup = await setupWebSocketWithHealthMonitoring();

// При закрытии приложения
window.addEventListener('beforeunload', cleanup);
``` 