Примечание. Это некоторое объяснение и псевдокод относительно того, как реализовать очень простой сервер, который может обрабатывать входящие и исходящие сообщения WebSocket согласно определенному формату кадрирования. Это не включает процесс рукопожатия. Кроме того, этот ответ был сделан в образовательных целях; это не полнофункциональная реализация.
Спецификация (RFC 6455)
Отправка сообщений
(Другими словами, сервер & rarr; браузер)
Отправляемые кадры должны быть отформатированы в соответствии с форматом кадрирования WebSocket. Для отправки сообщений этот формат выглядит следующим образом:
- один байт, который содержит тип данных (и некоторую дополнительную информацию, которая выходит за рамки обычного сервера)
- один байт, содержащий длину
- либо два, либо восемь байтов, если длина не помещается во втором байте (тогда второй байт - это код, указывающий, сколько байтов используется для длины)
- фактические (необработанные) данные
Первый байт будет 1000 0001
(или 129
) для текстового фрейма.
Второй байт имеет свой первый бит, установленный на 0
, потому что мы не кодируем данные (кодирование с сервера на клиент не является обязательным).
Для правильной отправки байтов длины необходимо определить длину необработанных данных:
- если
0 <= length <= 125
, вам не нужны дополнительные байты
- если
126 <= length <= 65535
, вам нужно два дополнительных байта, а второй байт - 126
- если
length >= 65536
, вам нужно восемь дополнительных байтов, а второй байт равен 127
Длина должна быть разделена на отдельные байты, что означает, что вам нужно сдвинуть бит вправо (с количеством битов восемь), а затем сохранить только последние восемь бит, выполнив AND 1111 1111
(который 255
).
После длины байта (ов) идут необработанные данные.
Это приводит к следующему псевдокоду:
bytesFormatted[0] = 129
indexStartRawData = -1 // it doesn't matter what value is
// set here - it will be set now:
if bytesRaw.length <= 125
bytesFormatted[1] = bytesRaw.length
indexStartRawData = 2
else if bytesRaw.length >= 126 and bytesRaw.length <= 65535
bytesFormatted[1] = 126
bytesFormatted[2] = ( bytesRaw.length >> 8 ) AND 255
bytesFormatted[3] = ( bytesRaw.length ) AND 255
indexStartRawData = 4
else
bytesFormatted[1] = 127
bytesFormatted[2] = ( bytesRaw.length >> 56 ) AND 255
bytesFormatted[3] = ( bytesRaw.length >> 48 ) AND 255
bytesFormatted[4] = ( bytesRaw.length >> 40 ) AND 255
bytesFormatted[5] = ( bytesRaw.length >> 32 ) AND 255
bytesFormatted[6] = ( bytesRaw.length >> 24 ) AND 255
bytesFormatted[7] = ( bytesRaw.length >> 16 ) AND 255
bytesFormatted[8] = ( bytesRaw.length >> 8 ) AND 255
bytesFormatted[9] = ( bytesRaw.length ) AND 255
indexStartRawData = 10
// put raw data at the correct index
bytesFormatted.put(bytesRaw, indexStartRawData)
// now send bytesFormatted (e.g. write it to the socket stream)
Получение сообщений
(Другими словами, сервер браузера & rarr;)
Полученные вами кадры имеют следующий формат:
- один байт, который содержит тип данных
- один байт, содержащий длину
- два или восемь дополнительных байтов, если длина не вписывается во второй байт
- четыре байта, которые являются масками (= ключи декодирования)
- фактические данные
Первый байт обычно не имеет значения - если вы просто отправляете текст, вы используете только текстовый тип. В этом случае это будет 1000 0001
(или 129
).
Второй байт и дополнительные два или восемь байтов нуждаются в некотором разборе, потому что вам нужно знать, сколько байтов используется для длины (вам нужно знать, где начинаются реальные данные). Сама длина обычно не требуется, поскольку у вас уже есть данные.
Первый бит второго байта всегда равен 1
, что означает, что данные замаскированы (= закодированы). Сообщения от клиента на сервер всегда маскируются. Вам нужно удалить этот первый бит, выполнив secondByte AND 0111 1111
. Есть два случая, в которых результирующий байт не представляет длину, поскольку он не помещается во второй байт:
- второй байт
0111 1110
или 126
означает, что следующие два байта используются для длины
- второй байт
0111 1111
или 127
означает, что следующие восемь байтов используются для длины
Четыре байта маски используются для декодирования фактических данных, которые были отправлены. Алгоритм декодирования выглядит следующим образом:
decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]
, где encodedByte
- исходный байт данных, encodedByteIndex
- индекс (смещение) байта, начиная с первого байта реальных данных , который имеет индекс 0
. masks
- массив, содержащий четыре байта маски.
Это приводит к следующему псевдокоду для декодирования:
secondByte = bytes[1]
length = secondByte AND 127 // may not be the actual length in the two special cases
indexFirstMask = 2 // if not a special case
if length == 126 // if a special case, change indexFirstMask
indexFirstMask = 4
else if length == 127 // ditto
indexFirstMask = 10
masks = bytes.slice(indexFirstMask, 4) // four bytes starting from indexFirstMask
indexFirstDataByte = indexFirstMask + 4 // four bytes further
decoded = new array
decoded.length = bytes.length - indexFirstDataByte // length of real data
for i = indexFirstDataByte, j = 0; i < bytes.length; i++, j++
decoded[j] = bytes[i] XOR masks[j MOD 4]
// now use "decoded" to interpret the received data