Principles and Implementation of Dependency Injection and Inversion of Control (IoC) Containers

Principles and Implementation of Dependency Injection and Inversion of Control (IoC) Containers (Repeated title, already covered)

Principles and Implementation of the WebSocket Protocol

Description
WebSocket is a protocol for full-duplex communication over a single TCP connection, allowing both the server and client to actively push data to each other, thereby avoiding the overhead of HTTP polling. Its core principle involves establishing a persistent connection via an HTTP upgrade request and then transmitting data in Frame format. Mastering the implementation mechanism of WebSocket is crucial for building real-time applications (such as chat systems, online games).

Problem-Solving Process

  1. Handshake Phase

    • Client Request: The client sends an HTTP upgrade request containing the following header fields:
      GET /chat HTTP/1.1
      Host: example.com
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==  # Randomly generated 16-byte Base64 encoded string
      Sec-WebSocket-Version: 13
      
    • Server Response: After validating the request, the server returns status code 101 (Switching Protocols) and generates a response key:
      HTTP/1.1 101 Switching Protocols
      Upgrade: websocket
      Connection: Upgrade
      Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=  # Calculated based on the client's Key
      
      • Calculation method: Concatenate the client's Sec-WebSocket-Key with the fixed GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", perform SHA-1 hashing, and finally Base64 encode the result.
  2. Data Frame Format (Frame Protocol)
    After a successful handshake, both parties communicate via binary frames. The format of a frame is as follows (unit: bits):

    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 ...                |
    +---------------------------------------------------------------+
    
    • Key Fields:
      • FIN (1 bit): Indicates whether this is the final frame of a message.
      • opcode (4 bits): Frame type (e.g., 1=text, 2=binary, 8=close connection).
      • MASK (1 bit): Frames sent from client to server must be masked (frames from server to client do not require masking).
      • Payload Len (7 bits): Data length. If length ≥ 126, subsequent extended bytes represent the actual length.
      • Masking-key (4 bytes): Exists only when MASK=1, used to decode the payload data.
  3. Mask Decoding and Encoding

    • Client Sending: Data must be encoded using XOR with the Masking-key. Decoding formula:
      decoded[i] = payload[i] XOR masking_key[i % 4]
      
    • Server Sending: No masking required; raw data is sent directly.
  4. Implementation Key Points

    • Server Example (Pseudocode):
      # 1. Handshake Response
      def handle_handshake(request):
          key = request.headers['Sec-WebSocket-Key']
          accept_key = base64encode(sha1(key + GUID))
          response = f"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {accept_key}\r\n\r\n"
          socket.send(response)
      
      # 2. Parse Frame
      def parse_frame(data):
          fin = (data[0] & 0x80) == 0x80
          opcode = data[0] & 0x0F
          masked = (data[1] & 0x80) == 0x80
          payload_len = data[1] & 0x7F
          offset = 2
          if payload_len == 126:
              payload_len = (data[2] << 8) + data[3]
              offset += 2
          elif payload_len == 127:
              payload_len = ...  # Handle 64-bit length
              offset += 8
          if masked:
              masking_key = data[offset:offset+4]
              offset += 4
              payload = data[offset:offset+payload_len]
              # Apply mask decoding
              decoded = bytearray(payload[i] ^ masking_key[i % 4] for i in range(len(payload)))
          return decoded
      
    • Heartbeat Mechanism (Ping/Pong): Keep the connection alive using opcode 9 (Ping) and 10 (Pong).
  5. Application Scenarios

    • Real-time message推送 (e.g., chat rooms, notification systems).
    • Multiplayer online games, collaborative editing tools.
    • Real-time financial market data updates.

Summary
WebSocket achieves efficient bidirectional communication through a lightweight frame protocol, avoiding the latency of HTTP polling. Understanding its handshake process, frame structure, and masking mechanism is fundamental to implementing custom WebSocket services or debugging real-time applications.