← Назад к вопросам

Сколько будет WebSockets при двух открытых вкладках?

1.8 Middle🔥 131 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

WebSockets при двух открытых вкладках

Это отличный вопрос, который проверяет понимание того, как браузер работает с WebSockets и как устроено межвкладочное взаимодействие. Ответ зависит от нескольких факторов.

Базовый ответ: ДВА WebSocket соединения

По умолчанию, каждая вкладка (tab) браузера работает независимо и имеет свой собственный контекст JavaScript. Поэтому если в каждой вкладке открыт одинаковый сайт, то обычно будет ДВА отдельных WebSocket соединения — одно для каждой вкладки.

// Код в каждой вкладке
const socket = new WebSocket("wss://example.com/ws");

socket.onopen = () => {
  console.log("Connected");
};

socket.onmessage = (event) => {
  console.log("Message:", event.data);
};

Результат:

  • Вкладка 1: WebSocket #1 подключена
  • Вкладка 2: WebSocket #2 подключена
  • Итого: 2 WebSocket соединения

Почему два, а не одно?

  1. Отдельные контексты — каждая вкладка имеет свой глобальный объект window и отдельную переменную socket
  2. Независимые сессии — браузер не синхронизирует состояние WebSockets между вкладками автоматически
  3. TCP соединения — каждый WebSocket — это отдельное TCP соединение на уровне ОС

Можно ли сделать ОДНОWeb Socket для двух вкладок?

Да! Есть несколько подходов:

1. SharedWorker (самый элегантный)

// shared-worker.js
let socket;
const clients = [];

onconnect = (event) => {
  const port = event.ports[0];
  clients.push(port);
  
  if (!socket) {
    socket = new WebSocket("wss://example.com/ws");
    
    socket.onmessage = (msg) => {
      // Отправляем сообщение всем вкладкам
      clients.forEach(p => p.postMessage({type: "message", data: msg.data}));
    };
  }
  
  port.onmessage = (event) => {
    if (event.data.type === "send") {
      socket.send(event.data.payload);
    }
  };
  
  port.start();
};

// main.js (в каждой вкладке)
const worker = new SharedWorker("shared-worker.js");
const port = worker.port;

port.onmessage = (event) => {
  if (event.data.type === "message") {
    console.log("Received:", event.data.data);
  }
};

port.start();

function sendMessage(text) {
  port.postMessage({type: "send", payload: text});
}

Результат:

  • SharedWorker: 1 WebSocket соединение
  • Все вкладки обмениваются через SharedWorker
  • Итого: 1 WebSocket соединение на несколько вкладок

2. Service Worker + Broadcast Channel

// service-worker.js
let socket;
const connections = new Set();

self.onmessage = (event) => {
  if (event.data.type === "INIT") {
    if (!socket) {
      socket = new WebSocket("wss://example.com/ws");
      
      socket.onmessage = (msg) => {
        connections.forEach(client => {
          client.postMessage({type: "WS_MESSAGE", data: msg.data});
        });
      };
    }
    connections.add(event.ports[0]);
    event.ports[0].start();
  }
};

// main.js (в каждой вкладке)
const channel = new BroadcastChannel("websocket-channel");

channel.onmessage = (event) => {
  if (event.data.type === "WS_MESSAGE") {
    console.log("Received from WebSocket:", event.data.data);
  }
};

function sendViaWebSocket(message) {
  channel.postMessage({type: "SEND", message});
}

3. Простой способ — Broadcast Channel (работает, но требует полного контроля)

// main.js
const channel = new BroadcastChannel("app-sync");
let socket = null;
let isTabWithSocket = false;

// Определяем, будет ли эта вкладка хранить WebSocket
function initializeSocket() {
  channel.postMessage({type: "REQUEST_SOCKET_OWNER"});
  
  setTimeout(() => {
    if (!isTabWithSocket) {
      // Эта вкладка владеет WebSocket
      socket = new WebSocket("wss://example.com/ws");
      isTabWithSocket = true;
      
      socket.onmessage = (msg) => {
        channel.postMessage({type: "WS_MESSAGE", data: msg.data});
      };
    }
  }, 100);
}

channel.onmessage = (event) => {
  if (event.data.type === "REQUEST_SOCKET_OWNER" && isTabWithSocket) {
    // Отвечаем, что мы владеем сокетом
  }
  
  if (event.data.type === "WS_MESSAGE") {
    console.log("Message from WebSocket:", event.data.data);
  }
};

initializeSocket();

Практические проблемы с двумя WebSockets

Пример проблемы:

// Сервер отправляет сообщение каждому клиенту
// Вкладка 1: получает "Hello"
// Вкладка 2: получает "Hello"
// 
// Если вкладки синхронизируют одно и то же состояние,
// может произойти дублирование или рассинхронизация

Когда нужно одно соединение?

  1. Реал-тайм синхронизация данных — chat, collaborative editor
  2. Экономия ресурсов — минимизировать сетевые соединения
  3. Единое состояние приложения — Redux, MobX
  4. Server push — уведомления должны приходить одному потребителю

Проверка в DevTools

// Посмотреть все открытые соединения
window.performance.getEntriesByType(resource)
  .filter(r => r.name.includes(ws))
  .forEach(r => console.log(r.name));

// Или через DevTools:
// F12 → Network → Filter → WS (WebSocket)
// Откроешь две вкладки → увидишь 2 соединения

Итоговый ответ

СценарийКоличество соединенийКак реализовать
По умолчанию2 WebSocketКаждая вкладка подключается независимо
Оптимизированный1 WebSocketSharedWorker, Service Worker или Broadcast Channel
Специальный случай3+ WebSocketЕсли разные URL или если специально создавать

Для интервью ожидаемый ответ:

"По умолчанию будет ДВА WebSocket соединения, потому что каждая вкладка имеет независимый JavaScript контекст. Но если нужно оптимизировать и использовать одно соединение, можно использовать SharedWorker или Service Worker для централизованного управления WebSocket, а остальные вкладки будут взаимодействовать с ним через MessagePort или BroadcastChannel."