HTTP 2 协议学习与实战
字数 1097 2025-08-09 18:43:59

HTTP/2 协议学习与实战

HTTP协议基础问题

在网络编程中,存在两个常见问题:

  1. 数据粘包:多个数据包粘连在一起,难以区分边界
  2. 数据不完整:数据包在传输过程中丢失部分内容

传输协议通过设计特定的数据结构来解决这些问题。

HTTP/1.x的局限性

HTTP/1.x的主要缺点:

  • 每次请求响应后都会断开连接
  • 反复进行TCP三次握手和四次挥手
  • 服务器无法主动推送

HTTP/2的核心改进

HTTP/2在不改变HTTP语义、方法、状态码、URL和首部字段的情况下:

  • 支持长连接
  • 采用二进制分帧传输方式
  • 在应用层(HTTP)和传输层(TCP)之间增加二进制分帧层
  • 实现低延迟高吞吐量

基础知识准备

数据表示基础

  1. 字节与比特

    • 1字节(byte) = 8比特(bit)
    • 1字节最大值为127 (0111 1111)
  2. 原码、反码和补码

    • 原码:最高位是符号位(0正1负),其余表示数值
    • 反码:正数同原码;负数符号位不变,其余取反
    • 补码:正数同原码;负数在反码末位加1
    • 计算机系统中数值一律用补码表示和存储
  3. & 0xFF的作用

    • 保证补码一致性
    • 只保留低8位数据
    • 示例:
      byte b = -127;  // 补码: 1000 0001
      int a = b;      // 补码扩展: 1111 1111 1111 1111 1111 1111 1000 0001
      a = b & 0xFF;   // 结果: 0000 0000 0000 0000 0000 0000 1000 0001 (129)
      

移位运算符

  1. 左移(<<):低位补0

    int a = 1;      // 0000 0001
    int b = a << 1; // 0000 0010 (2)
    
  2. 右移(>>):高位补符号位

    int b = 2;      // 0000 0010
    int c = b >> 1; // 0000 0001 (1)
    

数据分片与合并

存储大数示例(201314)

// 二进制: 0000 0011 0001 0010 0110 0010
byte a = (byte) 201314;         // 0110 0010 (98) - 取最低8位
byte b = (byte) (201314 >> 8);  // 0001 0010 (18) - 取9-16位
byte c = (byte) (201314 >> 16); // 0000 0011 (3)  - 取17-24位

使用或运算符(|)合并

long merged = (((long)a) << 16) | (((long)b) << 8) | ((long)c);

HTTP/2帧结构

HTTP/2通信的最小单位是帧,所有帧共享8字节首部:

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                      ...
+---------------------------------------------------------------+

字段说明:

  1. Length (24位):帧有效载荷长度

    • 默认不大于2^14(16,384)
    • 可通过SETTINGS_MAX_FRAME_SIZE设置更大值(16,384-16,777,215)
  2. Type (8位):帧类型

    • 决定帧格式和语义
    • 未知类型帧应被忽略
  3. Flags (8位):帧类型特定的布尔标志

  4. R (1位):保留位,必须为0

  5. Stream Identifier (31位):流标识符

    • 0x0保留用于整个连接的帧
    • 流:连接中的虚拟通道,承载双向消息,有唯一整数ID
  6. Frame Payload:实际数据

HTTP/2实战实现

简化帧设计

+---------------------+---------------------+
| Length (3字节)      | Type (1字节)        |
+---------------------+---------------------+
| Payload (变长)                            |
+-------------------------------------------+

核心代码实现

Frame抽象类

public abstract class Frame {
    public static final int HEADER_LENGTH_SIZE = 3;  // 长度字段大小
    public static final int HEADER_TYPE_SIZE = 1;   // 类型字段大小
    public static final int HEADER_SIZE = HEADER_LENGTH_SIZE + HEADER_TYPE_SIZE;
    public static final int MAX_CAPACITY = (int) Math.pow(2, HEADER_LENGTH_SIZE * 8) - 1;
    public static final byte TYPE_STRING_FRAME = 11; // 字符串类型标识
}

Receive接收处理

public class Receive {
    public static Receive handler(InputStream inputStream) throws IOException {
        byte[] header = new byte[Frame.HEADER_SIZE];
        int headerReadCount = inputStream.read(header);
        
        int length = getLength(header);  // 解析长度
        int type = getType(header);      // 解析类型
        
        byte[] body = new byte[length];
        int bodyReadCount = inputStream.read(body);
        
        return new Receive(length, type, body);
    }
    
    // 解析长度:3字节合并为整数
    public static int getLength(byte[] header) {
        return ((header[0] & 0xFF) << 16 | (header[1] & 0xFF) << 8 | header[2] & 0XFF);
    }
    
    // 解析类型
    public static int getType(byte[] header) {
        return header[3];
    }
}

Sender发送处理

public class Sender {
    public Sender(byte type, String data) {
        payload = data.getBytes();
        length = payload.length;
        
        // 设置长度字段(3字节)
        header[0] = (byte) (payload.length >> 16);
        header[1] = (byte) (payload.length >> 8);
        header[2] = (byte) payload.length;
        
        // 设置类型字段
        header[3] = type;
        
        // 合并头部和载荷
        packet = new byte[length + Frame.HEADER_SIZE];
        System.arraycopy(header, 0, packet, 0, Frame.HEADER_SIZE);
        System.arraycopy(payload, 0, packet, Frame.HEADER_SIZE, length);
    }
}

Server端示例

public class Demo1 {
    public static void main(String[] args) {
        ServerSocket serverSocket = new ServerSocket(8848);
        Socket accept = serverSocket.accept();
        InputStream inputStream = accept.getInputStream();

        Receive receive = Receive.handler(inputStream);
        
        if (receive.type != Frame.TYPE_STRING_FRAME) {
            throw new RuntimeException("Invalid packet type");
        }

        System.out.println(new String(receive.body));
    }
}

Client端示例

public class Demo2 {
    public static void main(String[] args) {
        Socket socket = new Socket("127.0.0.1", 8848);
        Sender sender = new Sender(Frame.TYPE_STRING_FRAME, "Hello World");
        
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(sender.getPacket());
    }
}

总结

HTTP/2通过二进制分帧机制解决了HTTP/1.x的性能问题:

  1. 使用帧结构明确数据边界,解决粘包问题
  2. 通过长度字段确保数据完整性
  3. 流标识实现多路复用
  4. 二进制格式提高解析效率

实现自定义协议时需要注意:

  • 合理设计帧结构
  • 正确处理字节序和数据类型转换
  • 考虑大端小端问题
  • 实现完善错误处理机制
HTTP/2 协议学习与实战 HTTP协议基础问题 在网络编程中,存在两个常见问题: 数据粘包 :多个数据包粘连在一起,难以区分边界 数据不完整 :数据包在传输过程中丢失部分内容 传输协议通过设计特定的数据结构来解决这些问题。 HTTP/1.x的局限性 HTTP/1.x的主要缺点: 每次请求响应后都会断开连接 反复进行TCP三次握手和四次挥手 服务器无法主动推送 HTTP/2的核心改进 HTTP/2在不改变HTTP语义、方法、状态码、URL和首部字段的情况下: 支持长连接 采用二进制分帧传输方式 在应用层(HTTP)和传输层(TCP)之间增加二进制分帧层 实现低延迟高吞吐量 基础知识准备 数据表示基础 字节与比特 : 1字节(byte) = 8比特(bit) 1字节最大值为127 (0111 1111) 原码、反码和补码 : 原码 :最高位是符号位(0正1负),其余表示数值 反码 :正数同原码;负数符号位不变,其余取反 补码 :正数同原码;负数在反码末位加1 计算机系统中数值一律用补码表示和存储 & 0xFF的作用 : 保证补码一致性 只保留低8位数据 示例: 移位运算符 左移(<<) :低位补0 右移(>>) :高位补符号位 数据分片与合并 存储大数示例(201314) : 使用或运算符(|)合并 : HTTP/2帧结构 HTTP/2通信的最小单位是帧,所有帧共享8字节首部: 字段说明: Length (24位):帧有效载荷长度 默认不大于2^14(16,384) 可通过SETTINGS_ MAX_ FRAME_ SIZE设置更大值(16,384-16,777,215) Type (8位):帧类型 决定帧格式和语义 未知类型帧应被忽略 Flags (8位):帧类型特定的布尔标志 R (1位):保留位,必须为0 Stream Identifier (31位):流标识符 0x0保留用于整个连接的帧 流:连接中的虚拟通道,承载双向消息,有唯一整数ID Frame Payload :实际数据 HTTP/2实战实现 简化帧设计 核心代码实现 Frame抽象类 : Receive接收处理 : Sender发送处理 : Server端示例 : Client端示例 : 总结 HTTP/2通过二进制分帧机制解决了HTTP/1.x的性能问题: 使用帧结构明确数据边界,解决粘包问题 通过长度字段确保数据完整性 流标识实现多路复用 二进制格式提高解析效率 实现自定义协议时需要注意: 合理设计帧结构 正确处理字节序和数据类型转换 考虑大端小端问题 实现完善错误处理机制