Spec-Zone.ru › Web APIs

Создание сервера WebSocket на Java

Этот пример демонстрирует создание сервера WebSocket API с использованием Oracle Java.

Хотя для создания сервера WebSocket можно использовать другие языки серверной стороны, в этом примере используется Oracle Java для упрощения кода.

Этот сервер соответствует RFC 6455, поэтому он обрабатывает только подключения с Chrome версии 16, Firefox 11, IE 10 и выше.

Первые шаги

WebSocket взаимодействуют через соединение TCP (Transmission Control Protocol). Класс ServerSocket Java расположен в пакете java.net.

ServerSocket

Конструктор ServerSocket принимает один параметр port типа int.

При создании объекта ServerSocket, он привязывается к номеру порта, указанному в аргументе port.

Вот реализация, разделенная на части:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class WebSocket {
  public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
    ServerSocket server = new ServerSocket(80);
    try {
      System.out.println("Server has started on 127.0.0.1:80.\r\nWaiting for a connection…");
      Socket client = server.accept();
      System.out.println("A client connected.");

Методы класса Socket

java.net.Socket.getInputStream()

Возвращает поток ввода для этого сокета.

java.net.Socket.getOutputStream()

Возвращает поток вывода для этого сокета.

Методы класса OutputStream

write(byte[] b, int off, int len)

Записывает len байта из заданного массива байтов, начиная с смещения off в этот поток вывода.

Методы класса InputStream

read(byte[] b, int off, int len)

Считывает до len байтов данных из потока ввода в массив байтов.

Давайте расширим наш пример.

InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
Scanner s = new Scanner(in, "UTF-8");

Установление соединения

Когда клиент подключается к серверу, он отправляет GET-запрос для обновления соединения на WebSocket из простого HTTP-запроса. Это известно как установление соединения.

try {
  String data = s.useDelimiter("\\r\\n\\r\\n").next();
  Matcher get = Pattern.compile("^GET").matcher(data);

Создание ответа проще, чем понимание, почему это необходимо делать таким образом.

Необходимо:

  1. Получить значение заголовка запроса Sec-WebSocket-Key без начальных и конечных пробелов
  2. Связать его с "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
  3. Вычислить SHA-1 и Base64-код
  4. Записать его обратно в качестве значения заголовка ответа Sec-WebSocket-Accept в составе HTTP-ответа.
if (get.find()) {
  Matcher match = Pattern.compile("Sec-WebSocket-Key: (.*)").matcher(data);
  match.find();
  byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n"
    + "Connection: Upgrade\r\n"
    + "Upgrade: websocket\r\n"
    + "Sec-WebSocket-Accept: "
    + Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-1").digest((match.group(1) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes("UTF-8")))
    + "\r\n\r\n").getBytes("UTF-8");
  out.write(response, 0, response.length);

Декодирование сообщений

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

Если мы отправим "abcdef", получим следующие байты:

129 134 167 225 225 210 198 131 130 182 194 135
  • 129:

    FIN (Это всё сообщение?) RSV1 RSV2 RSV3 Код операции
    1 0 0 0 0x1=0001

    FIN: Вы можете отправлять сообщения кадрами, но сейчас упростим. Код операции 0x1 означает, что это текстовое сообщение. Полный список кодов операций

  • 134:

    Если второй байт минус 128 находится в диапазоне от 0 до 125, это длина сообщения. Если это 126, следующие 2 байта (16-битное беззнаковое целое число), если 127, следующие 8 байт (64-битное беззнаковое целое число, самый старший бит ОБЯЗАТЕЛЬНО должен быть 0), представляют длину.

    Примечание: Может быть 128, потому что первый бит всегда 1.

  • 167, 225, 225 и 210 – байты ключа для декодирования. Он меняется каждый раз.

  • Остальные закодированные байты – это сообщение.

Алгоритм декодирования

Декодированный байт = закодированный байт XOR (позиция закодированного байта ПОБЫТОВАЯ И 0x3)ый байт ключа

Пример на Java:

          byte[] decoded = new byte[6];
          byte[] encoded = new byte[] { (byte) 198, (byte) 131, (byte) 130, (byte) 182, (byte) 194, (byte) 135 };
          byte[] key = new byte[] { (byte) 167, (byte) 225, (byte) 225, (byte) 210 };
          for (int i = 0; i < encoded.length; i++) {
            decoded[i] = (byte) (encoded[i] ^ key[i & 0x3]);
          }
        }
      } finally {
        s.close();
      }
    } finally {
      server.close();
    }
  }
}

Связанное

  • Создание серверов WebSocket

© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_a_WebSocket_server_in_Java

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API