websocket数据帧格式

客户端、服务端数据的交换,离不开数据帧格式的定义。因此,在实际讲解数据交换之前,我们先来看下WebSocket的数据帧格式。

WebSocket客户端、服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message)。

  1. 发送端:将消息切割成多个帧,并发送给服务端;

  2. 接收端:接收消息帧,并将关联的帧重新组装成完整的消息;

本节的重点,就是讲解数据帧的格式。

数据帧格式概览

下面给出了WebSocket数据帧的统一格式。熟悉TCP/IP协议的同学对这样的图应该不陌生。

  1. 从左到右,单位是比特。比如FINRSV1各占据1比特,opcode占据4比特。

  2. 内容包括了标识、操作代码、掩码、数据、数据长度等。


数据帧格式讲解(以上图片介绍):

FIN:1个比特。

如果是1,表示这是消息(message)的最后一个分片(fragment),如果是0,表示不是是消息(message)的最后一个分片(fragment)。

RSV1, RSV2, RSV3:各占1个比特。

一般情况下全为0。当客户端、服务端协商采用WebSocket扩展时,这三个标志位可以非0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用WebSocket扩展,连接出错。

Opcode: 4个比特。

操作代码,Opcode的值决定了应该如何解析后续的数据载荷(data payload)。如果操作代码是不认识的,那么接收端应该断开连接(fail the connection)。可选的操作代码如下:

  • %x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。

  • %x1:表示这是一个文本帧(frame)

  • %x2:表示这是一个二进制帧(frame)

  • %x3-7:保留的操作代码,用于后续定义的非控制帧。

  • %x8:表示连接断开。

  • %x9:表示这是一个ping操作。

  • %xA:表示这是一个pong操作。

  • %xB-F:保留的操作代码,用于后续定义的控制帧。

Mask: 1个比特。

表示是否要对数据载荷进行掩码操作。从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作。

如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。

如果Mask是1,那么在Masking-key中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask都是1。

Payload length:数据载荷的长度,单位是字节。为7位,或7+16位,或1+64位。

假设数Payload length === x,如果

  • x为0~126:数据的长度为x字节。

  • x为126:后续2个字节代表一个16位的无符号整数,该无符号整数的值为数据的长度。

  • x为127:后续8个字节代表一个64位的无符号整数(最高位为0),该无符号整数的值为数据的长度。

此外,如果payload length占用了多个字节的话,payload length的二进制表达采用网络序(big endian,重要的位在前)。

Masking-key:0或4字节(32位)

所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask为1,且携带了4字节的Masking-key。如果Mask为0,则没有Masking-key。

备注:载荷数据的长度,不包括mask key的长度。

Payload data:(x+y) 字节

载荷数据:包括了扩展数据、应用数据。其中,扩展数据x字节,应用数据y字节。

扩展数据:如果没有协商使用扩展的话,扩展数据数据为0字节。所有的扩展都必须声明扩展数据的长度,或者可以如何计算出扩展数据的长度。此外,扩展如何使用必须在握手阶段就协商好。如果扩展数据存在,那么载荷数据长度必须将扩展数据的长度包含在内。

应用数据:任意的应用数据,在扩展数据之后(如果存在扩展数据),占据了数据帧剩余的位置。载荷数据长度 减去 扩展数据长度,就得到应用数据的长度。

C++数据帧代码实例 : 本代码使用了C++ dfm库

#include <dfm/dfm.h>  
#include <iostream>  
  
int main() {  
    // 创建一个数据帧  
    dfm::DataFrame df = dfm::DataFrame::from_rows(  
        dfm::Row({ "Tom", 20, 'A' }),  
        dfm::Row({ "Nick", 21, 'B' }),  
        dfm::Row({ "John", 19, 'A' }),  
        dfm::Row({ "Peter", 18, 'C' })  
    );  
  
    // 给数据帧添加列名  
    df.set_column_names({"Name", "Age", "Grade"});  
  
    // 显示数据帧  
    std::cout << df << std::endl;  
  
    // 选择列  
    auto age_column = df.select("Age");  
    std::cout << "Age column:" << std::endl;  
    std::cout << age_column << std::endl;  
  
    // 选择多列  
    auto multi_columns = df.select({"Name", "Grade"});  
    std::cout << "Multiple columns:" << std::endl;  
    std::cout << multi_columns << std::endl;  
  
    // 选择行  
    auto row_0 = df.at(0);  
    std::cout << "Row 0:" << std::endl;  
    std::cout << row_0 << std::endl;  
  
    // 条件选择  
    auto young_people = df.where(df["Age"] < 20);  
    std::cout << "People younger than 20:" << std::endl;  
    std::cout << young_people << std::endl;  
  
    // 修改数据  
    df.at(0, "Name") = "Tommy";  
    std::cout << "Modified data frame:" << std::endl;  
    std::cout << df << std::endl;  
  
    // 添加新列  
    df.insert_column("Height", {175, 180, 165, 170});  
    std::cout << "Added new column:" << std::endl;  
    std::cout << df << std::endl;  
  
    // 删除列  
    df.remove_column("Height");  
    std::cout << "Removed column:" << std::endl;  
    std::cout << df << std::endl;  
  
    // 删除行  
    df.remove_row(0);  
    std::cout << "Removed row:" << std::endl;  
    std::cout << df << std::endl;  
  
    return 0;  
}

我们使用了dfm::DataFrame类来创建一个数据帧,并演示了如何选择列、行,如何修改数据,如何添加和删除列和行

好了 本篇文章就到这里结束了 在这里小编向大家推荐一个性价比高的课程:

https://xxetb.xetslk.com/s/2PjJ3T

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebSocket 是一种协议,它使用 TCP 作为传输层协议,使得 Web 应用程序能够实现双向通信。WebSocket 的数据格式是基于的,每个都有一个特定的格式,用于表示不同类型的消息。以下是 WebSocket 数据帧格式: ``` 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 ... | +---------------------------------------------------------------+ ``` 下面是各个字段的详细说明: - FIN:1 位,表示此是否为消息的最后一。如果是最后一,则为 1;否则为 0。 - RSV1、RSV2、RSV3:3 位,暂时保留,目前默认为 0。 - Opcode:4 位,表示此的类型,如下所示: - 0x0 表示连续 - 0x1 表示文本 - 0x2 表示二进制 - 0x8 表示连接关闭 - 0x9 表示 Ping - 0xA 表示 Pong - MASK:1 位,表示 Payload 数据是否经过掩码处理。如果是,则为 1;否则为 0。 - Payload len:7 位,表示 Payload 数据的长度。如果 Payload 数据的长度小于等于 125 字节,则该字段的值就是 Payload 数据的长度。如果 Payload 数据的长度在 126 字节和 65535 字节之间,则该字段的值为 126,并且后面接着 2 个字节表示 Payload 数据的真实长度。如果 Payload 数据的长度大于 65535 字节,则该字段的值为 127,并且后面接着 8 个字节表示 Payload 数据的真实长度。 - Extended payload length:如果 Payload len 字段的值为 126 或 127,则使用 Extended payload length 表示 Payload 数据的真实长度。如果 Payload len 字段的值为 126,则 Extended payload length 占用 2 个字节;如果 Payload len 字段的值为 127,则 Extended payload length 占用 8 个字节。 - Masking-key:如果 MASK 字段的值为 1,则表示 Payload 数据经过掩码处理,需要使用 Masking-key 对 Payload 数据进行解码。Masking-key 占用 4 个字节。 - Payload Data:表示数据内容。 总体来说,WebSocket 数据帧格式比较复杂,但是由于它是底层协议,对于开发人员来说,只需要使用 WebSocket API 提供的接口,就可以方便地进行双向通信了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值