Сьогодні незамінними для нас стали застосунки, які дають змогу миттєво обмінюватися повідомленнями та слідкувати за новинами онлайн. Вебсокети — один з інструментів, що дозволяє розробникам реалізувати такі додатки.
Що таке вебсокети
WebSocket — це двонаправлений повнодуплексний протокол зв’язку між клієнтом та сервером. Що це значить? На відміну від HTTP-протоколу, який працює за принципом «запит від клієнта — відповідь від сервера», у вебсокетах і сервер, і клієнт можуть надсилати один одному повідомлення. Кожна сторона комунікації здатна одночасно отримувати та відправляти дані.
У вебсокетах обмін повідомленнями проходить через єдиний канал зв’язку. Він залишається відкритим протягом усієї комунікації, а за необхідності будь-яка зі сторін може його закрити.
Відмінності від HTTP-протоколу
Структура WebSocket протоколу
Вебсокет протокол існує як надбудова TCP. Специфікація визначає дві URI схеми для вебсокетів: ws:// для нешифрованого з’єднання та wss:// відповідно — для шифрованого. Протокол складається з початкового хендшейку і безпосередньо обміну даними.
Handshake
У код-блоці нижче можете поглянути, як виглядає handshake з боку клієнта. Тут присутній header Connection: Upgrade. Також видно, який саме upgrade пропонується, — Upgrade: websocket:
Сервер підтверджує handshake статус кодом 101 — зміна протоколу, і так само надсилає деталі про новий connection:
Обмін даними
Дані надсилаються у вигляді фреймів із заданим типом. Кожне повідомлення може складатися з одного чи більше фреймів. Усі вони повинні бути однакового типу. Такими типами можуть бути текст, бінарні дані та контрол фрейми, призначені не для передачі даних, а для службових сигналів на рівні протоколу. Наприклад, що з’єднання потрібно закрити.
Socket.IO
Це один з найпопулярніших інструментів для роботи з вебсокет протоколом. Він складається з вебсокет сервера та клієнтської бібліотеки. Спершу вони були реалізовані на JavaScript, але згодом з’явилися реалізації на багатьох інших мовах програмування. Socket.IO реалізує додатковий функціонал, якого немає у чистому WebSocket. Наприклад, автоматичні перепідключення, якщо з’єднання втрачене; або fallback до HTTP long polling, якщо WebSocket протокол не підтримується; а також впровадження неймспейсів і кімнат.
Namespaces
Необхідні для розділення відповідальності (separation of concerns) у межах одного застосунку. Socket.IO дозволяє створювати декілька неймспейсів, які поводитимуться як окремі канали комунікації. Водночас під капотом вони будуть використовувати одне й те саме з’єднання. Розділяти на неймспейси може бути логічно за модулями у програмі або, наприклад, за спільними пермішенами.
Кімнати
Це другий рівень ієрархії. У кожному неймспейсі, у тому числі дефолтному, можна створювати окремі канали — так звані кімнати, до яких клієнти можуть долучатися та виходити з них. Таким чином, ви можете транслювати повідомлення у «кімнату», і його отримають всі клієнти, котрі приєдналися до неї. Це може бути зручно для того, щоб одночасно відправити повідомлення групі користувачів або зібрати повідомлення з декількох девайсів для одного юзера.
Альтернативи сокетам
HTTP Polling
Якщо нам постійно потрібно слідкувати за оновленням інформації на сервері, в деяких випадках це можна реалізувати за допомогою HTTP-протоколу — у вигляді HTTP Рolling.
Існує декілька видів HTTP Polling:
- short polling — простий підхід, однак вважається поганою практикою. Клієнт постійно перепитує сервер, чи готова запитувана інформація. Сервер обробляє реквести щойно вони надходять і відповідає пустим респонсом, якщо дані не готові. В такому випадку велика кількість реквестів перевантажує сервер.
- long polling — клієнт надсилає один реквест на сервер і очікує відповіді. Сервер своєю чергою затримує реквест, допоки потрібні дані не стануть доступними чи у реквеста не закінчиться заданий таймаут. За оптимальних умов ми отримуємо відповідь щойно на сервері змінюються дані й не створюємо так багато трафіку, як при short polling. Однак на практиці доволі складно налаштувати відправку реквестів так, щоб вони не були ні занадто частими (і в більшості випадків не отримували відповіді), ні занадто повільними, тобто довго затримувались на сервері й марно витрачали ресурси.
У результаті HTTP polling — не дуже зручний підхід, якщо нам вкрай важливо отримувати інформацію в реальному часі.
HTTP Streaming & Server-sent events
Гарний варіант для subscribe-only випадків. Наприклад, для підписки на оновлення стрічки новин чи надсилання сповіщень у браузер. Клієнт робить один HTTP-реквест, встановлюючи з’єднання, а сервер відповідає серією респонсів у міру того, як у нього з’являються відповідні дані. Респонси відправлятимуться, допоки клієнт не закриє з’єднання. Таким чином, зникає необхідність відкривати й закривати з’єднання для кожної пари реквест-респонс.
Недоліки такого підходу:
- Не гарантує миттєвої доставки повідомлень: з’єднання може бути перерване, реквест може потрапити в чергу інших HTTP-реквестів.
- Клієнт не може відправляти дані на сервер, що унеможливлює використання цього підходу в застосунках, які потребують справжньої інтерактивності. Тобто там, де клієнт і сервер можуть надсилати один одному дані без додаткових проміжних запитів.
Застосування вебсокетів
Оскільки вебсокети підтримують двонаправлений зв’язок, вони ідеально підходять для ситуацій, коли потрібен швидкий двосторонній обмін даними. Це онлайн-ігри, чати, фінансові застосунки, новини, обмін даними з IoT-девайсами.
Не варто використовувати вебсокети, якщо немає потреби в інтерактивності, немає постійного двостороннього трафіку, а натомість — є високі вимоги щодо безпеки. Адже довготривале відкрите з’єднання додає чимало ризиків.
Security
Як будь-яка комунікація в мережі, комунікація по вебсокет протоколу має певні вразливості.
Виділяють такі можливі напрями атак:
- XSS (cross site scripting), SQL-ін’єкції — впровадження шкідливого коду у повідомлення.
- Man-in-the-middle — перехоплення інформації з каналу зв’язку.
- DoS (Denial of Service) — відправка великої кількості запитів, аби зробити ресурс недоступним користувачам.
- Неавторизований доступ до інформації.
Як захиститися:
- Валідувати дані.
- Використовувати шифроване з’єднання — wss://.
- Впровадити rate limiting — обмеження по кількості повідомлень за одиницю часу.
- Додати аутентифікацію на етапі хендшейку. Один із варіантів — використовувати ticket-based аутентифікацію. Це коли клієнт перед апгрейдом з’єднання контактує із HTTP-сервером, який генерує ticket з необхідною інформацією про юзера. Далі клієнт надсилає цей ticket WebSocket-серверу, котрий валідує його і тільки потім надає згоду на з’єднання.
Вебсокети в Python
Серед бібліотек, які існують у Python та активно розвиваються, можна згадати такі:
- python-socketio — фреймворк-незалежна імплементація Socket.IO для Python, має синхронний та асинхронний варіанти;
- Flask-SocketIO — інтеграція Socket.IO та Flask;
- Django Channels — розширення для Django-фреймворку, додає підтримку вебсокет протоколу, HTTP long polling, MQTT; дозволяє обрати між синхронним та асинхронним варіантом імплементації;
- Autobahn|Python — реалізація вебсокет протоколу і WAMP (web application messaging protocol) на Twisted та asyncio;
- websockets — бібліотека для створення вебсокет серверів і клієнтів на основі вебсокет протоколу. Дефолтна імплементація побудована на asyncio, але дає змогу використовувати threading за бажанням;
- websocket-client — низькорівневий вебсокет клієнт для Python на основі чистого вебсокет протоколу.
WebSocket протокол дає можливість створювати інтерактивну комунікацію між клієнтом і сервером без необхідності постійно пінгувати сервер. Таким чином ми економимо час, прискорюємо роботу застосунку і можемо підтягувати зміни в режимі реального часу.
Як і з будь-якою технологією, вебсокети незамінні там, де вони доречні, але їх застосування не завжди потрібне. Часом воно несе додаткові ризики й потребує окремих знань для правильного, безпечного застосування.