Как я могу отправлять и получать сообщения WebSocket на стороне сервера?

85
  • Как я могу отправлять и получать сообщения на стороне сервера с помощью WebSocket в соответствии с протоколом?

  • Почему я получаю кажущиеся случайными байты на сервере при отправке данных из браузера на сервер? Это данные как-то закодированы?

  • Как обрамление работает в направлениях сервер → клиент и клиент → сервер?

pimvdb
источник

Ответы:

154

Примечание. Это некоторое объяснение и псевдокод того, как реализовать очень простой сервер, который может обрабатывать входящие и исходящие сообщения 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
pimvdb
источник
Почему 1000 0001(129) для текстового фрейма? Спецификация говорит говорит: %x1 denotes a text frame. Так должно быть 0000 0001( 0x01), или?
Деннис
3
@Dennis: Код операции кадра 0001, как указано в заголовке этой части спецификации: « Код операции : 4 бита». Первый байт состоит из FIN, RSV1-3 и кода операции. Плавник 1, RSV1-3 все три 0и опкод это , 0001который добавляет к 1000 0001для первого байта. Также смотрите иллюстрацию в спецификации, которая показывает, как байты разделяются на разные части.
pimvdb
У вас есть несколько строк, которые читаются как «bytesFormatted [2] = (bytesRaw.length >> 56) И 255» в модели Сервер-> Клиент. Не могли бы вы разобрать это для меня? И мне кажется логическим оператором, поэтому я не могу ожидать, что простое добавление числа после него что-нибудь сделает для меня в C #. Точно так же я не уверен, что должен обозначать ">>" в вашей разметке - однако он переносится на C # ... Что бы это ни значило для меня ...: P
DigitalJedi805
Если кто-нибудь может прояснить это для меня, я буду рад опубликовать свою реализацию на C # в качестве ответа.
DigitalJedi805,
1
@Neevek: Они имеют в виду, что сами байты маски должны быть непредсказуемыми. Если они постоянные, в них нет особого смысла. По сути, когда злоумышленник получает часть данных, он не может декодировать ее без масок. Если маски позиция не предсказуема , то это немного сложно для подлинного сервера расшифровывает :)
pimvdb
26

Реализация Java (если требуется)

Чтение: от клиента к серверу

        int len = 0;            
        byte[] b = new byte[buffLenth];
        //rawIn is a Socket.getInputStream();
        while(true){
            len = rawIn.read(b);
            if(len!=-1){

                byte rLength = 0;
                int rMaskIndex = 2;
                int rDataStart = 0;
                //b[0] is always text in my case so no need to check;
                byte data = b[1];
                byte op = (byte) 127;
                rLength = (byte) (data & op);

                if(rLength==(byte)126) rMaskIndex=4;
                if(rLength==(byte)127) rMaskIndex=10;

                byte[] masks = new byte[4];

                int j=0;
                int i=0;
                for(i=rMaskIndex;i<(rMaskIndex+4);i++){
                    masks[j] = b[i];
                    j++;
                }

                rDataStart = rMaskIndex + 4;

                int messLen = len - rDataStart;

                byte[] message = new byte[messLen];

                for(i=rDataStart, j=0; i<len; i++, j++){
                    message[j] = (byte) (b[i] ^ masks[j % 4]);
                }

                parseMessage(new String(message)); 
                //parseMessage(new String(b));

                b = new byte[buffLenth];

            }
        }

Написание: от сервера к клиенту

public void brodcast(String mess) throws IOException{
    byte[] rawData = mess.getBytes();

    int frameCount  = 0;
    byte[] frame = new byte[10];

    frame[0] = (byte) 129;

    if(rawData.length <= 125){
        frame[1] = (byte) rawData.length;
        frameCount = 2;
    }else if(rawData.length >= 126 && rawData.length <= 65535){
        frame[1] = (byte) 126;
        int len = rawData.length;
        frame[2] = (byte)((len >> 8 ) & (byte)255);
        frame[3] = (byte)(len & (byte)255); 
        frameCount = 4;
    }else{
        frame[1] = (byte) 127;
        int len = rawData.length;
        frame[2] = (byte)((len >> 56 ) & (byte)255);
        frame[3] = (byte)((len >> 48 ) & (byte)255);
        frame[4] = (byte)((len >> 40 ) & (byte)255);
        frame[5] = (byte)((len >> 32 ) & (byte)255);
        frame[6] = (byte)((len >> 24 ) & (byte)255);
        frame[7] = (byte)((len >> 16 ) & (byte)255);
        frame[8] = (byte)((len >> 8 ) & (byte)255);
        frame[9] = (byte)(len & (byte)255);
        frameCount = 10;
    }

    int bLength = frameCount + rawData.length;

    byte[] reply = new byte[bLength];

    int bLim = 0;
    for(int i=0; i<frameCount;i++){
        reply[bLim] = frame[i];
        bLim++;
    }
    for(int i=0; i<rawData.length;i++){
        reply[bLim] = rawData[i];
        bLim++;
    }

    out.write(reply);
    out.flush();

}
Харибабу Пасупати
источник
3
Какая будет подходящая длина буфера для операции чтения?
jackgerrits
К сожалению, это не работает. Я просто скопировал пустую трансляцию (с сервера на клиент) в свою программу. Сокет успешно подключен, сообщение отправлено в браузер, но браузер ничего не получил.
Ник
18

Реализация JavaScript:

function encodeWebSocket(bytesRaw){
    var bytesFormatted = new Array();
    bytesFormatted[0] = 129;
    if (bytesRaw.length <= 125) {
        bytesFormatted[1] = bytesRaw.length;
    } else if (bytesRaw.length >= 126 && bytesRaw.length <= 65535) {
        bytesFormatted[1] = 126;
        bytesFormatted[2] = ( bytesRaw.length >> 8 ) & 255;
        bytesFormatted[3] = ( bytesRaw.length      ) & 255;
    } else {
        bytesFormatted[1] = 127;
        bytesFormatted[2] = ( bytesRaw.length >> 56 ) & 255;
        bytesFormatted[3] = ( bytesRaw.length >> 48 ) & 255;
        bytesFormatted[4] = ( bytesRaw.length >> 40 ) & 255;
        bytesFormatted[5] = ( bytesRaw.length >> 32 ) & 255;
        bytesFormatted[6] = ( bytesRaw.length >> 24 ) & 255;
        bytesFormatted[7] = ( bytesRaw.length >> 16 ) & 255;
        bytesFormatted[8] = ( bytesRaw.length >>  8 ) & 255;
        bytesFormatted[9] = ( bytesRaw.length       ) & 255;
    }
    for (var i = 0; i < bytesRaw.length; i++){
        bytesFormatted.push(bytesRaw.charCodeAt(i));
    }
    return bytesFormatted;
}

function decodeWebSocket (data){
    var datalength = data[1] & 127;
    var indexFirstMask = 2;
    if (datalength == 126) {
        indexFirstMask = 4;
    } else if (datalength == 127) {
        indexFirstMask = 10;
    }
    var masks = data.slice(indexFirstMask,indexFirstMask + 4);
    var i = indexFirstMask + 4;
    var index = 0;
    var output = "";
    while (i < data.length) {
        output += String.fromCharCode(data[i++] ^ masks[index++ % 4]);
    }
    return output;
}
Ричард Эстбери
источник
5
Вероятно, стоит отметить, что JavaScript на самом деле не поддерживает сдвиг с числами больше, чем 2^31 - 1.
pimvdb 07
13

Реализация C #

Браузер -> Сервер

    private String DecodeMessage(Byte[] bytes)
    {
        String incomingData = String.Empty;
        Byte secondByte = bytes[1];
        Int32 dataLength = secondByte & 127;
        Int32 indexFirstMask = 2;
        if (dataLength == 126)
            indexFirstMask = 4;
        else if (dataLength == 127)
            indexFirstMask = 10;

        IEnumerable<Byte> keys = bytes.Skip(indexFirstMask).Take(4);
        Int32 indexFirstDataByte = indexFirstMask + 4;

        Byte[] decoded = new Byte[bytes.Length - indexFirstDataByte];
        for (Int32 i = indexFirstDataByte, j = 0; i < bytes.Length; i++, j++)
        {
            decoded[j] = (Byte)(bytes[i] ^ keys.ElementAt(j % 4));
        }

        return incomingData = Encoding.UTF8.GetString(decoded, 0, decoded.Length);
    }

Сервер -> Браузер

    private static Byte[] EncodeMessageToSend(String message)
    {
        Byte[] response;
        Byte[] bytesRaw = Encoding.UTF8.GetBytes(message);
        Byte[] frame = new Byte[10];

        Int32 indexStartRawData = -1;
        Int32 length = bytesRaw.Length;

        frame[0] = (Byte)129;
        if (length <= 125)
        {
            frame[1] = (Byte)length;
            indexStartRawData = 2;
        }
        else if (length >= 126 && length <= 65535)
        {
            frame[1] = (Byte)126;
            frame[2] = (Byte)((length >> 8) & 255);
            frame[3] = (Byte)(length & 255);
            indexStartRawData = 4;
        }
        else
        {
            frame[1] = (Byte)127;
            frame[2] = (Byte)((length >> 56) & 255);
            frame[3] = (Byte)((length >> 48) & 255);
            frame[4] = (Byte)((length >> 40) & 255);
            frame[5] = (Byte)((length >> 32) & 255);
            frame[6] = (Byte)((length >> 24) & 255);
            frame[7] = (Byte)((length >> 16) & 255);
            frame[8] = (Byte)((length >> 8) & 255);
            frame[9] = (Byte)(length & 255);

            indexStartRawData = 10;
        }

        response = new Byte[indexStartRawData + length];

        Int32 i, reponseIdx = 0;

        //Add the frame bytes to the reponse
        for (i = 0; i < indexStartRawData; i++)
        {
            response[reponseIdx] = frame[i];
            reponseIdx++;
        }

        //Add the data bytes to the response
        for (i = 0; i < length; i++)
        {
            response[reponseIdx] = bytesRaw[i];
            reponseIdx++;
        }

        return response;
    }
Nitij
источник
1
Функция декодирования всегда возвращает мое конкретное сообщение с неопределенным для меня приложением, как здесь, test�c=ܝX[в котором «тест» - это мое сообщение. Откуда идет другая часть?
Snickbrack
1
Извините за задержку с ответом. Я создал небольшое приложение на C # (консоль и веб), чтобы опробовать веб-сокеты. Вы можете скачать их отсюда, чтобы увидеть, как это закодировано. Ссылка: dropbox.com/s/gw8hjsov1u6f7c0/Web%20Sockets.rar?dl=0
Nitij
Это не помогло мне с большими сообщениями. Я заменил код длины> 65535 на: var l = Convert.ToUInt64 (length); var b = BitConverter.GetBytes (l); Array.Reverse (b, 0, b.Length); b.CopyTo (кадр, 2); ... который, кажется, исправил ситуацию.
Шон
Отличная работа. Только одно: в DecodeMessage я вычисляю длину «декодированного» массива на основе данных о длине полезной нагрузки, которые включены в фрейм данных, поскольку длина массива «байтов» не может быть точной. Длина массива "bytes" зависит от способа чтения потока.
user1011138
@Sean, можешь ли ты показать мне свой полный пример решения проблемы с большими сообщениями? я не могу изменить этот код на ваш образец.
Али Юсефи
6

Ответ pimvdb реализован на Python:

def DecodedCharArrayFromByteStreamIn(stringStreamIn):
    #turn string values into opererable numeric byte values
    byteArray = [ord(character) for character in stringStreamIn]
    datalength = byteArray[1] & 127
    indexFirstMask = 2 
    if datalength == 126:
        indexFirstMask = 4
    elif datalength == 127:
        indexFirstMask = 10
    masks = [m for m in byteArray[indexFirstMask : indexFirstMask+4]]
    indexFirstDataByte = indexFirstMask + 4
    decodedChars = []
    i = indexFirstDataByte
    j = 0
    while i < len(byteArray):
        decodedChars.append( chr(byteArray[i] ^ masks[j % 4]) )
        i += 1
        j += 1
    return decodedChars

Пример использования:

fromclient = '\x81\x8c\xff\xb8\xbd\xbd\xb7\xdd\xd1\xd1\x90\x98\xea\xd2\x8d\xd4\xd9\x9c'
# this looks like "?ŒOÇ¿¢gÓ ç\Ð=«ož" in unicode, received by server
print DecodedCharArrayFromByteStreamIn(fromclient)
# ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!']
Хантер Фернандес
источник
Я пытался использовать ваш код в своем скрипте, но безуспешно. Не могли бы вы помочь? stackoverflow.com/questions/43748377/…
як
5

В дополнение к функции кодирования фреймов PHP, здесь следует функция декодирования:

function Decode($M){
    $M = array_map("ord", str_split($M));
    $L = $M[1] AND 127;

    if ($L == 126)
        $iFM = 4;
    else if ($L == 127)
        $iFM = 10;
    else
        $iFM = 2;

    $Masks = array_slice($M, $iFM, 4);

    $Out = "";
    for ($i = $iFM + 4, $j = 0; $i < count($M); $i++, $j++ ) {
        $Out .= chr($M[$i] ^ $Masks[$j % 4]);
    }
    return $Out;
}

Я реализовал это , а также другие функции в простом в использовании класса WebSocket PHP здесь .

Tacticus
источник
4

Реализация PHP:

function encode($message)
{
    $length = strlen($message);

    $bytesHeader = [];
    $bytesHeader[0] = 129; // 0x1 text frame (FIN + opcode)

    if ($length <= 125) {
            $bytesHeader[1] = $length;
    } else if ($length >= 126 && $length <= 65535) {
            $bytesHeader[1] = 126;
            $bytesHeader[2] = ( $length >> 8 ) & 255;
            $bytesHeader[3] = ( $length      ) & 255;
    } else {
            $bytesHeader[1] = 127;
            $bytesHeader[2] = ( $length >> 56 ) & 255;
            $bytesHeader[3] = ( $length >> 48 ) & 255;
            $bytesHeader[4] = ( $length >> 40 ) & 255;
            $bytesHeader[5] = ( $length >> 32 ) & 255;
            $bytesHeader[6] = ( $length >> 24 ) & 255;
            $bytesHeader[7] = ( $length >> 16 ) & 255;
            $bytesHeader[8] = ( $length >>  8 ) & 255;
            $bytesHeader[9] = ( $length       ) & 255;
    }

    $str = implode(array_map("chr", $bytesHeader)) . $message;

    return $str;
}
DanBlack
источник
4

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

def DecodedWebsockRecieve(stringStreamIn):
    byteArray =  stringStreamIn 
    datalength = byteArray[1] & 127
    indexFirstMask = 2 
    if datalength == 126:
        indexFirstMask = 4
    elif datalength == 127:
        indexFirstMask = 10
    masks = [m for m in byteArray[indexFirstMask : indexFirstMask+4]]
    indexFirstDataByte = indexFirstMask + 4
    decodedChars = []
    i = indexFirstDataByte
    j = 0
    while i < len(byteArray):
        decodedChars.append( chr(byteArray[i] ^ masks[j % 4]) )
        i += 1
        j += 1
    return ''.join(decodedChars)

def EncodeWebSockSend(socket,data):
    bytesFormatted = []
    bytesFormatted.append(129)

    bytesRaw = data.encode()
    bytesLength = len(bytesRaw)
    if bytesLength <= 125 :
        bytesFormatted.append(bytesLength)
    elif bytesLength >= 126 and bytesLength <= 65535 :
        bytesFormatted.append(126)
        bytesFormatted.append( ( bytesLength >> 8 ) & 255 )
        bytesFormatted.append( bytesLength & 255 )
    else :
        bytesFormatted.append( 127 )
        bytesFormatted.append( ( bytesLength >> 56 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 48 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 40 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 32 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 24 ) & 255 )
        bytesFormatted.append( ( bytesLength >> 16 ) & 255 )
        bytesFormatted.append( ( bytesLength >>  8 ) & 255 )
        bytesFormatted.append( bytesLength & 255 )

    bytesFormatted = bytes(bytesFormatted)
    bytesFormatted = bytesFormatted + bytesRaw
    socket.send(bytesFormatted) 

Использование для чтения:

bufSize = 1024     
read = DecodedWebsockRecieve(socket.recv(bufSize))

Использование для записи:

EncodeWebSockSend(sock,"hellooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")
DR.
источник
2

Реализация на Go

Кодировать часть (сервер -> браузер)

func encode (message string) (result []byte) {
  rawBytes := []byte(message)
  var idxData int

  length := byte(len(rawBytes))
  if len(rawBytes) <= 125 { //one byte to store data length
    result = make([]byte, len(rawBytes) + 2)
    result[1] = length
    idxData = 2
  } else if len(rawBytes) >= 126 && len(rawBytes) <= 65535 { //two bytes to store data length
    result = make([]byte, len(rawBytes) + 4)
    result[1] = 126 //extra storage needed
    result[2] = ( length >> 8 ) & 255
    result[3] = ( length      ) & 255
    idxData = 4
  } else {
    result = make([]byte, len(rawBytes) + 10)
    result[1] = 127
    result[2] = ( length >> 56 ) & 255
    result[3] = ( length >> 48 ) & 255
    result[4] = ( length >> 40 ) & 255
    result[5] = ( length >> 32 ) & 255
    result[6] = ( length >> 24 ) & 255
    result[7] = ( length >> 16 ) & 255
    result[8] = ( length >>  8 ) & 255
    result[9] = ( length       ) & 255
    idxData = 10
  }

  result[0] = 129 //only text is supported

  // put raw data at the correct index
  for i, b := range rawBytes {
    result[idxData + i] = b
  }
  return
}

Часть декодирования (браузер -> сервер)

func decode (rawBytes []byte) string {
  var idxMask int
  if rawBytes[1] == 126 {
    idxMask = 4
  } else if rawBytes[1] == 127 {
    idxMask = 10
  } else {
    idxMask = 2
  }

  masks := rawBytes[idxMask:idxMask + 4]
  data := rawBytes[idxMask + 4:len(rawBytes)]
  decoded := make([]byte, len(rawBytes) - idxMask + 4)

  for i, b := range data {
    decoded[i] = b ^ masks[i % 4]
  }
  return string(decoded)
}
Rmonjo
источник
2

Clojure, функция декодирования предполагает, что фрейм отправляется как карта {:data byte-array-buffer :size int-size-of-buffer}, потому что фактический размер может отличаться от размера байтового массива в зависимости от размера фрагмента вашего входного потока.

Код размещен здесь: https://gist.github.com/viperscape/8918565

(defn ws-decode [frame]
  "decodes websocket frame"
  (let [data (:data frame)
        dlen (bit-and (second data) 127)
        mstart (if (== dlen 127) 10 (if (== dlen 126) 4 2))
        mask (drop 2 (take (+ mstart 4) data))
        msg (make-array Byte/TYPE (- (:size frame) (+ mstart 4)))]
   (loop [i (+ mstart 4), j 0]
      (aset-byte msg j (byte (bit-xor (nth data i) (nth mask (mod j 4)))))
      (if (< i (dec(:size frame))) (recur (inc i) (inc j))))
    msg))

(defn ws-encode [data]
  "takes in bytes, return websocket frame"
  (let [len (count data)
        blen (if (> len 65535) 10 (if (> len 125) 4 2))
        buf (make-array Byte/TYPE (+ len blen))
        _ (aset-byte buf 0 -127) ;;(bit-or (unchecked-byte 0x80) 
                                           (unchecked-byte 0x1)
        _ (if (= 2 blen) 
            (aset-byte buf 1 len) ;;mask 0, len
            (do
              (dorun(map #(aset-byte buf %1 
                      (unchecked-byte (bit-and (bit-shift-right len (*(- %2 2) 8))
                                               255)))
                      (range 2 blen) (into ()(range 2 blen))))
              (aset-byte buf 1 (if (> blen 4) 127 126))))
        _ (System/arraycopy data 0 buf blen len)]
    buf))
scape
источник
0

Реализация C ++ (не мной) здесь . Обратите внимание, что когда ваши байты превышают 65535, вам нужно сдвинуть с помощью длинного значения, как показано здесь .

оборота pcunite
источник