Como posso enviar e receber mensagens WebSocket no servidor?

86
  • Como posso enviar e receber mensagens no lado do servidor usando WebSocket, de acordo com o protocolo?

  • Por que obtenho bytes aparentemente aleatórios no servidor quando envio dados do navegador para o servidor? Os dados são codificados de alguma forma?

  • Como funciona o enquadramento nas direções servidor → cliente e cliente → servidor?

pimvdb
fonte

Respostas:

154

Nota: Esta é uma explicação e pseudocódigo sobre como implementar um servidor muito trivial que pode lidar com mensagens WebSocket de entrada e saída de acordo com o formato de enquadramento definitivo. Não inclui o processo de handshaking. Além disso, essa resposta foi feita para fins educacionais; não é uma implementação completa.

Especificação (RFC 6455)


Enviando mensagens

(Em outras palavras, servidor → navegador)

Os frames que você está enviando precisam ser formatados de acordo com o formato de frame do WebSocket. Para enviar mensagens, este formato é o seguinte:

  • um byte que contém o tipo de dados (e algumas informações adicionais que estão fora do escopo de um servidor comum)
  • um byte que contém o comprimento
  • dois ou oito bytes se o comprimento não couber no segundo byte (o segundo byte é um código que diz quantos bytes são usados ​​para o comprimento)
  • os dados reais (brutos)

O primeiro byte será 1000 0001(ou 129) para um quadro de texto.

O segundo byte tem seu primeiro bit definido como 0porque não estamos codificando os dados (a codificação do servidor para o cliente não é obrigatória).

É necessário determinar o comprimento dos dados brutos para enviar os bytes de comprimento corretamente:

  • se 0 <= length <= 125você não precisa de bytes adicionais
  • se 126 <= length <= 65535, você precisa de dois bytes adicionais e o segundo byte é126
  • se length >= 65536, você precisa de oito bytes adicionais, e o segundo byte é127

O comprimento deve ser dividido em bytes separados, o que significa que você precisará deslocar os bits para a direita (com uma quantidade de oito bits) e, então, reter apenas os últimos oito bits AND 1111 1111(que é 255).

Após o (s) byte (s) de comprimento, vêm os dados brutos.

Isso leva ao seguinte pseudocódigo:

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)

Recebendo mensagens

(Em outras palavras, navegador → servidor)

Os quadros que você obtém estão no seguinte formato:

  • um byte que contém o tipo de dados
  • um byte que contém o comprimento
  • dois ou oito bytes adicionais se o comprimento não couber no segundo byte
  • quatro bytes que são as máscaras (= chaves de decodificação)
  • os dados reais

O primeiro byte geralmente não importa - se você está apenas enviando texto, está usando apenas o tipo de texto. Será 1000 0001(ou 129) nesse caso.

O segundo byte e os dois ou oito bytes adicionais precisam de alguma análise, porque você precisa saber quantos bytes são usados ​​para o comprimento (você precisa saber onde os dados reais começam). O comprimento em si geralmente não é necessário, pois você já tem os dados.

O primeiro bit do segundo byte é sempre, o 1que significa que os dados são mascarados (= codificados). As mensagens do cliente para o servidor são sempre mascaradas. Você precisa remover esse primeiro bit fazendo secondByte AND 0111 1111. Existem dois casos em que o byte resultante não representa o comprimento porque não cabia no segundo byte:

  • um segundo byte de 0111 1110, ou 126, significa que os dois bytes seguintes são usados ​​para o comprimento
  • um segundo byte de 0111 1111, ou 127, significa que os oito bytes seguintes são usados ​​para o comprimento

Os quatro bytes de máscara são usados ​​para decodificar os dados reais que foram enviados. O algoritmo de decodificação é o seguinte:

decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]

onde encodedByteé o byte original nos dados, encodedByteIndexé o índice (deslocamento) do byte contando a partir do primeiro byte dos dados reais , que possui índice 0. masksé uma matriz contendo os quatro bytes da máscara.

Isso leva ao seguinte pseudocódigo para decodificação:

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
fonte
Por que 1000 0001(129) para um quadro de texto? A especificação diz diz: %x1 denotes a text frame. Portanto, deve ser 0000 0001( 0x01) ou?
Dennis
3
@Dennis: O opcode do quadro é 0001, como afirma no cabeçalho dessa parte das especificações: "Opcode: 4 bits". O primeiro byte consiste em FIN, RSV1-3 e opcode. FIN é 1, RSV1-3 são os três 0e o opcode é 0001que soma 1000 0001para o primeiro byte. Veja também a arte na especificação que mostra como os bytes são divididos nas diferentes partes.
pimvdb
Você tem algumas linhas que dizem 'bytesFormatted [2] = (bytesRaw.length >> 56) AND 255' no modelo Servidor-> Cliente - você se importaria de quebrar isso para mim? E parece ser um operador lógico para mim, então não posso esperar que simplesmente colocar um número depois dele vá fazer alguma coisa para mim em C #. Da mesma forma, não tenho certeza do que o ">>" em sua marcação deve indicar - no entanto, ele se transfere para C # ... O que quer que isso signifique para mim ...: P
DigitalJedi805
Se alguém puder esclarecer isso para mim, ficarei feliz em postar minha implementação do C # como uma resposta.
DigitalJedi805
1
@Neevek: O que eles querem dizer é que os próprios bytes da máscara precisam ser imprevisíveis. Se forem constantes, não há muito sentido para eles. Basicamente, quando um usuário malicioso possui uma fatia de dados, ele não deve ser capaz de decodificá-la sem as máscaras. Se a posição das máscaras não for previsível, será um pouco difícil para o servidor genuíno decodificar :)
pimvdb
26

Implementação Java (se houver)

Leitura: Cliente para Servidor

        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];

            }
        }

Escrita: Servidor para Cliente

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();

}
Haribabu Pasupathy
fonte
3
Qual seria um tamanho de buffer apropriado para a operação de leitura?
jackgerrits
Infelizmente, isso não funciona. Acabei de copiar a transmissão vazia (do servidor para o cliente) para o meu programa. Soquete conectado com sucesso, mensagem enviada ao navegador com sucesso, mas nada recebido pelo navegador.
nick
18

Implementação de 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;
}
Richard Astbury
fonte
5
Provavelmente, vale a pena notar que o JavaScript na verdade não oferece suporte para deslocamento com números maiores que 2^31 - 1.
pimvdb
13

Implementação C #

Navegador -> Servidor

    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);
    }

Servidor -> Navegador

    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
fonte
1
A função de decodificação sempre retorna minha mensagem específica com um apêndice indefinido para mim como aqui test�c=ܝX[em que "teste" é minha mensagem. De onde vem a outra parte?
Snickbrack
1
Desculpe pelo atraso na resposta. Eu criei um pequeno aplicativo C # (Console e Web) para testar web sockets. Você pode baixá-los aqui para ver como está codificado. Link: dropbox.com/s/gw8hjsov1u6f7c0/Web%20Sockets.rar?dl=0
Nitij
Isso falhou para mim em mensagens grandes. Substituí o código de comprimento> 65535 por: var l = Convert.ToUInt64 (comprimento); var b = BitConverter.GetBytes (l); Array.Reverse (b, 0, b.Length); b.CopyTo (frame, 2); ... que parece ter consertado as coisas.
Sean,
Bom trabalho. Apenas uma coisa: em DecodeMessage, estou calculando o comprimento do array "decodificado" com base nos dados de comprimento da carga útil que estão incluídos no dataframe porque o comprimento do array "bytes" não podia ser exato. O comprimento do array "bytes" depende da maneira como o fluxo é lido.
user1011138
@Sean você pode me mostrar seu exemplo completo para corrigir problemas de mensagens grandes? eu não posso mudar esse código para sua amostra.
Ali Yousefi
6

Resposta de pimvdb implementada em 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

Um exemplo de uso:

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', '!']
Hunter Fernandes
fonte
Tentei usar seu código em meu script, mas sem sucesso. Você poderia talvez ser capaz de ajudar? stackoverflow.com/questions/43748377/…
yak
5

Além da função de codificação de quadro PHP, aqui segue uma função de decodificação:

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;
}

Implementei essa e também outras funções em uma classe WebSocket PHP fácil de usar aqui .

Tacticus
fonte
4

Implementação de 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
fonte
4

Obrigado pela resposta, eu gostaria de adicionar a versão Python de hfern (acima) para incluir a função Sending se alguém estiver interessado.

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) 

Uso para leitura:

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

Uso para escrita:

EncodeWebSockSend(sock,"hellooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")
DR.
fonte
2

Implementação em Go

Parte da codificação (servidor -> navegador)

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
}

Parte da decodificação (navegador -> servidor)

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
fonte
2

Clojure, a função de decodificação assume que o quadro é enviado como mapa de {:data byte-array-buffer :size int-size-of-buffer} , porque o tamanho real pode não ser o mesmo tamanho da matriz de bytes, dependendo do tamanho do bloco de seu fluxo de entrada.

Código postado aqui: 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))
fuga
fonte
0

Implementação C ++ (não por mim) aqui . Observe que quando seus bytes estão acima de 65535, você precisa mudar com um valor longo, conforme mostrado aqui .

pcunite
fonte