2011-08-12 8 views
8

Po zaktualizowaniu Chrome do wersji 14 użytkownicy przeszli od version three of the draft do version eight of the draft.Jak (de) konstruować ramki danych w hybrydach WebSockets 08+?

Mam wewnętrzną aplikację do czatu działającą na WebSocket i chociaż mam nowy handshake, ramka danych również się zmieniła. Mój serwer WebSocket jest oparty na Nugget.

Czy ktoś ma oprogramowanie WebSocket współpracujące z wersją ósmą wersji roboczej i ma przykład, jak ustawić ramkę dla danych wysyłanych przez przewód?

Odpowiedz

16

(Patrz także: How can I send and receive WebSocket messages on the server side?)


Jest to dość proste, ale ważne jest, aby zrozumieć format.

Pierwszy bajt jest prawie zawsze 1000 0001, gdzie 1 oznacza „ostatnią klatkę”, trzy 0 s są tak daleko zastrzeżone bity bez znaczenia i 0001 oznacza, że ​​jest to ramka tekstowa (który Chrome wysyła metodą ws.send()).

(Aktualizacja:.. Chrome może teraz również wysłać ramek binarnych z ArrayBuffer Ostatnie cztery bity pierwszego bajtu będzie 0002, więc można się różnić między tekstu i danych binarnych Dekodowanie danych działa dokładnie tak ten sam sposób.)

Drugi bajt zawiera 1 (co oznacza, że ​​jest "zamaskowany" (zakodowany)), po którym następuje siedem bitów, które reprezentują rozmiar ramki. Jeśli jest między 000 0000 i 111 1101, to jest rozmiar. Jeśli jest to 111 1110, następujące 2 bajty są długością (ponieważ nie zmieściłoby się w siedmiu bitach), a jeśli jest to 111 1111, następujące 8 bajtów to długość (jeśli nie zmieściłaby się również w dwóch bajtach).

Poniżej znajdują się cztery bajty, które są "maskami", których potrzebujesz do dekodowania danych ramki. Odbywa się to za pomocą kodowania xor, które wykorzystuje jedną z masek zdefiniowanych przez indexOfByteInData mod 4 danych. Dekodowanie działa po prostu jako encodedByte xor maskByte (gdzie maskByte jest indexOfByteInData mod 4).

Teraz muszę powiedzieć, że nie mam doświadczenie z C# w ogóle, ale to jest jakiś pseudokod (jakiś akcent JavaScript Obawiam):

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

Można również pobrać the specification, które mogą być pomocne (zawiera oczywiście wszystko, co jest potrzebne do zrozumienia formatu).

+0

Słodko ... Wypróbuję to w poniedziałek. Dam ci zaakceptowaną odpowiedź, jeśli się uda ... :) – gislikonrad

+0

W końcu udało mi się spojrzeć na to rozwiązanie dla mojego serwera websocket. Pracował jak czar ... Dzięki, człowieku ... – gislikonrad

+0

@ Gísli Konráð: Świetnie się udało; WebSockets nie są tak naprawdę przyjazne debugowaniu. – pimvdb

2

Aby uzyskać dokładniejsze informacje, Chrome przeszedł z wersji Hixie-76 protokołu do wersji HyBi-10 protokołu. HyBi-08 przez HyBi-10 wszystkie zgłaszają się jako wersja 8, ponieważ tak naprawdę zmienił się tylko tekst specyfikacji, a nie format drutu.

Ramka zmieniła się z używania "\ x00 ... \ xff" do używania 2-7-bajtowego nagłówka dla każdej ramki, która zawiera między innymi długość ładunku. Istnieje schemat formatu ramki w specyfikacji section 4.2. Zwróć też uwagę, że dane z klienta (przeglądarki) na serwer są maskowane (4 bajty nagłówków klatek klienta i serwera zawierają klucz do odmaskowania).

Możesz spojrzeć na websockify, który jest WebSockets do gniazda TCP proxy/most, który stworzyłem do obsługi noVNC. Jest on zaimplementowany w pythonie, ale powinieneś być w stanie uzyskać ten pomysł z procedur encode_hybi i decode_hybi.

8

Ten kod C# działa dobrze dla mnie. Odkodować dane tekstowe przychodzące z przeglądarki do serwera C# za pośrednictwem gniazda.

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

To powinno być oznaczone jako odpowiedź, zgodnie z pytaniem (kod C#). – cdie

+0

Co to jest "totalLength" i dlaczego dodajesz do niej 'keyIndex + 4'? –