Написание клиентских приложений с помощью веб-сокетов
=====================================================

Веб-сокеты - технология, которая позволяет открыть интерактивную сессию общения между браузером пользователя и сервером. Соединяясь через веб-сокеты, веб-приложения могут осуществлять взаимодействие в реальном времени вместо того, чтобы делать запросы к клиенту о входящих/исходящих изменениях.

**Примечание:** У нас есть работающий пример чата, части кода из которого используются в статье. Пример будет доступен, когда инфраструктура сайта сможет должным образом поддерживать хостинг примеров с использованием веб-сокетов.

Доступность веб-сокетов
-----------------------

API веб-сокетов доступно в Javascript коде, область видимости которого включает объект DOM `Window` или любой объект, реализующий `WorkerUtils`; это означает, что вы можете использовать Web Workers.

**Примечание:** API веб-сокетов (как и протокол лежащий в его основе) всё ещё проходят этап активной разработки; в настоящее время существует много проблем совместимости с разными браузерами (и даже с разными релизами одного и того же браузера).

Создание объекта WebSocket
--------------------------

`Чтобы общаться через протокол веб-сокетов необходимо создать объект WebSocket`; при его создании автоматически происходит попытка открыть соединение с сервером.

Конструктор WebSocket принимает один обязательный и один необязательный параметр:
```

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString protocols
);

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString[] protocols
);
```


`url`

URL, с которым происходит соединение; это должен быть URL веб-сокет-сервера.

`protocols` Необязательный

Может быть одной строкой протокола или массивом таких строк. Эти строки используют для индикации под-протоколов; таким образом, один сервер может реализовывать несколько под-протоколов веб-сокетов (к примеру, вам может потребоваться, чтобы сервер мог обрабатывать разные типы взаимодействий в зависимости от определённого под-протокола). Если вы не укажете строку протокола, то будет передана пустая строка.

В конструкторе могут возникать следующие исключения:

`SECURITY_ERR`

Порт, к которому проводится подключение, заблокирован.

### Ошибки подключения

Если ошибка случается во время попытки подключения, то в объект `WebSocket` сначала посылается простое событие с именем «error» (таким образом, задействуя обработчик `onerror`), потом - событие `CloseEvent` (таким образом, задействуя обработчик `onclose`) чтобы обозначить причину закрытия соединения.

Однако, начиная с версии Firefox 11, типичным является получение в консоль от платформы Mozilla расширенного сообщения об ошибке и кода завершения, как то определено в RFC 6455, Section 7.4 посредством `CloseEvent`.

### Примеры

Этот простой пример создаёт новый WebSocket, подключаемый к серверу `ws://www.example.com/socketserver`. В данном примере в конструктор сокета в качестве дополнительного параметра передаётся пользовательский протокол "protocolOne", хотя эта часть может быть опущена.


```js
var exampleSocket = new WebSocket(
  "ws://www.example.com/socketserver",
  "protocolOne",
);
```


После выполнения функции, `exampleSocket.readyState` (en-US) будет иметь значение `CONNECTING`. `readyState` изменится на `OPEN` как только соединение станет готовым к передаче данных.

Если нужно открыть соединение, поддерживающее несколько протоколов, можно передать массив протоколов:


```js
var exampleSocket = new WebSocket("ws://www.example.com/socketserver", [
  "protocolOne",
  "protocolTwo",
]);
```


Когда соединение установлено (что соответствует, `readyState` `OPEN`), `exampleSocket.protocol` сообщит, какой протокол выбрал сервер.

В приведенных выше примерах `ws` заменяет `http`, аналогично `wss` заменяет `https`. Установка соединения через WebSocket зависит от механизма обновления HTTP, таким образом запрос на обновление неявный, когда мы обращаемся к серверу HTTP с помощью `ws://www.example.com` или `wss://www.example.com`.

Отправка данных на сервер
-------------------------

Однажды открыв соединение, вы можете передавать данные на сервер. Для осуществления этого, вызовите метод `send()` объекта `WebSocket` для каждого сообщение, которое желаете отправить:


```js
exampleSocket.send("Вот текст, который будет отправлен серверу.");
```


Вы можете пересылать данные в виде строки, `Blob`, так и `ArrayBuffer`.

**Примечание:** До версии 11, Firefox поддерживал отправку данных только в виде строки.

Так как установка соедиения асинхронна и подвержена сбоям, то нет никакой гарантии, что вызов метода `send()`, после создания объекта WebSocket, будет завершен успешно. По крайней мере, мы можем быть уверены, что попытка отправить данные будет иметь место только после того, как соединение будет установлено, определив обработчик `onopen` для выполнения этого действия:


```js
exampleSocket.onopen = function (event) {
  exampleSocket.send("Вот текст, который будет отправлен серверу.");
};
```

### Использование JSON для передачи объектов

Одна удобная вещь которую вы можете сделать, это использовать JSON для пересылки сложных данных на сервер. Например, приложение-чат может взаимодействовать с сервером, используя протокол, реализованный с использованием пакетов данных, инкапсулированных в JSON:


```js
// Отправьте текст всем пользователям через сервер
function sendText() {
  // Создайте объект содержащий данные, необходимые серверу для обрабоки сообщения от клиента чата.
  var msg = {
    type: "message",
    text: document.getElementById("text").value,
    id: clientID,
    date: Date.now(),
  };

  // Отправьте объект в виде JSON строки.
  exampleSocket.send(JSON.stringify(msg));

  // Очистите элемент ввода текста, чтобы получить следующую строку текста от пользователя.
  document.getElementById("text").value = "";
}
```

Получение сообщений от сервера
------------------------------

WebSockets — это API, управляемый событиями; когда сообщения получены, событие "message" доставлено в функцию `onmessage`. Чтобы начать прослушивание входящих данных, вы можете сделать что-то вроде этого:


```js
exampleSocket.onmessage = function (event) {
  console.log(event.data);
};
```

### Получение и интерпретация JSON объектов

Давайте рассмотрим клиентское приложение чата, которое впервые упоминалось в разделе Использование JSON для передачи объектов. Есть разные типы пакетов данных, которые может получить клиент, например:

* Вход в систему
* Текст сообщения
* Обновление списка пользователей

Код обрабатывающий эти входящие сообщения, может выглядеть так:


```js
exampleSocket.onmessage = function (event) {
  var f = document.getElementById("chatbox").contentDocument;
  var text = "";
  var msg = JSON.parse(event.data);
  var time = new Date(msg.date);
  var timeStr = time.toLocaleTimeString();

  switch (msg.type) {
    case "id":
      clientID = msg.id;
      setUsername();
      break;
    case "username":
      text =
        "<b>User <em>" +
        msg.name +
        "</em> signed in at " +
        timeStr +
        "</b><br>";
      break;
    case "message":
      text = "(" + timeStr + ") <b>" + msg.name + "</b>: " + msg.text + "<br>";
      break;
    case "rejectusername":
      text =
        "<b>Your username has been set to <em>" +
        msg.name +
        "</em> because the name you chose is in use.</b><br>";
      break;
    case "userlist":
      var ul = "";
      for (i = 0; i < msg.users.length; i++) {
        ul += msg.users[i] + "<br>";
      }
      document.getElementById("userlistbox").innerHTML = ul;
      break;
  }

  if (text.length) {
    f.write(text);
    document.getElementById("chatbox").contentWindow.scrollByPages(1);
  }
};
```


Здесь мы используем `JSON.parse()` чтобы преобразовать JSON строку в объект, затем обработайте его.

### Формат текстовых данных

Текст, полученный через WebSocket должен иметь кодировку UTF-8

До Gecko 9.0, некоторые не символьные значения в допустимом тексте UTF-8 могут привести к разрыву соединения. Теперь Gecko допускает эти значения.

Закрытие соединения
-------------------

Когда вы закончили использовать соединение WebSocket, закройте его используя метод `close()`:


```js
exampleSocket.close();
```


Перед попыткой закрыть соединение может быть полезно проверить атрибут `bufferedAmount` чтобы определить, не переданы ли еще какие-либо данные по сети.

Безопасность
------------

WebSocket не следует использовать в среде со смешанным содержимым: то есть вы не должны открывать незащищенное соединение WebSocket со страницы, загруженной с использованием HTTPS, или наоборот. Фактически, некоторые браузеры явно запрещают это, например Firefox 8 и выше.