Alexlin

V1

2022/01/28阅读:104主题:极简黑

CoAP如何传输大数据-块传输

最新消息请关注公众 "晓谈物联"

为什么CoAP要做块传输

在前面的篇文章<CoAP-理论初识>我们知道CoAP是基于UDP底层的应用协议,而UDP的报文长度理论上只有 -1个字节,去掉头部(IP&端口)信息,则payload部分的长度为: -1-8-20;
而实际上UDP的大小还受到MTU,系统的UDP缓存大小的影响,所以实际上一把UDP的应用层数据要远小于64K,关于UDP的特性后续有机会再写一写;
由于UDP的限制,导致CoAP无法一次性发太长的数据,为了解决这个问题,CoAP引入了块传输的机制;
所谓块传输也就是把要发的数据切成一块一块进行发送;
下面就围绕着CoAP是如何切数据如何传块数据进行展开

CoAP为块传输添加的元素

CoAP 的基础上定义了块传输协议 ,具体的协议定制可以参考以下链接
https://datatracker.ietf.org/doc/html/rfc7959

为了进行块传输,CoAP引入了几个新选项:Block1 Block2 Size1 Size2和新的响应码2.31 4.08 4.13
定义如下

2.1、 Block1 & Block2

+-----+---+---+---+---+--------+--------+--------+---------+
| No. | C | U | N | R | Name   | Format | Length | Default |
+-----+---+---+---+---+--------+--------+--------+---------+
|  23 | C | U | - | - | Block2 | uint   |    0-3 | (none)  |
|     |   |   |   |   |        |        |        |         |
|  27 | C | U | - | - | Block1 | uint   |    0-3 | (none)  |
+-----+---+---+---+---+--------+--------+--------+---------+

2.2、 Size1 & Size2

+-----+---+---+---+---+-------+--------+--------+---------+
| No. | C | U | N | R | Name  | Format | Length | Default |
+-----+---+---+---+---+-------+--------+--------+---------+
|  60 |   |   | x |   | Size1 | uint   |    0-4 | (none)  |
|     |   |   |   |   |       |        |        |         |
|  28 |   |   | x |   | Size2 | uint   |    0-4 | (none)  |
+-----+---+---+---+---+-------+--------+--------+---------+

2.3、 响应码

+------+---------------------------+-----------+
| Code | Description               | Reference |
+------+---------------------------+-----------+
| 2.31 | Continue                  | RFC 7959  |
| 4.08 | Request Entity Incomplete | RFC 7959  |
| 4.13 | Request Entity Too Large  | RFC 7252  |
+------+---------------------------+-----------+

Block 选项

Block是进行块传输的描述,其中Block为可变长,取值范围为0~3个字节; Block有两个选项,分别是Block1和Block2,但是格式都一样,如下

0
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|  NUM  |M| SZX |
+-+-+-+-+-+-+-+-+
0                   1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          NUM          |M| SZX |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
0                   1                   2
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                   NUM                 |M| SZX |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

3.1、 描述性用法: Block1+Request或Block2+Response

表示正在进行块传输,NUM描述正在传输的Payload在整个Body的哪个部分,这种组合称为描述性用法(Descriptive usage) 直白点的说,描述性用法就是用来表示正在进行传输块的大小以及序号等属性
具体的属性如下

  • NUM:表示当前Message的Payload在整个Body中的编号,从0开始;
  • M: 表示是否有更多的块数据才能完成Body的传输;
  • SZX:取值为0~6
    • 当M为1时,当前的Message的Payload大小为 ;
    • 当M为0时,当前的Message的Payload大小为1到 ;

3.2、 控制性用法: Block1+Response或Block2+Request

表示提供了如何形成或处理有效负载的附加控制,这种组合称为控制性用法(Control Usage)
说人话就是: 控制性用法就是用来控制/更改块传输的属性,具体的属性见下面

3.2.1、 Block2+Request

  • NUM:表示期望Response传输的块号
  • M: 没意义,设置为0;
  • SZX
    • NUM为0时,表示希望采用的块大小;
    • NUM非0时,直接采用上一个接收到的Response块的大小;

说明

  1. Block2作为描述性用法时,也就是Block2+Response这种组合,Client可以通过Block2+Request(NUM=0,M=0,SZX:期望值) 的方式通知Server,Client希望采用的块大小,且接下来的Request中的NUM,为本次接收到的NUM+1,然后后续Server按照Client的SZX进行块传送;
  2. Block2+Response这种组合发送给Client时,如果M不为0 (表示后续还需要跟多的数据) ,则Client需要继续发送携带Block2其中NUM=上一个Response中Block2的NUM+1,M=1,SZX=上一个Response中的Block2的SZX的Request,请求下一个块;

3.2.2、 Block1+Response

  • NUM:表示确认的块号
  • M: 若Block1+Request中的M为1,则Server可以选择是否单独对每个块执行操作,或者以原子方式处理整个主体的请求,或者是两者的任何混合;
    • 如果Response中M=1,表示这不是对Request的最终Response;
    • 如果Response中M=0,表示整个数据包已经发送完成,是最终的Response;
  • SZX:表面Server期望接收的块的最大值,如果Server通过Response+Block1组合指定了比初始交互更小的值,则后续Client需要使用该值进行后续的块传输;

Size1和Size2选项

Size1、Size2用于指示需要传输资源的总大小

4.1、 Size1选项

用于表示通过Request传输的资源大小;

  • Size1+Block1+Request : 表示Client请求Server的资源大小;
  • Size1+Response:一般出现在4.13的Response,表示Client请求的资源超出Server的大小,Size1为Server能处理的最大值;

4.2、 Size2选项

用于表示Response传输的资源大小;

  • Size2+Request,取值为0,用于Client向Server请求其提供的资源大小;
  • Size2+Response用于Server向Client指示通过块传输的资源大小;

响应码

  • 2.31 Continue,表明Server收到了Client的Request传输的块,期望继续发下一个块,但是目前还不到发最终Response
  • 4.08 Request Entity Incomplete,服务端未能按时收到完成的Body,可能是客户端未按顺序发送或者超时发送,也有其他原因,如类型不匹配等错误
  • 4.13 Request Entity Too Large,请求资源太长,超过服务端限制

举几个栗子

格式:2:0/0/64 = Block:/NUM/M/SZX

6.1、 1号栗子

Block2场景 Block-Wise

  1. Client向Server发起GET请求;
  2. Server响应,其中2:/0/1/128代表携带Block2,NUM=0,M=1,SZX=128; 大白话:这个一个块传输(Block2),这种第0(NUM)个序号数据块,后面还有数据块(M=1),这次的块大小为128个字节;
  3. Client根据第二个数据包发起请求参数为2:1/0/128获取下一个块数据,序号为1,期待块大小为128个字节;
  4. Server响应
  5. Client继续请求
  6. Server响应,此次,M值为0,代表整个数据资源已经传输完成;

6.2、 2号栗子

Block2场景 Block2

  1. Client向Server发起请求,且期望Server以64个字节最为块大小进行传输;
  2. Server响应Client,且以Client期望的块大小(64Byte)进行传输
  3. 后续流程跟上一个例子一样;

6.3、 3号栗子

Block2场景 Client通过Block2更改Server传输的块大小

  1. Client发起请求
  2. Server响应,其中响应参数为 2:0/1/128
  3. Client表示希望以SZX为64进行接收,且由于本次的SZX为128,相当于接收了两块64的数据,所以下个接收块为2,于是请求参数为2:2/1/64
  4. 后续交互同上例子a

6.4、 4号栗子

Block1场景

  1. Client发起块传输(参数为1:0/1/128),其中块序号为0,且后续有更多的块,本次块大小为128个字节;
  2. Server返回Response,携带Block1,表示收到了Client发送块参数为:1:0/1/128的数据;
  3. 同时Client继续发送后续数据,服务端依旧响应;

6.5、 5号栗子

Coap-Block1-Request-M0 这个栗子和4号栗子的区别在于Server响应的M设置为0,这种情况表明服务端的资源正在创建或者改动或者当前资源不可用;

6.6、 6号栗子

Server不服Client传输的大小,通过Response通知Client更改块大小,当然接收到的块序号通过后续更改的块大小来计算,也就是128/32=4; 所以下个块需要为4;

6.7、 7号栗子

Block1和Block2的组合 首先Client进行了POST请求,增加了一个资源,最终Server返回2.04资源被创建成功,且进行了块响应;

最后

最新消息请关注公众 "晓谈物联"

春节快到了,祝各位春节快乐~

参考

  1. https://datatracker.ietf.org/doc/html/rfc7959
  2. https://datatracker.ietf.org/doc/html/rfc7252
  3. https://wenku.baidu.com/view/1d210d711611cc7931b765ce050876323112746e.html
- END -

分类:

后端

标签:

C++

作者介绍

Alexlin
V1