Как (де) построить фреймы данных в WebSockets Hybi 08+? - PullRequest
8 голосов
/ 12 августа 2011

С тех пор, как Chrome обновился до версии 14, они перешли с версии 3 черновика на версии 8 черновика .

У меня есть внутреннее приложение чата, работающее на WebSocket, и, хотя я получил новое рукопожатие, очевидно, изменилось и оформление данных. Мой сервер WebSocket основан на Nugget .

У кого-нибудь есть WebSocket, работающий с восьмой версией черновика, и есть пример того, как кадрировать данные, передаваемые по проводам?

Ответы [ 3 ]

16 голосов
/ 13 августа 2011

(См. Также: Как я могу отправлять и получать сообщения WebSocket на стороне сервера? )


Это довольно просто, но важно понимать формат.

Первый байт почти всегда равен 1000 0001, где 1 означает «последний кадр», три 0 s являются зарезервированными битами без какого-либо значения, а 0001 означает, что это текстовый фрейм ( который Chrome отправляет методом ws.send()).

( Обновление: Теперь Chrome также может отправлять двоичные кадры с ArrayBuffer. Последние четыре бита первого байта будут 0002, поэтому вы можете различать текстовые и двоичные данные. декодирование данных работает точно так же.)

Второй байт содержит 1 (что означает, что он «замаскирован» (кодирован)), за которым следуют семь битов, которые представляют размер кадра. Если это между 000 0000 и 111 1101, это размер. Если это 111 1110, следующие 2 байта являются длиной (потому что она не помещается в семь битов), а если это 111 1111, следующие 8 байтов являются длиной (если она также не помещается в два байта) ).

Далее следуют четыре байта, которые являются «масками», которые вам нужны для декодирования данных кадра. Это делается с использованием xor-кодирования, которое использует одну из масок, как определено indexOfByteInData mod 4 данных. Декодирование просто работает как encodedByte xor maskByte (где maskByte равно indexOfByteInData mod 4).

Теперь я должен сказать, что у меня совсем нет опыта работы с C #, но это какой-то псевдокод (боюсь, с некоторым акцентом на JavaScript):

var length_code = bytes[1] & 127, // remove the first 1 by doing '& 127'
    masks,
    data;

if(length_code === 126) {
    masks = bytes.slice(4, 8);   // 'slice' returns part of the byte array
    data  = bytes.slice(8);      // and accepts 'start' (inclusively)
} else if(length_code === 127) { // and 'end' (exclusively) as arguments
    masks = bytes.slice(10, 14); // Passing no 'end' makes 'end' the length
    data  = bytes.slice(14);     // of the array
} else {
    masks = bytes.slice(2, 6);
    data  = bytes.slice(6);
}

// 'map' replaces each element in the array as per a specified function
// (each element will be replaced with what is returned by the function)
// The passed function accepts the value and index of the element as its
// arguments
var decoded = data.map(function(byte, index) { // index === 0 for the first byte
    return byte ^ masks[ index % 4 ];          // of 'data', not of 'bytes'
    //         xor            mod
});

Вы также можете загрузить спецификацию , которая может быть полезна (она, конечно, содержит все необходимое для понимания формата).

12 голосов
/ 29 июля 2014

Этот код на C # отлично работает для меня. Декодировать текстовые данные, поступающие из браузера на сервер c # через сокет.

    public static string GetDecodedData(byte[] buffer, int length)
    {
        byte b = buffer[1];
        int dataLength = 0;
        int totalLength = 0;
        int keyIndex = 0;

        if (b - 128 <= 125)
        {
            dataLength = b - 128;
            keyIndex = 2;
            totalLength = dataLength + 6;
        }

        if (b - 128 == 126)
        {
            dataLength = BitConverter.ToInt16(new byte[] { buffer[3], buffer[2] }, 0);
            keyIndex = 4;
            totalLength = dataLength + 8;
        }

        if (b - 128 == 127)
        {
            dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);
            keyIndex = 10;
            totalLength = dataLength + 14;
        }

        if (totalLength > length)
            throw new Exception("The buffer length is small than the data length");

        byte[] key = new byte[] { buffer[keyIndex], buffer[keyIndex + 1], buffer[keyIndex + 2], buffer[keyIndex + 3] };

        int dataIndex = keyIndex + 4;
        int count = 0;
        for (int i = dataIndex; i < totalLength; i++)
        {
            buffer[i] = (byte)(buffer[i] ^ key[count % 4]);
            count++;
        }

        return Encoding.ASCII.GetString(buffer, dataIndex, dataLength);
    }
2 голосов
/ 12 августа 2011

Чтобы быть более точным, Chrome перешел от версии протокола Hixie-76 к версии HyBi-10 протокола.От HyBi-08 до HyBi-10 все сообщается как версия 8, потому что на самом деле изменился только текст спецификации, а не формат провода.

Кадрирование изменилось с использования '\ x00 ... \ xff' наиспользуя 2-7-байтовый заголовок для каждого кадра, который среди прочего содержит длину полезной нагрузки.Диаграмма формата кадра приведена в разделе раздела 4.2 спецификации.Также обратите внимание, что данные от клиента (браузера) к серверу маскируются (4 байта заголовков фрейма клиент-сервер содержат ключ снятия маски).

Вы можете посмотреть websockify , которыйсозданный мной прокси / мост сокета WebSockets to TCP для поддержки noVNC .Он реализован на python, но вы должны быть в состоянии понять эту идею из процедур encode_hybi и decode_hybi .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...