Примечание. Это некоторое объяснение и псевдокод того, как реализовать очень простой сервер, который может обрабатывать входящие и исходящие сообщения WebSocket в соответствии с окончательным форматом кадрирования. Он не включает процесс установления связи. Кроме того, этот ответ был дан в образовательных целях; это не полнофункциональная реализация.
Спецификация (RFC 6455)
Отправка сообщений
(Другими словами, сервер → браузер)
Отправляемые вами кадры должны быть отформатированы в соответствии с форматом кадрирования 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)
Получение сообщений
(Другими словами браузер → сервер)
Полученные кадры имеют следующий формат:
- один байт, содержащий тип данных
- один байт, содержащий длину
- два или восемь дополнительных байтов, если длина не помещается во втором байте
- четыре байта, которые являются масками (= ключи декодирования)
- фактические данные
Первый байт обычно не имеет значения - если вы просто отправляете текст, вы используете только текстовый тип. Будет 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
1000 0001(129) для текстового фрейма? Спецификация говорит говорит:%x1 denotes a text frame. Так должно быть0000 0001(0x01), или?