Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Binary Opcode encoding and decoding implementation specific in websockets?

Suppose I am creating a websocket client. And a specific websocket url returns frame as 'Binary Frame (Opcode 2)' .The questions are

1. Why would the developer want to wrap the original message inside a binary opcode frame?
2. Is retrieving the message implementation centric? In another way, does the the client has to know the same logic that was used to encode at the server?
3. If the above is false then is there a global way to decode/parse the binary opcode to see the actual data that is being sent?

like image 713
deepg Avatar asked Sep 05 '26 06:09

deepg


1 Answers

Handling Websocket frames


A Websocket Frames basically look like this:

0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

Explanation


  1. FIN
    • The FIN bit tells you if this is all the Data you will receive. Large Websocket messages can be fragmented (Send via multiple frames)
  2. RSV1-3
    • This are reserved bits, they may be used later
  3. opcode
    • This can be used to determine what type of frame you received
    • Possible options are
      • 0 (Continue)
      • If this frame is a part, not the first part of a fragmented message
    • 1 (Text)
      • Normal UTF-8 Encoded Text data
    • 2 (Binary)
      • Binary data
    • 8 (Close)
      • This closes the Websocket
    • 9 (Ping)
      • Server pings client to check if client is still reachable
    • 10 (Pong)
      • Client responds with a Pong and the Ping Data to validate its reachability
  4. MASK
    • Most significant bit of the 2nd byte, tells you if the payload has been masked. A Server must not mask any frame!
  5. Payload Length (This is where things can get complicated)
    • Take the 2nd byte and read every bit except the Most significant bit
      • Byte is 125 or fewer that's your length
      • Byte is 126
        • Your length is an uint16 of byte 3 and 4
      • Byte is 127
        • Your length is a uint64 of byte 3 to 8
  6. Masking key
    • Only exists if the MASK bit is set
    • The next 4 bytes is the masking key, this key is used to decode the payload
  7. Payload
    • This is not the whole payload if the FIN bit is set
    • Payload can be decoded either as Text (UTF-8) or Binary (Can be any data)
    • The payload needs to be masked if the MASK bit is set

Steps to decode a Websocket Frame


  1. Get the FIN bit
    • Get the first byte and & it with 127, the result is your FIN bit
  2. Get the OpCode (The OpCode tells you what type of frame you have)
    • Get the first byte and & it with 15, the result is your opcode
  3. Get the MASK Bit
    • Get the second byte and & it with 127, if it is 127 you have a masking key
  4. Get the payload length
    • Take the 2nd byte and read every bit except the Most significant bit
      • Byte is 125 or less thats your length
      • Byte is 126
        • Your length is an uint16 of byte 3 and 4
      • Byte is 127
        • Your length is a uint64 of byte 3 to 8
  5. Get the masking key
    • The next 4 bytes is the masking key, this key is used to decode the payload
  6. Payload
    • The payload is length big and is starting from the masking key
    • If the MASK bit is set the payload needs to be masked
      • To demask the payload you just need to xor operation on every byte with the masking key on index count modulo 4

int count = 0; for (int i = dataIndex; i < totalLength; i++) { frameData[i] = (byte)(frameData[i] ^ key[count % 4]); count++; }

Interpret the data


  • Text
    • Data needs to be interpreted as UTF-8 Text
  • Binary
    • This data needs to be interpreted depending on what data you await, this can be achieved by adding a byte to the start of the payload and then interpret it accordingly.
like image 79
David Gölzhäuser Avatar answered Sep 09 '26 21:09

David Gölzhäuser