Come posso inviare e ricevere messaggi WebSocket sul lato server?


85
  • Come posso inviare e ricevere messaggi lato server utilizzando WebSocket, come da protocollo?

  • Perché ricevo byte apparentemente casuali sul server quando invio dati dal browser al server? I dati sono codificati in qualche modo?

  • Come funziona il framing in entrambe le direzioni server → client e client → server?

Risposte:


154

Nota: questa è una spiegazione e uno pseudocodice su come implementare un server molto banale in grado di gestire i messaggi WebSocket in entrata e in uscita secondo il formato di framing definitivo. Non include il processo di handshaking. Inoltre, questa risposta è stata fatta per scopi educativi; non è un'implementazione completa.

Specifiche (RFC 6455)


Invio di messaggi

(In altre parole, server → browser)

I frame che stai inviando devono essere formattati in base al formato di frame WebSocket. Per l'invio di messaggi, questo formato è il seguente:

  • un byte che contiene il tipo di dati (e alcune informazioni aggiuntive che non rientrano nell'ambito di un server banale)
  • un byte che contiene la lunghezza
  • due o otto byte se la lunghezza non rientra nel secondo byte (il secondo byte è quindi un codice che dice quanti byte sono usati per la lunghezza)
  • i dati effettivi (grezzi)

Il primo byte sarà 1000 0001(o 129) per una cornice di testo.

Il secondo byte ha il suo primo bit impostato su 0perché non stiamo codificando i dati (la codifica da server a client non è obbligatoria).

È necessario determinare la lunghezza dei dati grezzi in modo da inviare correttamente i byte di lunghezza:

  • se 0 <= length <= 125non hai bisogno di byte aggiuntivi
  • se 126 <= length <= 65535, hai bisogno di due byte aggiuntivi e il secondo byte è126
  • se length >= 65536, hai bisogno di otto byte aggiuntivi e il secondo byte è127

La lunghezza deve essere suddivisa in byte separati, il che significa che dovrai spostare il bit a destra (con una quantità di otto bit) e quindi conservare solo gli ultimi otto bit facendo AND 1111 1111(che è 255).

Dopo il byte di lunghezza vengono i dati grezzi.

Questo porta al seguente pseudocodice:

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)

Ricezione di messaggi

(In altre parole, browser → server)

I frame che ottieni sono nel seguente formato:

  • un byte che contiene il tipo di dati
  • un byte che contiene la lunghezza
  • due o otto byte aggiuntivi se la lunghezza non rientrava nel secondo byte
  • quattro byte che sono le maschere (= chiavi di decodifica)
  • i dati effettivi

Il primo byte di solito non ha importanza: se stai solo inviando testo, stai usando solo il tipo di testo. Sarà 1000 0001(o 129) in quel caso.

Il secondo byte e gli altri due o otto byte richiedono un'analisi, perché è necessario sapere quanti byte vengono utilizzati per la lunghezza (è necessario sapere dove iniziano i dati reali). La lunghezza stessa di solito non è necessaria poiché hai già i dati.

Il primo bit del secondo byte è sempre, il 1che significa che i dati sono mascherati (= codificati). I messaggi dal client al server sono sempre mascherati. È necessario rimuovere quel primo bit facendo secondByte AND 0111 1111. Ci sono due casi in cui il byte risultante non rappresenta la lunghezza perché non rientrava nel secondo byte:

  • un secondo byte di 0111 1110, o 126, significa che i seguenti due byte vengono utilizzati per la lunghezza
  • un secondo byte di 0111 1111, o 127, significa che i seguenti otto byte vengono utilizzati per la lunghezza

I quattro byte della maschera vengono utilizzati per decodificare i dati effettivi che sono stati inviati. L'algoritmo per la decodifica è il seguente:

decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]

dove encodedByteè il byte originale nei dati, encodedByteIndexè l'indice (offset) del conteggio dei byte dal primo byte dei dati reali , che ha indice 0. masksè un array contenente i quattro byte della maschera.

Questo porta al seguente pseudocodice per la decodifica:

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

Perché 1000 0001(129) per una cornice di testo? La specifica dice dice: %x1 denotes a text frame. Quindi dovrebbe essere 0000 0001( 0x01) o?
Dennis

3
@Dennis: L'opcode del frame è 0001, come si legge nell'intestazione di quella parte delle specifiche: "Opcode: 4 bits". Il primo byte è composto da FIN, RSV1-3 e codice operativo. FIN è 1, RSV1-3 sono tutti 0e tre e il codice operativo è 0001che si somma 1000 0001per il primo byte. Vedere anche la grafica nelle specifiche che mostra come i byte sono suddivisi nelle diverse parti.
pimvdb

Hai poche righe che leggono come 'bytesFormatted [2] = (bytesRaw.length >> 56) AND 255' nel modello Server-> Client - Ti dispiacerebbe scomporlo? E sembra essere un operatore logico per me, quindi non posso aspettarmi che il semplice inserimento di un numero dopo possa fare qualcosa per me in C #. Allo stesso modo, non sono sicuro di cosa dovrebbe indicare il ">>" nel tuo markup, tuttavia viene trasferito in C # ... Qualunque cosa significhi per me ...: P
DigitalJedi805

Se qualcuno può chiarirmi questo aspetto, sarò lieto di pubblicare la mia implementazione C # come risposta.
DigitalJedi805

1
@Neevek: Quello che vogliono dire è che i byte della maschera stessi devono essere imprevedibili. Se sono costanti, non hanno molto senso. Fondamentalmente, quando un utente malintenzionato ha una fetta di dati, non dovrebbe essere in grado di decodificarli senza le maschere. Se la posizione delle maschere non è prevedibile, è un po 'difficile da decodificare per il server originale :)
pimvdb

26

Implementazione Java (se richiesta)

Lettura: da client a server

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

            }
        }

Scrittura: da server a client

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
Quale sarebbe una lunghezza del buffer appropriata per l'operazione di lettura?
jackgerrits

Purtroppo non funziona. Ho appena copiato la trasmissione void (dal server al client) nel mio programma. Socket connesso correttamente, messaggio inviato al browser con successo, ma nulla ricevuto dal browser.
nick

18

Implementazione 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
Probabilmente vale la pena notare che JavaScript non supporta effettivamente lo spostamento con numeri più grandi di 2^31 - 1.
pimvdb

13

Implementazione C #

Browser -> Server

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

Server -> Browser

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

1
La funzione di decodifica restituisce sempre il mio messaggio specifico con un'appendice per me non definita come qui test�c=ܝX[in cui "test" è il mio messaggio. Da cosa viene l'altra parte?
Snickbrack

1
Scusa per il ritardo della risposta. Ho creato una piccola applicazione C # (Console e Web) per provare i web socket. Puoi scaricarli da qui per vedere come è codificato. Link: dropbox.com/s/gw8hjsov1u6f7c0/Web%20Sockets.rar?dl=0
Nitij

Questo non è riuscito per me su messaggi di grandi dimensioni. Ho sostituito il codice length> 65535 con: var l = Convert.ToUInt64 (length); var b = BitConverter.GetBytes (l); Array.Reverse (b, 0, b.Length); b.CopyTo (frame, 2); ... che sembra aver risolto le cose.
Sean

Buon lavoro. Solo una cosa: su DecodeMessage sto calcolando la lunghezza dell'array "decodificato" in base ai dati di lunghezza del payload inclusi nel dataframe perché la lunghezza dell'array "byte" non potrebbe essere esatta. La lunghezza dell'array "byte" dipende dal modo in cui viene letto il flusso.
user1011138

@ Sean, puoi mostrarmi il tuo esempio completo per risolvere il problema dei messaggi di grandi dimensioni? non posso cambiare quel codice nel tuo campione.
Ali Yousefi

6

La risposta di pimvdb implementata in 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 esempio di utilizzo:

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', '!']

Ho provato a utilizzare il tuo codice nel mio script, ma senza successo. Potresti forse essere in grado di aiutare? stackoverflow.com/questions/43748377/…
yak

5

Oltre alla funzione di codifica dei frame PHP, segue una funzione di decodifica:

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

Ho implementato questa e anche altre funzioni in una classe PHP WebSocket facile da usare qui .


4

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

4

Grazie per la risposta, vorrei aggiungere alla versione Python di hfern (sopra) per includere la funzione di invio se qualcuno è interessato.

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) 

Utilizzo per la lettura:

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

Utilizzo per la scrittura:

EncodeWebSockSend(sock,"hellooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")

2

Implementazione in Go

Codifica parte (server -> browser)

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 di decodifica (browser -> server)

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

2

Clojure, la funzione di decodifica presuppone che il frame venga inviato come mappa di {:data byte-array-buffer :size int-size-of-buffer} , perché la dimensione effettiva potrebbe non essere la stessa dimensione dell'array di byte a seconda della dimensione del blocco del flusso di input.

Codice pubblicato qui: 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))

0

Implementazione C ++ (non da me) qui . Nota che quando i tuoi byte sono superiori a 65535, devi spostare con un valore lungo come mostrato qui .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.