Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 22 из 25 ~16 мин +20 XP

Отладка: wscat, curl и логи соединений

Отладка websocket начинается не с серверного кода, а с трёх инструментов: curl показывает рукопожатие как обычный HTTP-обмен, wscat разговаривает с сервером из терминала, а DevTools раскрывает каждый кадр по времени. Здесь разберём, что отвечают 400 и 403 при подключении, что скрывается за кодом 1006 и какие логи на сервере полезны, а какие — спам.

Содержание урока · 9 разделов

Клиент не подключается, экран молчит, консоль браузера пуста — и первое желание у большинства в этот момент перечитать код Upgrader'а. Зря: websocket-соединение — это сначала HTTP-запрос и HTTP-ответ, и вся диагностика отказа лежит в коде ответа и заголовках. Вопрос «кто виноват» раскладывается по трём этажам, и на каждом этаже свой инструмент. Не дошло рукопожатие — виноват HTTP-слой, и его показывает curl за одну команду. Рукопожатие прошло, а кадров нет — виноват протокол, и с ним разговаривает wscat из терминала. Кадры ходят, но клиент их теряет — смотрим фреймы в DevTools и логи read error на сервере. За вечер вы проходите эти три этажа сверху вниз и находите место поломки быстрее, чем открывается дамп трафика. Они же покрывают большинство обращений «не работает real-time»: остаток — сетевые политики инфраструктуры, и их лечат конфигом прокси, а не кодом сервера.

Отладка websocket: рукопожатие глазами через curl#

Рукопожатие — обычный GET с несколькими обязательными заголовками, поэтому его можно показать curl'ом и прочитать, как читают ответ любого API. Три заголовка обязательны: Connection: Upgrade, Upgrade: websocket и Sec-WebSocket-Version: 13, плюс Sec-WebSocket-Key — случайная строка, из которой сервер обязан вычислить Sec-WebSocket-Accept. Успех выглядит как HTTP/1.1 101 Switching Protocols: соединение переключено, дальше по нему идут кадры, и curl будет ждать их бесконечно — флаг -N отключает буферизацию, а Ctrl-C возвращает терминал. Самое ценное в этом упражнении — увидеть код отказа: 400 значит, что сервер не узнал websocket-запрос, 403 — что Upgrader его узнал, но не пустил. Обе цифры разобраны ниже, после таблицы.

curl: полный рукопожатие в одну команду

GO
curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  http://localhost:8080/ws

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Значение пары Key и Accept взято из примера RFC 6455: сервер вычисляет Accept как Base64 от SHA-1 ключа и константы GUID, и gorilla делает это по стандарту. Флаг -i показывает заголовки ответа, -N не буферизует вывод. Если ответ 101 — транспорт и Upgrader невиновны, ищите поломку выше по стеку, в кадрах.

wscat: собеседник для сервера#

curl подтверждает транспорт, но не умеет разговаривать протоколом. Для диалога ставится wscat — утилита на Node, которая открывает соединение и превращает терминал в чат: каждая введённая строка уходит кадром, каждый входящий кадр печатается в ответ. Установка — npm install -g wscat, запуск — wscat -c ws://localhost:8080/ws. Заголовки передаются флагом -H, и это способ проверить авторизацию из тринадцатого урока без браузера: -H Authorization: Bearer с настоящим токеном. wscat показывает и служебные события — Connected, Disconnected с кодом и причиной, — что делает его удобным для проверки heartbeat из шестого урока: открыли соединение, ничего не трогали и смотрим, жив ли ping-цикл. Инструменты разработчика подобного рода экономят минуты: сервер проверяется без фронтенда, сборки и открытий вкладок.

Сессия wscat против сервера чата

GO
wscat -c ws://localhost:8080/ws -H "Authorization: Bearer eyJhbGciOi..."

Connected (press CTRL+C to quit)
> {"type":"join","room":"general"}
< {"type":"system","text":"u42 joined general"}
> {"type":"chat","room":"general","text":"привет"}
< {"type":"chat","user":"u42","text":"привет"}

Стрелки внутри сессии: строка с угловой скобкой — то, что ушло на сервер, с меньшей — то, что пришло с него. Вводим конверты из десятого урока как сырой JSON: сервер, который отвечает на join системным сообщением и на chat эхом, проверен без браузера. Ctrl-C закрывает соединение по close handshake — в логах сервера это чистый уход, а не обрыв.

DevTools: вкладка Network и WS-фреймы#

Браузерные инструменты разработчика дают то, чего нет у curl и wscat: хронологию кадров конкретной вкладки. Откройте Network, отфильтруйте по типу WS, кликните соединение — появится панель сообщений, где каждый кадр лежит строкой со временем, направлением и размером: исходящие и входящие подсвечены разными цветами, а служебные ping и close видны как отдельные записи. Здесь диагностируются вещи, которых сервер не видит: дубликаты соединений после багованного reconnect из семнадцатого урока (две строки вместо одной), пачки из двадцатого урока с массивом items внутри, отсутствие close frame при закрытии вкладки. Клик по кадру раскрывает JSON, и расследование «почему интерфейс не отрисовал сообщение» заканчивается за минуту: либо кадра нет — виноват сервер, либо кадр есть — виноват обработчик на клиенте. В Firefox панель называется Frames, механика та же: фильтр по WS, клик по соединению, список кадров с направлением.

Молчащие соединения: где умирает heartbeat#

Отдельный класс проблем — соединение, которое числится открытым, но ничего не передаёт: NAT дома и корпоративный брандмауэр убивают тихое TCP-соединение по таймауту простоя, обычно через несколько минут тишины. Симптом узнаваем: клиент работал час, ушёл в фон, вернулся к молчащему интерфейсу, а onclose пришёл через минуты после фактической смерти сокета. Диагностика двухсторонняя: DevTools показывает последние ping и их время, логи read error на сервере дают момент обрыва, и разрыв между отметками — то самое окно молчания. Лечение — heartbeat из шестого урока: ping раз в тридцать-шестьдесят секунд держит соединение помеченным как живое и переводит смерть из «неизвестно когда» в «не позже одного периода пинга». Проверяется вручную за две минуты: wscat, брошенный на Wi-Fi, и телефон на мобильном интернете дают два разных времени жизни, и оба становятся длиннее после включения пинга.

Логи соединений: read error без спама#

Серверный лог — последний этаж, и его главная задача — не показать всё, а отделить норму от поломки. В readPump любая ошибка чтения заканчивается возвратом, и наивный лог печатает каждую: на мобильной сети соединения рвутся по несколько раз в минуту, и лог превращается в поток abnormal closure 1006 — это норма, а не инцидент. Полезная строка содержит userID, код закрытия и причину, а уровень зависит от кода: 1000 и 1001 — штатные уходы, их логируют на уровне debug; 1006 — обрыв, info; неожиданные коды вроде 1009 при обычных сообщениях — warn, потому что намекают на лимит из четырнадцатого урока. Записывать тела сообщений в лог не стоит: там персональные данные, а для разбора протокола достаточно типа конверта.

Симптом, причина и инструмент#

СимптомТипичная причинаИнструмент
400 Bad Request при подключениизапрос без заголовков Upgrade дошёл до Upgradercurl -i: сравнить заголовки
403 при подключенииCheckOrigin не пустил Origincurl с -H Origin и лог в ветке
101 получен, кадров нетклиент не в комнате или не подписанwscat: послать join и смотреть
onclose с кодом 1006обрыв без close handshakeлоги read error на сервере
Кадры приходят, интерфейс молчитобработчик не знает тип конвертаDevTools: раскрыть кадр и сверить type
Кадров в два раза больше, чем сообщенийдубль соединения после reconnectDevTools: две строки в списке WS

Проверка: что означает 403

Upgrader ответил на рукопожатие кодом 403. Что это чаще всего значит?

Проверка: загадочный 1006

Браузер вызывает onclose с кодом 1006. Что произошло?

curl без заголовков Upgrade

Прочитайте код и выберите, что он выведет:

// сервер: обычный Upgrader из четвёртого урока
// var upgrader = websocket.Upgrader{}
// mux.HandleFunc("/ws", func(w, r) { upgrader.Upgrade(w, r, nil) })

curl -i http://localhost:8080/ws

Соберите рукопожатие curl'ом

Впишите три пропуска: два обязательных заголовка и версию протокола.

curl -i -N \ -H ": Upgrade" \ -H "Upgrade: " \ -H "Sec-WebSocket-Version: " \ -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ http://localhost:8080/ws

Порядок расследования

Расставьте шаги: от кода ответа до логов сервера.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: отладка

Термин

101 Switching Protocols

нажмите, чтобы перевернуть
Определение

Успешное рукопожатие: HTTP-соединение переключено на websocket. Всё, что ниже 101, — отказ до кадров.

1 / 5

Три этажа отладки встали на место: curl отвечает за рукопожатие, wscat — за диалог, DevTools и логи — за кадры и их потери. Коды 400 и 403 больше не загадка, а указание на конкретную ветку. Когда соединение стабильно, следующий вопрос — что будет, когда клиентов станет больше одного сервера: двадцать третий урок про масштабирование строит мост между инстансами. Эхо-сервер для упражнений описан в четвёртом уроке, ping-цикл и 1006 — в шестом уроке про heartbeat, лимит кадра и 1009 — в четырнадцатом, а Origin и CheckOrigin — в пятнадцатом уроке. Токен в заголовке для wscat берётся из тринадцатого урока про авторизацию. Структурированные логи с уровнями удобно вести через курс по zap — в проде логов много, и без уровней они бесполезны. План курса — на странице курса WebSocket.

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • Рукопожатие — обычный HTTP: curl с заголовками Upgrade показывает код ответа и Accept за одну команду.
  • 101 означает, что транспорт жив: дальше ищите поломку в кадрах, а не в Upgrader'е.
  • wscat -c проверяет протокол без браузера: конверты вводятся сырым JSON, заголовки летят через -H.
  • DevTools на вкладке WS раскрывает каждый кадр по времени и направлению: дубликаты и пачки видны сразу.
  • 400 — сервер не понял рукопожатие, 403 — CheckOrigin не пустил Origin, 1006 — обрыв без close handshake.
  • Лог read error сортируется по кодам: штатные уходы в debug, обрывы в info, странные коды в warn.

Домашнее задание

Поднимите эхо-сервер из четвёртого урока и пройдите три этажа: получите 101 через curl с заголовками Upgrade, прогоните конверты join и chat через wscat -c, найдите кадры на вкладке WS в DevTools. Затем ужесточите CheckOrigin под конкретный домен и добейтесь 403 с чужим Origin, добавив в ветку лог с отвергнутым значением. В readPump заведите строку лога с userID и кодом закрытия и убедитесь, что Ctrl-C в wscat даёт чистый 1000, а обрыв Wi-Fi — 1006.

Личные заметкиПусто
Хранятся только в вашем браузере (WebSocket · Отладка: wscat, curl и логи соединений)

Вопросы и ответы по уроку

Можно ли обойтись без wscat, если есть браузер?

Можно, но дороже: вкладка требует собранного фронтенда и правильного Origin, а wscat работает из любой папки и с любым заголовком. В CI и на сервере без графики браузера нет вовсе, и терминальный клиент остаётся единственным способом поговорить с сокетом. Для скриптов есть ещё вариант на Go из двадцать первого урока — Dialer в main-функции.

Почему нельзя логировать тела сообщений?

В телах лежат персональные данные: тексты переписки, имена, иногда токены, случайно присланные клиентом. Логи живут дольше запросов, копируются в агрегаторы и читаются всеми, у кого есть доступ к инфраструктуре, поэтому тело в логе — утечка по определению. Для отладки протокола хватает типа конверта, userID и размера кадра.

Как поймать рукопожатие между клиентом и прокси?

curl до и после прокси: сравните заголовки запроса и код ответа на обоих участках. Если напрямую 101, а через прокси 400 или обрыв — прокси вырезает заголовки Upgrade или держит соединение открытым для буферизации, и это настраивается на его стороне. Помните, что некоторые прокси по умолчанию не пропускают websocket и требуют явного включения.

Термины урока

Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.

Следующая ступень учебного пути

RabbitMQ25 уроков~11 чпрактика25 уроков уже доступны

RabbitMQ в Go: очереди сообщений на amqp091-go

Интерактивный курс RabbitMQ в Go: обменники, очереди, ack, prefetch, dead letter и RPC. 25 уроков amqp091-go с примерами кода и итоговым проектом.

  • Подключаться к RabbitMQ из Go и управлять connection и channel
  • Объявлять очереди, обменники и привязки с нужными свойствами
  • Публиковать и потреблять сообщения с ручными ack
  • Настраивать prefetch и справедливое распределение между воркерами