2011-08-12 13 views
8

Desde que Chrome se actualizó a v14, pasaron de version three of the draft a version eight of the draft.Cómo (de) construir marcos de datos en WebSockets hybi 08+?

Tengo una aplicación de chat interna ejecutándose en WebSocket, y aunque he conseguido que funcione el nuevo protocolo de enlace, el encuadre de datos aparentemente también ha cambiado. Mi servidor WebSocket se basa 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?

Respuesta

16

(Véase también: How can I send and receive WebSocket messages on the server side?)


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

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

(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 variar entre el texto y datos binarios La descodificación de los datos funciona exactamente de la 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 fotograma. 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 codificación xor que usa una de las máscaras tal como se define en indexOfByteInData mod 4 de los datos. La decodificación 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 algo de pseudocódigo (algunos acento JavaScript Me temo):

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 the specification que puede ser útil (por supuesto, contiene todo lo que necesita para comprender el formato).

+0

Dulce ... Lo probaré en el trabajo el lunes. Le daré la respuesta aceptada si funciona ... :) – gislikonrad

+0

Finalmente me puse a buscar esta solución para mi servidor websocket. Funcionó como un amuleto ... Gracias, hombre ... – gislikonrad

+0

@ Gísli Konráð: Genial funcionó para ti; WebSockets no son realmente amigables para la depuración. – pimvdb

2

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 fotograma que contiene la longitud de la carga, entre otras cosas. Hay un diagrama del formato de marco en section 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 mirar websockify que es un proxy/puente de socket WebSockets to TCP que he creado para admitir noVNC. Se implementa en python, pero debería poder obtener la idea de las rutinas encode_hybi y decode_hybi.

8

Este código C# funciona bien para mí. Decodifica datos de texto que provienen de un navegador a un servidor C# 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); 
    } 
+0

Esto debería marcarse como respuesta, de acuerdo con la pregunta (código C#) – cdie

+0

¿Qué es 'totalLength' y por qué agrega' keyIndex + 4' a él? –

Cuestiones relacionadas