В статье про мультиплеер на Supabase Realtime разобрана простая схема: включаешь Realtime в настройках проекта, подписываешься на канал — и обновления долетают до всех игроков сами, без единой строчки серверного кода. Это управляемый сервис: Supabase держит соединения, следит за нагрузкой, перезапускает упавшее. Socket.io устроен по-другому. Это не сервис, а библиотека, из которой ты сам собираешь WebSocket-сервер — со своей логикой, своим хостингом и своей ответственностью за то, чтобы он не упал в пятницу вечером.
Содержание
- Разница в одной мысли: кто держит сервер
- Что такое Socket.io и чем он не является
- Комнаты и события: гибкость, которой нет у managed-сервиса
- Своя логика — то, что managed-сервис в принципе не покроет
- Не завязан на конкретную базу данных
- Кто проверяет права доступа
- Обратная сторона: сервер теперь твой
- Сравнение Socket.io и Supabase Realtime
- Честный совет новичку
- Как выглядит минимальный сервер на Socket.io
- Если решил остаться на Supabase
- Частые ошибки при переходе на свой сервер
- Чек-лист «свой сервер реального времени готов»
- Частые вопросы
- Заключение
Разница в одной мысли: кто держит сервер
Supabase Realtime и Socket.io решают одну и ту же задачу — доставить событие от одного браузера к другому в реальном времени. Разница не в задаче, а в том, кто отвечает за инфраструктуру между ними.
С Supabase Realtime сервер — забота провайдера. Ты подключаешься к готовой шине сообщений, платишь по тарифу за использование и не думаешь о том, что происходит на стороне, которая эти сообщения разносит. Упал сервер — это забота Supabase, не твоя. Выросла нагрузка — масштабирование тоже на стороне провайдера.
С Socket.io сервер — твоя забота целиком. Ты пишешь Node.js-приложение, которое держит открытое соединение с каждым игроком, разворачиваешь его на своём хостинге и сам решаешь, что делать, если процесс упал или трафика стало вдвое больше, чем вчера. Взамен получаешь то, чего управляемый сервис в принципе не может дать: полный контроль над тем, что именно происходит на сервере в момент, когда приходит событие.
Это ключевое отличие определяет всё остальное в статье. Управляемый сервис экономит время на старте и снимает вопрос эксплуатации. Своя библиотека отдаёт время взамен на свободу делать что угодно с логикой в реальном времени — не только «обновилась строка в таблице», а любой сценарий, который придумаешь сам.
Что такое Socket.io и чем он не является
Socket.io — библиотека для Node.js (и парная библиотека для браузера), которая упрощает работу с постоянным соединением между клиентом и сервером. В основе лежит WebSocket — тот же протокол, на котором построен и Supabase Realtime: браузер один раз открывает канал, и дальше сервер с клиентом обмениваются сообщениями без повторных запросов и ожидания ответа на каждый чих.
Важно не путать Socket.io с самим протоколом WebSocket. Голый WebSocket — это низкоуровневый стандарт: открыл соединение, шлёшь и принимаешь сырые сообщения, а всё остальное — переподключение при обрыве, деление сообщений на именованные события, поддержка браузеров без WebSocket — пишешь сам. Socket.io берёт эту рутину на себя: у него есть автоматический реконнект, встроенный формат именованных событий (socket.on('ate', handler) вместо разбора текста вручную) и fallback на HTTP long-polling для сетей, где WebSocket почему-то заблокирован.
За удобство приходится платить тем же, чем платишь за любую библиотеку с собственным протоколом поверх стандарта: клиент Socket.io должен использовать именно клиентскую библиотеку Socket.io, а не произвольный WebSocket-клиент. Это не проблема для веб-игры, где и сервер, и клиент пишешь сам, но стоит держать в голове, если в проект когда-нибудь понадобится подключить внешнюю систему, которая умеет только «чистый» WebSocket.
Комнаты и события: гибкость, которой нет у managed-сервиса
В статье про Supabase Realtime канал — это именованная комната вроде room:abc123, и события в ней — direction, moved, ate, died, spawned. У Socket.io есть прямой аналог — комнаты (rooms): сервер может добавить соединение игрока в одну или несколько комнат командой socket.join('room:abc123') и рассылать сообщения либо всем подключённым, либо только участникам конкретной комнаты.
На уровне «два игрока в одной партии обмениваются событиями о передвижении» разницы почти нет — что там, что там получится рабочий канал связи. Разница начинается там, где сценарий выходит за пределы «строка в таблице обновилась, разошли её всем подписчикам». У Socket.io на сервере можно:
- держать несколько разных комнат для одного игрока одновременно — например, комнату конкретной партии и отдельную комнату лобби, где считаются игроки онлайн;
- обрабатывать событие на сервере перед рассылкой, а не просто ретранслировать его от одного клиента к другому — скажем, проверить, что яблоко ещё не забрал другой игрок, прежде чем подтвердить съедение;
- присылать разным игрокам в одной партии разные версии одного события — приватную информацию сопернику не отправлять, публичную разослать всем;
- динамически создавать и закрывать комнаты по кастомной логике игры, а не только по факту изменения записи в базе.
Supabase Realtime тоже умеет присылать серверные события — через Postgres Changes события об изменении строк или через Broadcast от Edge Function, где можно выполнить произвольный код перед рассылкой. Но это код в рамках инфраструктуры Supabase, со своими ограничениями по времени выполнения и моделью Edge Function. Со своим Node.js-сервером таких рамок нет вообще — там ровно тот код, который написал сам, без встроенных лимитов провайдера.
Своя логика — то, что managed-сервис в принципе не покроет
Supabase Realtime отлично закрывает задачу «разослать всем подписчикам факт изменения». Он спроектирован именно под это: таблица обновилась — событие ушло. Но у него нет понятия сложного игрового состояния, которое сервер должен считать сам, а не просто ретранслировать.
Представь механику, где нужен матчмейкинг: игрок нажимает «найти соперника», сервер ищет второго игрока в очереди, сводит их в одну комнату и запускает отсчёт «3, 2, 1, старт» одновременно для обоих. Или механику с приватными каналами внутри одной партии — команда из трёх человек против команды из трёх, где своим видно больше, чем чужим. Или систему с таймаутами хода, где сервер сам следит, не пропустил ли игрок время, и штрафует за это. Всё это — логика, которую пишет сервер приложения, а не декларативное правило вида «пришли мне изменения таблицы».
С Supabase Realtime такую механику всё равно можно собрать, но логику придётся вынести в Edge Functions или другой серверный код рядом — Realtime сам по себе для этого не предназначен, он остаётся каналом доставки. Socket.io в этом смысле честнее: он изначально задуман как место, где живёт серверная логика реального времени, а не только труба для событий.
Похожий пример — режим наблюдателя. Игрок закончил партию и хочет посмотреть, как играют другие, не участвуя сам. С Socket.io это обычная третья комната: сервер добавляет наблюдателя туда же, помечает соединение как read-only и просто не принимает от него игровые события, только рассылает. Реализовать такое поверх Realtime тоже можно, но опять же через дополнительный серверный код рядом, а не средствами самого канала.
Не завязан на конкретную базу данных
Второе принципиальное отличие — независимость от Supabase как платформы. Realtime — это часть экосистемы Supabase: он тесно связан с Postgres проекта и удобнее всего работает, когда данные и так уже там лежат. Это разумный выбор, если приложение и так использует Supabase для аккаунтов и данных — тогда Realtime подключается почти бесплатно по усилиям.
Socket.io не привязан ни к какой базе вообще. Это библиотека уровня транспорта: она ничего не знает про то, откуда сервер берёт данные и куда их сохраняет. Рядом с ней можно поставить Postgres, MongoDB, обычный файл на диске или вообще не использовать базу — например, если состояние партии живёт только в памяти сервера, пока идёт игра. Про выбор базы под конкретную задачу — в статье про выбор базы данных для новичка: принцип оттуда не меняется, Socket.io просто не диктует вариант заранее.
Эта независимость особенно чувствуется, если проект со временем перерастает Supabase целиком — переходит на другого провайдера аутентификации, переезжает на собственную базу или комбинирует несколько источников данных. С Realtime такой переезд означает отказ и от самого Realtime — он не работает в отрыве от Supabase. С Socket.io переезд базы или аутентификации никак не затрагивает слой реального времени: он как был отдельным сервером, так и остаётся.
Кто проверяет права доступа
У Supabase Realtime доступ к данным регулируют политики Row Level Security — те же правила, что применяются к обычным запросам к базе. Игрок подписался на канал, привязанный к таблице рекордов, — Supabase сам сверяет, разрешено ли этому пользователю видеть именно эти строки, согласно уже настроенным политикам. Отдельно писать проверку прав для каждого канала не нужно: она встроена в модель доступа к базе, а не в код обработчика событий.
У Socket.io такой встроенной модели нет. Библиотека знает только про соединения и события — у неё нет понятия «строка базы данных, разрешённая этому пользователю». Значит, каждую проверку прав внутри своего сервера нужно писать вручную: определить, кто подключился, обычно по токену, переданному при установке соединения; убедиться, что этому игроку разрешено попасть именно в эту комнату; и на каждое входящее событие проверить, не пытается ли игрок отправить команду за пределами своих полномочий — например, прислать событие от имени чужого персонажа.
Это не делает Socket.io менее безопасным сам по себе — просто ответственность за проверку прав целиком переходит на код сервера. Для учебной партии, где все игроки в комнате друг другу доверяют, это не критично. Для проекта с реальными данными пользователей на кону такую проверку стоит продумать так же тщательно, как продумываются RLS-политики Supabase — принцип «каждое действие имеет право быть выполненным только тем, кому оно разрешено» переносится сюда один в один, просто применяется вручную в коде, а не декларативным правилом в базе.
Обратная сторона: сервер теперь твой
Всё перечисленное выше — плюсы. Теперь честно про цену. Развернуть Socket.io-сервер — не то же самое, что включить галочку в настройках проекта. Это отдельное Node.js-приложение, и оно нуждается в том же, в чём нуждается любой постоянно работающий сервер.
Хостинг. Node.js-процесс, который держит открытые WebSocket-соединения, должен где-то постоянно работать — обычный статический хостинг для этого не подходит, нужен сервер с долгоживущим процессом. Для деплоя такого сервера в курсе есть отдельная статья про Amvera для новичка: принципы разворачивания Node.js-приложения там применимы и к Socket.io-серверу, разница только в том, что это не сайт, а процесс, который слушает WebSocket-соединения.
Перезапуск при падении. Если процесс сервера упал — из-за необработанной ошибки, нехватки памяти, любой другой причины, — все подключённые к нему игроки в этот момент разом теряют соединение. У managed-сервиса такая ситуация тоже возможна теоретически, но её обрабатывает инфраструктура провайдера, а не твой код. Для своего сервера нужен механизм автоматического перезапуска процесса — большинство хостингов такое умеют из коробки, но об этом нужно узнать заранее, а не постфактум, когда сервер уже упал ночью.
Масштабирование. Один процесс Node.js держит ограниченное число одновременных соединений, зависящее от ресурсов сервера. Пока игроков немного, это не проблема вообще. Но если аудитория выросла настолько, что один сервер не справляется, горизонтальное масштабирование Socket.io — то есть запуск нескольких копий сервера, между которыми нужно синхронизировать события, — требует дополнительной инфраструктуры вроде адаптера на Redis. Для managed-сервиса такое масштабирование — часть тарифа, которую настраивает провайдер, а не разработчик.
Мониторинг. С Supabase Realtime статус сервиса виден в панели провайдера. Со своим сервером узнать, что он упал, — тоже отдельная задача: либо самому настраивать логирование и оповещения, либо периодически проверять руками, что процесс жив.
Отдельная статья расходов. Realtime входит в тариф Supabase вместе с остальными частями платформы. Свой Node.js-сервер требует отдельного хостинга, который работает постоянно, а не только в момент запроса, — а значит, это отдельная строка расходов, даже если сам трафик небольшой. Точные тарифы конкретных хостингов здесь приводить смысла нет — они разные и меняются, но сам факт отдельного счёта за постоянно работающий сервер стоит держать в голове при выборе.
Ничего из этого не запредельно сложно для одного Node.js-сервера с несколькими десятками одновременных игроков — учебный масштаб «Змейки» вполне по силам простому серверу без специальной инфраструктуры. Но это реальная работа, которую managed-сервис делает за тебя бесплатно с точки зрения твоего времени, а Socket.io — нет.
Сравнение Socket.io и Supabase Realtime
| Socket.io | Supabase Realtime | |
|---|---|---|
| Кто держит сервер | Ты сам — отдельный Node.js-процесс на своём хостинге | Провайдер (Supabase) |
| Гибкость серверной логики | Полная — любой код на сервере перед рассылкой события | Ограничена моделью Postgres Changes / Broadcast и рамками Edge Functions |
| Привязка к базе данных | Нет — любая база или вообще без неё | Тесно связан с Postgres проекта Supabase |
| Порог входа для новичка | Выше — нужно поднять и задеплоить отдельный сервер | Ниже — включается в настройках проекта |
| Обслуживание после запуска | Перезапуск, масштабирование, мониторинг — твоя ответственность | Берёт на себя провайдер |
| Комнаты, приватные каналы, кастомные события | Гибкие, задаются кодом сервера | Есть каналы и Broadcast, но сложные сценарии требуют выноса логики в Edge Functions |
| Когда оправдан | Кастомная игровая механика реального времени, независимость от Supabase | Таблица рекордов, счётчик игроков онлайн, простые сценарии обновлений |
Честный совет новичку
Для большинства задач уровня учебной «Змейки» — общая таблица рекордов, счётчик игроков онлайн, простой мультиплеер вроде того, что разобран в статье про Supabase Realtime, — Realtime проще и его достаточно. Не нужно поднимать отдельный сервер, следить за его живостью и разбираться с деплоем Node.js-приложения. Один переключатель в настройках Supabase закрывает задачу быстрее, чем успеешь дочитать документацию Socket.io.
Переходить на Socket.io имеет смысл в двух случаях. Первый — когда игровая механика становится сложнее, чем «строка в таблице обновилась, разошли её всем»: матчмейкинг, приватные каналы внутри команды, серверная проверка спорных ситуаций, кастомные события с логикой, которую managed-сервис не покрывает по замыслу. Второй — когда проект сознательно уходит от привязки к Supabase, например переезжает на другую базу или комбинирует несколько источников данных, а Realtime как часть экосистемы Supabase в новую архитектуру просто не вписывается.
Если ни один из этих двух случаев не про твой проект — вопрос не «Socket.io или Supabase Realtime», а «зачем вообще тратить время на второй сервер, если первый уже решает задачу». Управляемый сервис существует ровно затем, чтобы не изобретать инфраструктуру заново там, где в этом нет необходимости.
Как выглядит минимальный сервер на Socket.io
Чтобы разница ощущалась не только на словах, вот из чего физически состоит Socket.io-сервер в общих чертах — без точного синтаксиса конкретной версии библиотеки, а как схема того, что происходит.
Сервер запускает обычное Node.js-приложение и поднимает на нём Socket.io поверх HTTP-сервера. Дальше он слушает подключения: каждый браузер игрока, открывший игру, устанавливает соединение и получает уникальный идентификатор сокета. Сервер может сразу добавить это соединение в комнату — например, по идентификатору партии, который пришёл в ссылке.
Дальше сервер описывает обработчики именованных событий — что делать, когда от клиента пришло сообщение direction или ate. Внутри обработчика можно делать что угодно: проверить состояние партии, обновить счёт, решить, кто первым съел яблоко, и только после этого разослать результат участникам комнаты командой вроде io.to('room:abc123').emit('ate', payload). Именно этот шаг — «сервер сам решает, что рассылать, а не просто ретранслирует чужое сообщение» — и есть та серверная логика, которой у managed Broadcast-канала нет по умолчанию.
Клиентская часть на стороне браузера подключается к серверу по адресу, где он развёрнут, подписывается на нужные события через socket.on(...) и отправляет свои через socket.emit(...). Это зеркально тому, как в статье про Supabase Realtime браузер подписывался на канал и слушал Broadcast-события — только источник теперь не облако Supabase, а твой собственный сервер.
Ключевая вещь, которую стоит понять до того, как просить ИИ-ассистента писать код: Socket.io-сервер — это отдельное приложение, живущее отдельно от фронтенда игры. Игра, собранная в предыдущих модулях курса, — это статические файлы, которые открываются в браузере. Socket.io-сервер — постоянно работающий процесс, который эти файлы не раздаёт, а только принимает и рассылает события. На практике это значит два отдельных деплоя: сайт с игрой — как раньше, а сервер реального времени — отдельно, туда, где может работать долгоживущий Node.js-процесс.
Ещё одна деталь, которая пригодится при первом знакомстве с библиотекой: помимо комнат у Socket.io есть namespaces — отдельные пространства подключений на одном сервере, каждое со своим набором обработчиков событий. Для «Змейки» разница обычно не нужна: одного набора комнат достаточно, чтобы развести партии между собой. Namespaces полезны, когда на одном сервере живут разные, не связанные друг с другом типы соединений — например, игровой канал и отдельный административный канал для дашборда с метриками. Начинать проще с комнат, а к namespaces возвращаться только тогда, когда для этого появится конкретная причина.
Если решил остаться на Supabase
Если после сравнения выбор в пользу Supabase Realtime — это разумный дефолт для учебного проекта и для большинства реальных приложений с несложным мультиплеером, — стоит не забывать про безопасность данных, которые летают через каналы. Realtime работает поверх той же базы Postgres, что и остальной Supabase, а значит на неё распространяются те же политики доступа. Как их настроить, чтобы игрок не мог прочитать или изменить чужие данные через тот же канал, разобрано в статье про RLS-политики Supabase — это стоит сделать в любом случае, даже если Realtime используется только для таблицы рекордов.
Частые ошибки при переходе на свой сервер
Ошибка 1. Разворачивать Socket.io-сервер там же, где статический сайт
Обычный хостинг для статики — тот же, что использовался для игры на GitHub Pages, — не держит постоянный Node.js-процесс. Socket.io-серверу нужен хостинг, который умеет запускать и держать живым долгоживущее приложение, а не просто отдавать файлы по запросу.
Ошибка 2. Не настроить автоматический перезапуск процесса
Если сервер упадёт ночью без сторожа, который его поднимет обратно, все игроки останутся без соединения до утра, пока кто-то не заметит и не перезапустит вручную. Большинство хостингов для Node.js умеют перезапускать упавший процесс сами — эту настройку нужно проверить заранее, а не после первого падения.
Ошибка 3. Считать, что клиент Socket.io совместим с любым WebSocket-сервером
Socket.io добавляет собственный протокол поверх WebSocket — переподключение, именованные события, fallback на long-polling. Клиент Socket.io разговаривает по этому протоколу, а не по голому WebSocket, поэтому подключить его к серверу, который не использует Socket.io на своей стороне, не получится напрямую.
Ошибка 4. Доверять клиенту то, что должен решать сервер
Если браузер игрока сам решает, съедено ли яблоко, и просто сообщает об этом остальным, читер может отправить любое событие вручную — например, «я съел яблоко» без реального попадания. Серьёзные решения — кто выиграл, кто первым забрал объект, сколько очков начислить — должен проверять и подтверждать сервер, а не пересылать слепо то, что прислал клиент.
Ошибка 5. Забыть про переподключение при обрыве связи
Мобильный интернет и Wi-Fi время от времени рвут соединение на секунду-другую. Socket.io умеет переподключаться автоматически, но состояние игры на сервере при этом может быть уже не тем, с которым клиент расстался. Стоит заранее продумать, что происходит при переподключении: сервер присылает актуальное состояние партии заново, а не ждёт, что клиент сам его восстановит.
Чек-лист «свой сервер реального времени готов»
- Node.js-сервер с Socket.io развёрнут на хостинге, который держит постоянный процесс.
- Настроен автоматический перезапуск сервера при падении.
- Сервер решает спорные игровые ситуации сам, а не полагается на данные от клиента.
- Комнаты партий создаются и закрываются по понятной логике, без утечки памяти на старые партии.
- Продумано поведение при обрыве и восстановлении соединения.
- Есть способ узнать, что сервер упал, не дожидаясь жалоб игроков.
- Проверено, что клиентская библиотека Socket.io на фронтенде совпадает по версии с серверной.
- Учтено, потребуется ли в будущем несколько копий сервера и как между ними синхронизировать события.
Частые вопросы
Что выбрать для «Змейки» — Socket.io или Supabase Realtime?
Для таблицы рекордов и простого мультиплеера, разобранного в статье про Supabase Realtime, managed-сервиса достаточно и он проще в реализации. Socket.io стоит рассматривать, только если механика усложняется до уровня, который Realtime не покрывает по замыслу — матчмейкинг, приватные каналы, серверная проверка сложных сценариев.
Можно ли использовать оба варианта в одном проекте?
Технически да: например, Supabase закрывает аккаунты, данные и таблицу рекордов, а отдельный Socket.io-сервер обслуживает только конкретную сложную игровую механику. Но это усложняет архитектуру — два разных канала реального времени в одном проекте нужно синхронизировать между собой, и без явной необходимости так делать не стоит.
Нужен ли отдельный хостинг для Socket.io-сервера?
Да. Это постоянно работающий Node.js-процесс, а не статические файлы, поэтому обычный хостинг для сайтов не подойдёт — нужен сервис, который держит живой процесс и умеет его перезапускать. Принципы такого деплоя разобраны в статье про Amvera для новичка.
Работает ли Socket.io без базы данных вообще?
Да, если состояние не нужно сохранять между перезапусками сервера — например, пока партия активна, всё может жить в памяти процесса. Но как только нужно хранить рекорды, профили игроков или что-то ещё за пределами одной сессии, база данных всё равно понадобится, и Socket.io тут не диктует, какая именно.
Сложнее ли Socket.io в освоении, чем Supabase Realtime?
Да, по объёму работы точно сложнее: помимо кода событий нужно поднять сервер, задеплоить его отдельно от игры и следить за его состоянием. Supabase Realtime всю эту часть уже сделал за тебя, поэтому порог входа у него заметно ниже.
Что будет, если Socket.io-сервер упадёт во время игры?
Все подключённые к нему игроки потеряют соединение в реальном времени — обычно клиент попытается переподключиться автоматически, но пока сервер не поднимется снова, обновления не будут доходить. Поэтому автоматический перезапуск упавшего процесса — не опциональная настройка, а обязательная часть развёртывания такого сервера.
Нужно ли самому писать проверку прав доступа в Socket.io?
Да. У Supabase Realtime доступ регулируют RLS-политики базы, а у Socket.io такой встроенной модели нет — сервер должен сам проверять, кто подключился и что ему разрешено, в каждом обработчике событий. Для учебного проекта это не критично, для продакшена с реальными пользовательскими данными — обязательная часть разработки, а не факультативная.
Стоит ли начинать проект сразу с Socket.io, чтобы не переделывать потом?
Обычно нет. Начинать разумнее с того, что решает задачу быстрее — то есть с Supabase Realtime, — а переходить на свой сервер, когда механика реально упрётся в его ограничения. Переписать канал доставки событий на Socket.io позже, когда логика игры уже понятна, проще, чем с самого начала обслуживать инфраструктуру, которая, возможно, никогда не понадобится в таком объёме.
Заключение
Supabase Realtime и Socket.io решают одну задачу — доставку событий в реальном времени — но с противоположных концов. Realtime снимает с тебя вопрос инфраструктуры и отдаёт взамен ограниченную, но достаточную для большинства задач модель «изменилась строка — разослали событие». Socket.io отдаёт тебе полный контроль над сервером и логикой в обмен на то, что этот сервер теперь твой — со своим хостингом, перезапуском и мониторингом.
Для учебной «Змейки» и для большинства реальных приложений с несложным мультиплеером верный выбор — Supabase Realtime, и статья про него в этом курсе остаётся основным путём. Socket.io стоит держать в уме как инструмент на случай, когда игровая механика перерастёт возможности managed-сервиса или проект осознанно откажется от привязки к Supabase — тогда библиотека для своего сервера окажется на своём месте, а не станет лишней инфраструктурой ради инфраструктуры.
Читай дальше
Все статьиНе просто статьи — тебя доведут до результата
В практикуме за 2499 ₽ рядом живая команда практикующих разработчиков и маркетологов: ведём по шагам до твоего работающего приложения. Не «ролики и сам разбирайся» — помогаем на каждом затыке.
Перейти к практикуму