Websocket: інтерактивне майбутнє


У цьому пості ми продовжуємо серію публікацій про взаємодію клієнтської і серверної частини сайту\веб-додатки.

Якщо AJAX використовує, напевно, кожен професійний девелопер, то про websocket багато розробники знають лише з чуток. У чому ж основні «вузьке місце» AJAX? А в тому, що обмін даними може відбуватися тільки за ініціативою клієнта. Адже AJAX – це самий звичайний клієнт-серверний HTTP-запит, який, виходячи з самої ідеології HTTP, не може відбуватися інакше, як за «бажанням» клієнта.

Наведемо простий і зрозумілий приклад. Припустимо, що ми хочемо, щоб наша сторінка подгружала актуальні новини, які могли з’явиться за час перебування користувача на ній. Щоб реалізувати даний функціонал, ми повинні з певною періодичністю запитувати новини сервера. З цією метою на клієнта виконується подібний javascript-код:

function getNews() {
//гіпотетична функція, що повертає Id останньої новини
var last = getLastId();
$.ajax({
type: “POST”,
url: “/getLastnews”,
data: JSON.stringify({“action” : “getLastnews”, “lastId” : last}),
contentType: “application/json; charset=utf-8”,
success: function(data) {
//обробка прийшли від сервера даних
}
});
}
setInterval(getNews(), 5000);
В цьому шматку коду, клієнт повідомляє серверу id останньої наявної у нього новини і запитує оновлення новин кожні 5 секунд (5000 мілісекунд). Неважко здогадатися, що велика частина таких запитів проходить «в холосту», а взаємодія виглядає приблизно так:

Клієнт: «дай мені останні новини»
Сервер: «нових новин немає»
Клієнт: «дай мені останні новини»
Сервер: «нових новин немає»
Клієнт: «дай мені останні новини»
Сервер: «є оновлення новин!»
–передача новин
–висновок новин
Клієнт: «дай мені останні новини
Сервер: «немає нових новин»

При використанні AJAX (та й взагалі HTTP) у багатьох розробників виникало закономірне питання: «а чому б не відправляти дані від сервера до клієнта саме в той момент, коли це дійсно необхідно, тобто тоді, коли ці дані виникли».

Варто зазначити, що існує також інший підхід для оновлення вмісту, при якому на сервер відправляється HTTP-запит, який не закривається. Сервер пише дані у відповідь, а клієнт обробляє їх на момент надходження.

Сама ідеологія HTTP передбачає тільки взаємодія за схемою «запит-відповідь». Власне, саме виходячи з цієї схеми та будується саме визначення «клієнт» і «сервер». «Клієнт – той, хто запитує дані, сервер – той, хто відповідає». Саме для подолання цього обмеження і був створений протокол WebSocket.

Щоб відразу розвіяти поширені помилки зазначимо, що WebSocket – це не розширення HTTP. WebSocket – це окремий протокол передачі даних. На відміну від HTTP, де клієнт завжди виступає в ролі запитувача, а сервер – в ролі відповідального, WebSocket по суті стирає межу між сервером і клієнтом, перетворюючи їх в абсолютно рівноправних учасників обміну даними.

Інакше кажучи, обмін даними по WebSocket може бути здійснено як за ініціативою клієнта, так і з ініціативи сервера. Стає можливим передача інформації від сервера клієнту саме в той момент, коли ця передача потрібно.

Зараз, WebSocket де-юре ще не є абсолютним стандартному веба і знаходиться на фінальній стадії розробки. Однак, його тестова реалізація вже сьогодні включена у всі сучасні браузери і, якщо ви не хочете відставати від життя, цікавитися ним, слід вже сьогодні. Класи та модулі для websocket є для всіх популярних серверних середовищ розробки (PHP, NodeJS, Python).

Як же виглядає обмін даними по вебсокет?

Перед початком обміну клієнт (браузер) відправляє на сервер HTTP-запит з наступними заголовками:

GET /getLastnews HTTP/1.1
Host: somehost.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: chat, superchat
Origin: http://somehost.com
Sec-WebSocket-Version: 13
Перші два заголовка не представляють особливого інтересу: це стандартні заголовки протоколу HTTP. Саме в третьому заголовку клієнт «пропонує сервера перейти на вебсокет. Заголовок Sec-WebSocket-Key містить особливий ключ з’єднання, особливим чином оброблений хеш якого передається сервером у відповіді. У заголовку Sec-WebSocket-Protocol клієнт сервер повідомляє про тих реалізаціях протоколу, які він підтримують (цікавляться нюансами підпротоколів ми відішлемо до документації WebSocket). По суті даний заголовок чимось нагадує HTTP-заголовок Content-type.

Відповідь сервера, що підтримує вебсокет виглядає приблизно наступним чином:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
У заголовку Sec-WebSocket-Protocol північ повідомляє обрану ним реалізацію протоколу, заголовок Sec-WebSocket-Accept містить хеш ключа, відправленого клієнтом.

Після обміну «люб’язностями» передача даних в HTTP подібному вигляді припиняється, і спілкування повністю переходить на WebSocket.

На відміну від HTTP, з’єднання WebSocket завжди підтримується відкритим, дані можуть бути передані як від клієнта, так і сервера в будь-який момент. Щоб краще зрозуміти протокол, давайте подивимося, як він реалізується на стороні клієнта.

Насамперед необхідно створити об’єкт socket, що містить методи для роботи з протоколом, передавши конструктору в якості аргументу адресу нашого сервера. Зверніть увагу, що адреса сервера починається з ws:// (замість звичного http://) що зайвий раз нагадує нам про те, що ми має справу з принципово іншим протоколом. До речі, точно також, як і в разі HTTP(S) у WebSocket існує нешифрованная (ws://) і шифрована (wss://) реалізація.

var socket = new WebSocket(“ws://somehost.ua/getLastnews”);
У створеного нами об’єкта socket є чотири події:
onopen – з’єднання відкрито
onclose – з’єднання закрито
onmessage – надійшли нові дані
onerror – помилка

Давайте встановимо обробники для даних подій, встановивши для кожного з них свій колбек:

socket.onopen = function() {
console.log(“WebSocket з’єднання успішно відкрито”);
};
//в якості аргументу колбэку закриття з’єднання передається об’єкт, що містить інформацію про стан закритого з’єднання
socket.onclose = function(event) {
if (!event.wasClean) {
// властивість wasClean приймає значення true, якщо з’єднання закрито нормально і false, якщо з’єднання закрито з помилкою
console.log(“З’єднання закрито c помилкою”);
} else {
console.log(“З’єднання закрито нормально”);
}
};
socket.onmessage = function(event) {
console.log(“Прийшли дані:” + event.data);
//обробка прийшли даних. Дані передаються гіпотетичної функції showNews()
showNews(event.data);
};
socket.onerror = function(error) {
console.log(error.message);
};
Для відправки даних на сервер ми можемо використовувати метод .send():

socket.send(“Деякі дані”);
Як ми бачимо, на відміну від підходу AJAX, нам немає необхідності перевіряти наявність новин через певні інтервали часу, дані самі прийдуть до нас за ініціативою сервера і саме в той момент буде викликана функція-обробник onmessage. Після ініціалізації вебсокет ми маємо повноцінне, двостороннє з’єднання, яке тримається відкритим до тих пір, поки користувач не закриє сторінку (або не станеться помилка або з’єднання не буде закрито з ініціативи сервера).

Якщо цікава тема організації веб-сокет серверу, пишіть в коментарях. Будемо раді розкрити, розповісти smiley

Прочитавши пост, Ви могли б задати собі закономірне питання, якщо це така чудова технологія, чому ж вона досі не використовується повсюдно? Причин, як завжди, кілька, і причини ці, як завжди, традиційні:

  • Вебсокет не підтримується давніми версіями браузерів (зразок IE8, яким, до великої сумі всіх веб-розробників світу все ще хтось користується)
  • Вебсокетом володіє не таке велике число розробників, протокол тестовий, статей і навчальних матеріалів з реалізації сервера на вебсокет навіть в англомовному сегменті Інтернету не так багато
  • Вебсокет проблематично реалізувати на простому віртуальному кульовому хостингу, для його повноцінного безкостыльного використання потрібно VDS (VPS). Хостери могли б встановлювати сучасне програмне забезпечення на свої сервера, але вони цього робити не поспішають
  • Із зазначених вище причин, популярні CMS (включаючи вашу улюблену DLE) поки не поспішають впроваджувати вебсокет в свої релізи (90% наших клієнтів використовують кульовий хостинг який за замовчуванням не готовий до вебсокет)
  • Однак, зараз вже навряд чи викликає сумніви той факт, що з часом ця технологія буде поширюватися. Вже зараз багато сучасні портали і ресурси люблять і використовують вебсокет. Адже це дуже, дуже , дуже зручно smiley

    На цьому поки все. Удачі вам і гарного настрою. Підписуйтесь на нашу сторінку в соціальній мережі “Вконтаке” https://vk.com/dlepage
    13