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 0
perché 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 <= 125
non 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 1
che 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
1000 0001
(129) per una cornice di testo? La specifica dice dice:%x1 denotes a text frame
. Quindi dovrebbe essere0000 0001
(0x01
) o?