¿Cómo (de) construir marcos de datos en WebSockets hybi 08+?

Desde que Chrome se actualizó a v14, pasaron de la versión tres del borrador a la versión ocho del borrador .

Tengo una aplicación de chat interna ejecutándose en WebSocket, y aunque he conseguido que funcione el nuevo handshake, el encuadre de datos aparentemente también ha cambiado. Mi servidor WebSocket está basado en Nugget .

¿Alguien tiene WebSocket trabajando con la versión ocho del borrador y tiene un ejemplo sobre cómo enmarcar los datos que se envían por cable?

(Consulte también: ¿Cómo puedo enviar y recibir mensajes de WebSocket en el lado del servidor? )


Es bastante fácil, pero es importante entender el formato.

El primer byte es casi siempre 1000 0001 , donde el 1 significa “último cuadro”, los tres 0 s son bits reservados sin ningún significado hasta el momento y el 0001 significa que es un marco de texto (que Chrome envía con el ws.send() método).

( Actualización: Chrome ahora también puede enviar cuadros binarios con un ArrayBuffer . Los últimos cuatro bits del primer byte serán 0002 , por lo que puede diferir entre el texto y los datos binarios. La deencoding de los datos funciona exactamente de la misma manera).

El segundo byte contiene un 1 (lo que significa que está “enmascarado” (codificado)) seguido de siete bits que representan el tamaño del fotogtwig. Si está entre 000 0000 y 111 1101 , ese es el tamaño. Si es 111 1110 , los siguientes 2 bytes son la longitud (porque no cabría en siete bits), y si es 111 1111 , los siguientes 8 bytes son la longitud (si tampoco caben en dos bytes).

A continuación hay cuatro bytes que son las “máscaras” que necesita para decodificar los datos del cuadro. Esto se hace usando una encoding xor que usa una de las máscaras tal como se define en indexOfByteInData mod 4 de los datos. La deencoding simplemente funciona como encodedByte xor maskByte (donde maskByte es indexOfByteInData mod 4 ).

Ahora debo decir que no tengo experiencia con C # en absoluto, pero esto es un pseudocódigo (me temo que tengo algún acento de JavaScript):

 var length_code = bytes[1] & 127, // remove the first 1 by doing '& 127' masks, data; if(length_code === 126) { masks = bytes.slice(4, 8); // 'slice' returns part of the byte array data = bytes.slice(8); // and accepts 'start' (inclusively) } else if(length_code === 127) { // and 'end' (exclusively) as arguments masks = bytes.slice(10, 14); // Passing no 'end' makes 'end' the length data = bytes.slice(14); // of the array } else { masks = bytes.slice(2, 6); data = bytes.slice(6); } // 'map' replaces each element in the array as per a specified function // (each element will be replaced with what is returned by the function) // The passed function accepts the value and index of the element as its // arguments var decoded = data.map(function(byte, index) { // index === 0 for the first byte return byte ^ masks[ index % 4 ]; // of 'data', not of 'bytes' // xor mod }); 

También puede descargar la especificación que puede ser útil (por supuesto, contiene todo lo que necesita para comprender el formato).

Este código c # funciona bien para mí. Decodifica datos de texto que provienen de un navegador a un servidor de CA a través de un socket.

  public static string GetDecodedData(byte[] buffer, int length) { byte b = buffer[1]; int dataLength = 0; int totalLength = 0; int keyIndex = 0; if (b - 128 <= 125) { dataLength = b - 128; keyIndex = 2; totalLength = dataLength + 6; } if (b - 128 == 126) { dataLength = BitConverter.ToInt16(new byte[] { buffer[3], buffer[2] }, 0); keyIndex = 4; totalLength = dataLength + 8; } if (b - 128 == 127) { dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0); keyIndex = 10; totalLength = dataLength + 14; } if (totalLength > length) throw new Exception("The buffer length is small than the data length"); byte[] key = new byte[] { buffer[keyIndex], buffer[keyIndex + 1], buffer[keyIndex + 2], buffer[keyIndex + 3] }; int dataIndex = keyIndex + 4; int count = 0; for (int i = dataIndex; i < totalLength; i++) { buffer[i] = (byte)(buffer[i] ^ key[count % 4]); count++; } return Encoding.ASCII.GetString(buffer, dataIndex, dataLength); } 

Para ser más precisos, Chrome pasó de la versión Hixie-76 del protocolo a la versión HyBi-10 del protocolo. Todos los informes de HyBi-08 a HyBi-10 son de la versión 8 porque realmente solo cambiaba el texto de la especificación y no el formato de conexión.

El encuadre ha cambiado de usar ‘\ x00 … \ xff’ a usar un encabezado de 2 a 7 bytes para cada fotogtwig que contiene la longitud de la carga, entre otras cosas. Hay un diagtwig del formato de marco en la sección 4.2 de la especificación. También tenga en cuenta que los datos del cliente (navegador) al servidor están enmascarados (4 bytes de los encabezados del marco del cliente-servidor contienen la clave de desenmascaramiento).

Puede ver websockify, que es un proxy / bridge de socket WebSockets to TCP que he creado para admitir noVNC . Está implementado en python, pero debería poder obtener la idea de las rutinas encode_hybi y decode_hybi .