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
1000 0001(129) para um quadro de texto? A especificação diz diz:%x1 denotes a text frame. Portanto, deve ser0000 0001(0x01) ou?