Почему не работает websocket

Капризы WebSocket и при чём здесь костыли

Протокол WebSocket, как и любые другие протоколы, имеет свои преимущества и свои недостатки. Именно из-за последних появляются новые версии протоколов, новые протоколы и новые подходы к реализации всего, что вокруг них, а конкретно — клиентских и серверных приложений.

Хороший сервер — это такой сервер, который учитывает особенности протокола. Хороший клиент — то же самое. Здесь под «сервер» и «клиент» подразумевается именно имплементация протокола в виде конкретного софта или библиотеки.

Изучив WebSocket с разных сторон, образовался следующий список проблем, которые в WebSocket есть, а в других популярных и используемых местах их не имеется, либо не несут важных или критичных проблем. Но давайте сперва изучим другую сторону WebSocket, ту, которая лежит в его основе: TCP-keepalive.

TCP KeepAlive

Что такое KeepAlive? Это способ оставлять TCP-соединение открытым долгое время.

Как это делается? Раз в определенный период происходит обмен специальными пакетами, которые озаглавлены в документации «keepalive probes». Выполняется это с помощью PSH- и RST-пакетов.

Как долго может жить KeepAlive соединение? Существует максимальный временной интервал между пакетами с данными, в течение которого соединение может продолжать жить. Если обмен данными происходит в этот период, то следующий период начинается сначала, т.е. KeepAlive-соединение периодически (пусть и редко), обменивающееся внутри себя данными, может жить довольно долго.

В ядре Linux вокруг этого есть три настройки:

Здесь показаны их значения по умолчанию. net.ipv4.tcp_keepalive_time — это как раз максимальное время между пакетами с данными.

net.ipv4.tcp_keepalive_intvl — интервал обмена пакетами «keepalive probes» по умолчанию 75 секунд. «net.ipv4.tcp_keepalive_probes» — это количество возможных неотвеченных «keepalive probes» пакетов — по сути попыток возобновить соединение.

Как переводится соединение в режим KeepAlive? На языке C очень просто:

Так же можно использовать для своих сокетов свои величины intvl и probes:

В комментариях к этим опциям в документации отмечено: «This option should not be used in code intended to be portable.» Эти опции требуется применять очень аккуратно: это настройки, которые ДОЛЖНЫ быть одинаковы и на клиенте, и на сервере.

Если величины, например, tcp_keepalive_intvl разойдутся, то клиент, работающий по умолчанию, и сервер, работающий в режиме tcp_keepalive_intvl=25, будут иметь разную информацию о соединении. В этом примере 75/25 — клиент может долго думать, что соединение еще открыто, когда сервер будет уже знать о том, что оно закрылось. Это будет приводить к отправке пакетов в уже закрытое на том конце соединение и потере пакетов.

Но, тем не менее, если вы контролируете и клиентский код, и серверный, то эти величины для вас доступны.

Например, величина 75 секунд может быть слишком велика в определенных ситуациях, например, когда данные требуется передавать быстро, и при их недоставке так же быстро требуется получить ответ об ошибке.

WebSocket

Websocket использует TCP-KeepAlive соединения. В конкретном применении это дает как плюсы, так и минусы. Плюсы очевидны:

  1. Постоянное соединение, которое можно просунуть в Web-браузер, от которого наступает счастье и на FrontEnd, и на BackEnd в Web-приложении или мобильном приложении, работающем с сервером.
  2. Кратковременное отсутвие связи вообще не обрывает такое соединение.
  3. Позволяет работать асинхронно, вместо привычной для web-а работы в режиме запрос-ответ.

Проблемы WebSocket в современном использовании менее очевидны:

Молчаливый отвал соединения. При отправке пакета в WebSocket вы не узнаете о том, доставлен он или нет, пока не пройдет 75 секунд таймаута. Сам протокол ничего не расскажет об этом, а величина по умолчанию 75 секунд велика для быстрого взаимодействия. Например, тот же чат, работающий поверх реальной сети, где клиенты могут «выпадать из сети», усложняется архитектурно: сервер должен внутри себя иметь очередь, он должен иметь unack-буфер и логику переотправки сообщений. Часто по WebSocket разработчики запускают «свои самодельные ping-и» с целью понять, жив он или нет.

Смена сети клиентом. В сетях мобильной связи часто бывает так, что ваш внешний IP, NAT и вообще сеть, в которой присутствует мобильное устройство, меняются. Это даже не зависит от оператора: вы пришли домой, подключился Wi-Fi и весь websocket рухнул очень забавным образом:

  1. Сервер ничего не знает о вашей смене адреса, если клиент не закрыл соединение при переподключении к другой сети.
  2. Сервер продолжает отправлять ваши приватные данные на старый IP, а вас там уже нет. Там уже кто-то другой их получает и это утечка (только не говорите мне, что SSL решает эту проблему, к сожалению, он решает её на уровне криптографии, но не на уровне доступа. Иногда сам факт передачи Вам какой-либо информации от известного получателя, без деталей, являтеся ценным и это не редкость, а ежедневный кейс при борьбе с утечками информации)
Читайте также:  Сколько платятся алименты если не работаешь

Хотите пример такой утечки? Легко:

  1. Боб говорит Алисе что не пользуется услугами ТТТ-Банка и не может перевести ей денег.
  2. Боб покинул 4G сеть и ушел в Wi-Fi.
  3. Алиса знала, что на этом IP минуту назад был Боб.
  4. Сейчас IP Боба достался Алисе. (это не маловероятная ситуация, а вполне подстраиваемая, если сделать для этого некоторые усилия)
  5. Алиса получает пакет от сервера его банка «ТТТ-банк». Алиса знает, что IP отправителя принадлежит TTT-Банку (через whois, путем запросов на 443/80 порт и визуальное наблюдение API и т.п.). Теперь Алиса знает, что Боб пользуется мобильным приложением ТТТ-Банка и, скорее всего, у него есть карта.
  6. Итог:
    1. Боб пойман на вранье, чего Боб никак не хотел.
    2. Личная информация о том, каким банком пользуется Боб, известна Алисе, отчасти это уже может быть утечкой тайны (например, банковской)
    3. SSL не спас, что и ожидалось, т.к. ssl существует выше уровня IP, а IP отправителя (банка) известен Алисе.

Повышенная нагрузка на серверы. Клиент ничего не знает о своей смене внешнего IP и сует данные в отпавшее соединение — сервер получает пакеты старого соединения уже с нового IP и отбрасывает их. Если у вас популярное мобильное приложение и, как результат, нагруженный сервер, то маленький чих в сети оператора мобильной связи устроит DDoS атаку на ваш сервер и вам прилетят старые пакеты, реконнекты, данные, вместо того, чтобы просто передать данные 1 раз. Т.е. вам требуется иметь многократный запас пропускной способности на серверах.

Способы решения проблем WebSocket

На данный момент решения для отработки обрыва соединения костыльно-ориентированные:

  • Решать на L7 задачи L4 — гонять «свои пинги» по websocket-у
  • Тюнить сеть и на клиентах, и на серверах путем установки tcp_keepalive_intvl в коде или настройках сервера:
    • Очень чреватый путь, особенно в настройках сервера, т.к. затронет работу всех приложений.
    • Недоступен для мобильных клиентов на Android (если я не прав и на Android есть способ установить setsockopt — поправьте меня, плиз, в комментариях в FB или VK).
    • Требует грамотных админов, разработчиков, разбирающихся в сети, и усложняет продукт на ровном месте.
  • Использовать готовые решения, которые уже гоняют «свои пинги» по L7 и т.п.

Универсальных способов решения проблем утечки информации об отправляемых пакетах при смене мобильным клиентом сети до сих пор нет.

Источник

Не понятно работает websocket (wss). Иногда работает иногда нет. В чем проблема?

Есть сервер node js и есть клиент html5(js). Общение сделал через websocket. Все работает на ура! Позже понадобилось общение через wss. Взял у comodo 3-х месячный бесплатный сертификат. не самоподписный, есть цепочка на 3 верхних сертификата, да и проверка сайта на сертификат говорит что все хорошо. Установил на сервере в nginx скармливаю сертификат в node js:

Пробую подключиться клиентом. и дальше творятся чудеса, браузеры вообще по разному работают: Как я понял нужно чтоб прошло рукопожатие. Сначала вообще ничего не работало.

Далее нашел совет-трюк: перейти по ссылке wss://domain.ru:8082 заменив wss на https. И это сработало! Но не во всех браузерах. в ИЕ не сработало. Собственно это тоже не вариант. обычный пользователь так делать не будет. Собственно если этого не делать, то так ничего и не работает. в хроме это выглядит так:

Websocket connection to wss://domain.ru:8082 failed: Websocket opening handshake was canceled. (opcode-1)

До nodejs скорее всего вообще ничего не доходит т.к. ошибки подключения нет. Да и с трюком nodejs вполне себе начинает отвечать, поэтому думаю дело не в нем. Информации по этой проблеме очень мало, а на русском так вообще не нашел. Хотелось бы понять куда рыть? как дебажить?

UPD: Проверил логи nginx и тоже ничего. Ощущение что браузер ваще ничего никуда не отсылает, ибо не доверяет. Правильное ли предположение? Заранее спасибо!

Читайте также:  Почему не работает хлебопечка лджи

Источник

Как можно определить почему не работает связь с сервером (WebSocket)?

Part 1

Как можно определить почему не работает связь с сервером?

Web-server: OpenServer

Алиас — localhost —- yii333.com

Вообще по разному пробовал, и без алиаса.

Включал/выключал — защитить сервер от внешнего доступа

Фаервол: выключен

Используемая библиотека websocket: socketo.me

Проверка включение websockets на веб-сервере: Всё отрабатывает на отлично!

Running It ¶ Complete, let’s run it and test it. Open up three terminal windows, typing:

$ php bin/chat-server.php $ telnet localhost 8080 $ telnet localhost 8080 In each of the telnet windows, type a message («Hello World!») and see it appear in the other!

всё отлично работает, связь есть, сообщения пересылаются.

Когда перехожу к следующему шагу Next Steps

Next Steps ¶ Now that we have a basic working Chat application, let’s make that work in a web browser (Chrome, FireFox, or Safari [for now]). First, let’s go back to our chat-server.php script. We’re going to utilize another component of Ratchet; the WsServer class:

var conn = new WebSocket(‘ws://localhost:8080’); conn.onopen = function(e) < console.log("Connection established!"); >;

WebSocket connection to ‘ws://localhost:8080/’ failed: Error during WebSocket handshake: Unexpected response code: 400

этот код я отправлял как через консоль браузера, так и через html страничку. Перед тем как отправлять js код, я запускаю сервер через консоль, запущенную от имени администратора php bin/chat-server.php

Так-же, показывает что ошибка произошла в этой строке JS скрипта, если запускать скрипт через html страницу :

Если в адресной строке браузера набрать localhost:8080 то выдает ошибку:

Если в адресной строке браузера набрать localhost6:8080 или другой какой-нибудь адрес сервера, или выключить сервер, то выдаёт ошибку:

Part 2

Связь удалось настроить между сервером и клиентом.

Но, связь с сервером нестабильна. Если мы первый раз попытаемся соединиться через JS скрипт, то получим ошибку 400. Если мы будет обновлять страницу, тем самым перезагружая js скрипт, то примерно с второго/пятнадцатого раза, произойдёт связь с сервером. На сервере в консоли, отобразится лог о том что создан новый коннект. При отправки сообщения на сервер, поведение не менее странное. Мы можем отправить сообщение и сервер его прочитает и отобразит в консоли, а может и вообще не прочитать и ничего не отобразить в консоли. Причину всего этого, пока не удалось выяснить.

Источник

Web UI, WebSocket и проблема самоподписанных сертификатов

Доброго времени суток всем!

В этой заметке я постараюсь рассмотреть проблематику зашифрованного соединения с Web-сервером (HTTPS) и такого же соединения с Comet-сервером с использованием WebSocket (WSS) при использовании самоподписанных сертификатов, а так же варианты для решения данной проблемы.

В данный момент времени я занимаюсь разработкой Web UI к некоему продукту компании, который будет использоваться внутри клиентских корпоративных сетей.

Изначальные условия: доступ к Web UI прямо по IP адресу сервера, желательно использование стандартных портов 80/443, чтобы облегчить деплоймент, ОС сервера — Windows Server 2012.

Comet-сервер нужен нам для организации связи между неким сервисом, который формирует отчеты по заданным параметрам, и непосредственно Web UI, через который клиент отправляет запрос, получает статус о выполнении и завершении задания, а так же для передачи разных сервисных сообщений о событиях (например — пользователь вошел в систему, данные пользователя были изменены администратором и т.п.).

Для Comet-сервера и бэкенда Web UI мы используем Node.JS.

Вообще опыта использования Node.JS у меня особого нет, но как оказалось подобные задачи делать на нем весьма удобно — не вдаваясь в детали на само написание первой версии с глобальным бродкастом сообщений между всеми участниками без «комнат» или «каналов» у меня ушло буквально пару часов. И большая куча времени на инвестигирование модулей Node.JS, на которых можно реализовать WebSocket сервер (Socket.IO, WS) — тут время ушло на исследование стыковки C# серверного компонента и Comet-сервера. Socket.IO нам не подошел — в версии > 1 там реализовано много «поверх», решили по разным причинам использовать более нативную реализацию и взяли модуль WS.

Скажу сразу, что Comet-сервер имеет два интерфейса: первый это обычный HTTP интерфейс (внутренний с авторизацией запросов), по которому происходит авторизация пользователей и компонентов, отправка команд серверу; второй это непосредственно WebSocket интерфейс (WS), к которому подключаются авторизованные пользователи и компоненты для получения сообщений о событиях в системе.

Читайте также:  Как настроить телевизор lg life good

В связке HTTP+WS все естественно работает на «ура» и никаких проблем, но по заданию нам нужен SSL, и соответственно HTTPS+WSS.

Создать самоподписанный сертификат при помощи OpenSSL дело несложное, прикрутить к Web UI тоже буквально пара строчек, к интерфейсу Comet тоже просто.

И вот тут началось самое интересное.

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

Итак, мы идем на тестовый Web UI с эмуляцией клиента по адресу 127.0.0.1 , подтверждаем сертификат, далаем попытку соединения по адресу wss://127.0.0.1:4433/ — и получаем ошибку соединения. Потому что для адреса 127.0.0.1 и порта 4433 сертификат надо подтверждать второй раз. Браузер при этом ну никаких дополнительных сообщений о том, что сертификат нужно подтвердить, не выдает. Просто молча сбрасывает запрос и все, он даже до сервера не доходит.

Из четырех тестируемых браузеров — IE, Firefox, Opera, Chrome — второй раз не спрашивают подтверждения на другой порт по тому же адресу Chrome и Opera, IE и Firefox запрос молча обрубают с выдачей ошибки в консоль.

Поскольку скорее всего у клиента будет IE (все-таки Windows это массовая ОС у корпоративных клиентов), то это большая проблема.

Решение 1

Самое простое решение, которое приходит в голову — это проксировать запросы. В Linux я бы поставил Nginx и проксировал через него, но у нас Windows. Взял уже имеющийся у меня Apache 2.2, настроил SSL и дал вот такую директиву:

То есть запросы на /wss/ пробрасываем к WS интерфейсу, все остальное — на Web UI.

Не заработало. Гугление и прочее выдало что надо Apache 2.4 с модулем mod_proxy_wstunnel. Поставил, подключил — все прекрасно, Web UI выдает запрос один раз, WSS соединение устанавливается без проблем.

Но Apache в данном получается все-таки лишним компонентом, и несколько портит производительность, которую дает асинхронное приложение на Node.JS.

Решил попробовать написать прокси-сервер на Node.JS, не вышло. Если вкратце, то одновременные запросы на любой один порт с поднятием WS и HTTPS интерфейсов HTTPS интерфейс в упор не ловит запросов по WSS протоколу. Совсем. Проксировать отдельно HTTPS на внутренний HTTP и отдельно WSS на внутренний WS проксирует, а вот поймать урл при запросе wss://127.0.0.1/wss/ — никак.

Хорошо, вернемся к первому варианту и двум портам с подтверждением сертификатов.

В Node.JS WSS сервер создается на основе HTTPS-сервера и умеет отдавать некий контент при прямом запросе 127.0.0.1:4433 , чем я и решил воспользоваться:

Ради смеха решил попробовать iframe. В iframe выдается предупреждение… И никаких кнопок для пользователя чтобы подтвердить сертификат. Да и в любом случае я никак не могу отследить, подтвердил там чего пользователь или нет, чтобы потом установить соединение. Прописать в техтребованиях, чтобы пользователь обязательно сходил на два адреса? Тоже как-то некрасиво… AJAX-запрос на другой порт по тому же адресу вообще не работает из-за внутренней безопасности — для Javascript это два разных сайта.

Решение 2

Оказалось несколько бредовым, но весьма рабочим. Итак, нам надо два раза подтвердить сертификат. А что если мы попросим пользователя сначала сходить на HTTPS интерфейс WSS-сервера, а потом после подтверждения сделаем редирект на основной WebUI?

Работает! При запросе 127.0.0.1:4433/ меня теперь просит подтвердить сертификат, потом автоматически перебрасывает в Web UI 127.0.0.1 , где тоже просит подтвердить сертификат, после чего связка HTTPS на одном + WSS на другом порту прекрасно работает и больше ничего не спрашивает.

NB! Выше я ничего не написал про доменные имена. Конечно, их можно использовать, чтобы получить нормальные сертификаты, но у меня есть определенные ограничения в виде корпоративной сети, из которой далеко не всегда есть доступ к центру сертификации, выдавшему сертификат. Да и требовать заводить доменные имена во внутренней сети для продакшена в моем случае это лишняя головная боль. Еще один сервер для Web UI на Linux, например, с проксей в виде Nginx в силу разных причин мы тоже не рассматриваем.

Выводы

Для решения данной проблемы нашлось два пути:

  1. Проксирование с прямого адреса по части url (при помощи Apache или Nginx)
  2. Редирект с HTTPS-интерфейса WS-сервера на основной Web UI с подтверждением сертификатов два раза

Буду рад любым комментариям, может есть еще какие решения.

Источник

Оцените статью