¿Cómo puedo enviar y recibir mensajes de WebSocket en el servidor?

85
  • ¿Cómo puedo enviar y recibir mensajes en el lado del servidor usando WebSocket, según el protocolo?

  • ¿Por qué obtengo bytes aparentemente aleatorios en el servidor cuando envío datos desde el navegador al servidor? ¿Los datos están codificados de alguna manera?

  • ¿Cómo funciona el encuadre en las direcciones servidor → cliente y cliente → servidor?

pimvdb
fuente

Respuestas:

154

Nota: Esta es una explicación y un pseudocódigo sobre cómo implementar un servidor muy trivial que puede manejar los mensajes WebSocket entrantes y salientes según el formato de trama definitivo. No incluye el proceso de apretón de manos. Además, esta respuesta se ha realizado con fines educativos; no es una implementación con todas las funciones.

Especificación (RFC 6455)


Enviando mensajes

(En otras palabras, servidor → navegador)

Los marcos que envía deben formatearse de acuerdo con el formato de marco de WebSocket. Para enviar mensajes, este formato es el siguiente:

  • un byte que contiene el tipo de datos (y alguna información adicional que está fuera del alcance de un servidor trivial)
  • un byte que contiene la longitud
  • ya sea dos u ocho bytes si la longitud no cabe en el segundo byte (el segundo byte es entonces un código que indica cuántos bytes se utilizan para la longitud)
  • los datos reales (sin procesar)

El primer byte será 1000 0001(o 129) para un marco de texto.

El segundo byte tiene su primer bit configurado en 0porque no estamos codificando los datos (la codificación del servidor al cliente no es obligatoria).

Es necesario determinar la longitud de los datos brutos para enviar correctamente los bytes de longitud:

  • si 0 <= length <= 125no necesita bytes adicionales
  • si 126 <= length <= 65535necesita dos bytes adicionales y el segundo byte es126
  • si length >= 65536necesita ocho bytes adicionales y el segundo byte es127

La longitud debe dividirse en bytes separados, lo que significa que deberá desplazar los bits hacia la derecha (con una cantidad de ocho bits) y luego solo retener los últimos ocho bits haciendo AND 1111 1111(que es 255).

Después de los bytes de longitud, vienen los datos sin procesar.

Esto conduce al siguiente 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)

Recibir mensajes

(En otras palabras, navegador → servidor)

Los marcos que obtienes están en el siguiente formato:

  • un byte que contiene el tipo de datos
  • un byte que contiene la longitud
  • ya sea dos u ocho bytes adicionales si la longitud no cabe en el segundo byte
  • cuatro bytes que son las máscaras (= claves de decodificación)
  • los datos reales

El primer byte generalmente no importa: si solo está enviando texto, solo está usando el tipo de texto. Será 1000 0001(o 129) en ese caso.

El segundo byte y los dos u ocho bytes adicionales necesitan algo de análisis, porque necesita saber cuántos bytes se utilizan para la longitud (necesita saber dónde comienzan los datos reales). Por lo general, la longitud en sí no es necesaria, ya que ya tiene los datos.

El primer bit del segundo byte es siempre, lo 1que significa que los datos están enmascarados (= codificados). Los mensajes del cliente al servidor siempre están enmascarados. Necesitas eliminar ese primer bit haciendo secondByte AND 0111 1111. Hay dos casos en los que el byte resultante no representa la longitud porque no cabe en el segundo byte:

  • un segundo byte de 0111 1110, o 126, significa que los siguientes dos bytes se utilizan para la longitud
  • un segundo byte de 0111 1111, o 127, significa que los siguientes ocho bytes se utilizan para la longitud

Los cuatro bytes de máscara se utilizan para decodificar los datos reales que se han enviado. El algoritmo de decodificación es el siguiente:

decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]

donde encodedBytees el byte original en los datos, encodedByteIndexes el índice (desplazamiento) del byte contando desde el primer byte de los datos reales , que tiene index 0. maskses una matriz que contiene los cuatro bytes de máscara.

Esto conduce al siguiente pseudocódigo para decodificar:

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
fuente
¿Por qué 1000 0001(129) para un marco de texto? La especificación dice dice: %x1 denotes a text frame. Entonces debería ser 0000 0001( 0x01), o?
Dennis
3
@Dennis: El código de operación de la trama es 0001, como se indica en el encabezado de esa parte de las especificaciones: " Código de operación : 4 bits". El primer byte consta de FIN, RSV1-3 y código de operación. FIN es 1, RSV1-3 son los tres 0y el código de operación es el 0001que se suma 1000 0001para el primer byte. También vea la ilustración en la especificación que muestra cómo se dividen los bytes en las diferentes partes.
pimvdb
Tiene algunas líneas que se leen como 'bytesFormatted [2] = (bytesRaw.length >> 56) AND 255' en el modelo Servidor-> Cliente. ¿Le importaría desglosar eso por mí? Y me parece un operador lógico, por lo que no puedo esperar que simplemente poner un número después de que haga algo por mí en C #. Del mismo modo, no estoy seguro de lo que se supone que indica el ">>" en su marcado; sin embargo, se transfiere a C # ... Lo que sea que eso signifique para mí ...: P
DigitalJedi805
Si alguien puede aclararme esto, estaré encantado de publicar mi implementación de C # como respuesta.
DigitalJedi805
1
@Neevek: Lo que quieren decir es que los propios bytes de la máscara deben ser impredecibles. Si son constantes, no tienen mucho sentido. Básicamente, cuando un usuario malintencionado tiene una porción de datos, no debería poder decodificarlos sin las máscaras. Si la posición de las máscaras no es predecible, entonces es un poco difícil para el servidor genuino decodificar :)
pimvdb
26

Implementación de Java (si alguno lo requiere)

Lectura: cliente a 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];

            }
        }

Escritura: servidor a 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();

}
Pasupatía Haribabu
fuente
3
¿Cuál sería una longitud de búfer adecuada para la operación de lectura?
jackgerrits
Desafortunadamente, no funciona. Acabo de copiar la transmisión vacía (del servidor al cliente) en mi programa. El socket se conectó correctamente, el mensaje se envió correctamente al navegador, pero el navegador no recibió nada.
nick
18

Implementación 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
fuente
5
Probablemente valga la pena señalar que JavaScript en realidad no admite cambios con números mayores que 2^31 - 1.
pimvdb
13

Implementación de 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
fuente
1
La función de decodificación siempre devuelve mi mensaje específico con un apéndice indefinido para mí como aquí test�c=ܝX[en el que "prueba" es mi mensaje. ¿De qué viene la otra parte?
Snickbrack
1
Lo siento por la respuesta tardía. Creé una pequeña aplicación C # (consola y web) para probar los sockets web. Puede descargarlos desde aquí para ver cómo está codificado. Enlace: dropbox.com/s/gw8hjsov1u6f7c0/Web%20Sockets.rar?dl=0
Nitij
Esto me falló en mensajes grandes. Reemplacé el código de longitud> 65535 con: var l = Convert.ToUInt64 (length); var b = BitConverter.GetBytes (l); Array.Reverse (b, 0, b.Length); b. Copiar a (marco, 2); ... que parece haber arreglado las cosas.
Sean
Buen trabajo. Solo una cosa: en DecodeMessage, estoy calculando la longitud de la matriz "decodificada" en función de los datos de longitud de la carga útil que se incluyen en el marco de datos porque la longitud de la matriz de "bytes" no puede ser exacta. La longitud de la matriz de "bytes" depende de la forma en que se lea la secuencia.
user1011138
@Sean, ¿puede mostrarme su ejemplo completo para solucionar un problema de mensaje grande? No puedo cambiar ese código a tu muestra.
Ali Yousefi
6

La respuesta de pimvdb implementada en 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

Un ejemplo 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', '!']
Cazador Fernandes
fuente
Intenté usar su código en mi script, pero sin éxito. ¿Podrías quizás ayudarme? stackoverflow.com/questions/43748377/…
yak
5

Además de la función de codificación de cuadros de PHP, aquí sigue una función de decodificación:

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

He implementado esta y también otras funciones en una clase PHP de WebSocket fácil de usar aquí .

Tacticus
fuente
4

Implementación 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
fuente
4

Gracias por la respuesta, me gustaría agregar la versión de Python de hfern (arriba) para incluir la función de envío si alguien está interesado.

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 leer:

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

Uso para escribir:

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

Implementación en Go

Codificar parte (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
}

Decodificar parte (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
fuente
2

Clojure, la función de decodificación asume que el marco se envía como mapa de {:data byte-array-buffer :size int-size-of-buffer}, porque el tamaño real puede no ser el mismo que el de la matriz de bytes dependiendo del tamaño del fragmento de su flujo de entrada.

Código publicado aquí: 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))
escape
fuente
0

Implementación de C ++ (no por mí) aquí . Tenga en cuenta que cuando sus bytes superan los 65535, debe cambiar con un valor largo como se muestra aquí .

pcunite
fuente