首先附上答案:
在wireshark抓包的时候,发现一个data帧包含了END_STREAM标志位,可这个流却依然没有结束,仍在继续传输数据帧 —— 这是因为流的状态从open变成了half_closed(local),在这个状态下,流只可以发送Window_Updata,Priority,RST_Stream帧,可以接收任意帧。
frame中帧的标志位(flags)
一个frame是HTTP/2通信中最小的数据传输单元,frame的定义如下:
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
其中Flags为帧的标志位,如END_HEADERS为0x04(标志着头部数据的结束),END_STREAM为0x01(标志着流的结束)。
附上stream的简单转换过程
Stream的状态转换
+--------+
send PP | | recv PP
,--------| idle |--------.
/ | | \
v +--------+ v
+----------+ | +----------+
| | | send H / | |
,------| reserved | | recv H | reserved |------.
| | (local) | | | (remote) | |
| +----------+ v +----------+ |
| | +--------+ | |
| | recv ES | | send ES | |
| send H | ,-------| open |-------. | recv H |
| | / | | \ | |
| v v +--------+ v v |
| +----------+ | +----------+ |
| | half | | | half | |
| | closed | | send R / | closed | |
| | (remote) | | recv R | (local) | |
| +----------+ | +----------+ |
| | | | |
| | send ES / | recv ES / | |
| | send R / v send R / | |
| | recv R +--------+ recv R | |
| send R / `----------->| |<-----------' send R / |
| recv R | closed | recv R |
`----------------------->| |<----------------------'
+--------+
send: endpoint sends this frame
recv: endpoint receives this frame
H: HEADERS frame (with implied CONTINUATIONs)
PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
ES: END_STREAM flag
R: RST_STREAM frame
half closed (local/remote)
half closed (local/remote)
状态half closed (local)与half closed (remote)中的local与remote的区别,完全是基于各端自己的视角。对于同一个流的两端,如果一端认为这个流的状态是half closed (local),那么另一端只能认为这个流的状态是half closed (remote)。
处于half closed (local)状态的流只能被用于发送WINDOW_UPDATE,PRIORITY和RST_STREAM帧,但可以被用于接收任何类型的帧。相对应地,处于half closed (remote)状态的流只能被用于接收WINDOW_UPDATE,PRIORITY和RST_STREAM帧,但可以被用于发送任何类型的帧。