Okhttp Expected a SETTINGS frame but was WINDOW UPDATE

949 阅读4分钟

问题:okhttp 上传文件中报错

IOException : java.io.IOException: Expected a SETTINGS frame 
but was WINDOW_UPDATE when connected to Charles (proxy)

原因: HTTP/2 流量控制

HTTP/2 中采用了一种流量控制机制,其目标是:在不改变协议的情况下允许使用多种流量控制算法。

1.流量控制是特定于一个连接的。每种类型的流量控制都是在单独的一条的两个端点之间的,并不是在整个端到端的路径上的。(这里的一条指的是HTTP连接的一条,而不是IP路由的一条)

2.流量控制是基于 WINDOW_UPDATE 帧的。接收方公布自己打算在每个流以及整个连接上分别接收多少字节。这是一个以信用为基础的方案。

3.流量控制是有方向的,由接收者全面控制。接收方可以为每个流和整个连接设置任意的窗口大小。发送方必须尊重接收方设置的流量控制限制。客户方、服务端和中间代理作为接收方时都独立地公布各自的流量控制窗口,作为发送方时都遵守对端的流量控制设置。

4.无论是新流还是整个连接,流量控制窗口的初始值是65535字节。

5.帧的类型决定了流量控制是否适用于帧。目前,只有DATA帧服从流量控制,所有其它类型的帧并不消耗流量控制窗口的空间。这保证了重要的控制帧不会被流量控制阻塞。

6.流量控制不能被禁用。

7.HTTP/2 只定义了 WINDOW_UPDATE 帧的格式和语义,并没有规定接收方如何决定何时发送帧、发送什么样的值,也没有规定发送方如何选择发送包。具体实现可以选择任何满足需求的算法。

看来 HTTP/2 中采用了 WINDOW_UPDATE 这种特殊的帧来实现对流量控制的支持,它的用途是通知对端增加窗口的大小,其数据中会指定增加的窗口大小,从而告诉对方自己有足够的空间处理新的数据。

在 OkHttp 中实现了 HTTP/2 中的流量机制,它限制了同时能发送的数据大小,其默认值为 65535,当发送数据时,若窗口大小不足,则会进行阻塞,直到窗口有空闲大小。这样的设计我想是因为 HTTP/2 中采用了请求的多路复用机制,多个请求可以复用同一条连接进行并发地进行。如果同时在一条连接上进行的网络请求太多会对网络造成拥塞。因此为了保证网络的畅通性,对每条连接采取了这种窗口机制,限制了每条连接最大发送的数据量。

分析

流量控制窗口 (Flow Control Window)

HTTP/2中流量控制是通过每个流上每个发送端,保持一个窗口来实现的。流量控制窗口是一个简单的整数值,指示发送端被允许传输的字节数;因此,它的大小是接收端的缓存能力的衡量。 流量控制窗口对流和连接的流量控制窗口都适用。发送端绝对不能发送超出接收端广播的流量控制窗口大小的可用空间长度的受流量控制影响的帧。在各个流量控制窗口中没有可用空间时,可以发送带有END_STREAM标记的长度为0的帧(例如,空数据帧)。 WINDOW_UPDATE帧 HTTP/2定义的帧类型的一种,用途是通知对端增加窗口值,WINDOW_UPDATE会指定增加的大小

算法描述: 发送端保有一个流量控制窗口(window)初始值。初始值的设定请参考SETTING 帧的 SETTINGS_INITIAL_WINDOW_SIZE 发送端每发送一个DATA帧,就把window递减,递减量为这个帧的大小。如果当前window小于帧大小,那么这个帧就必须被拆分到不大于window,如果window等于0,就不能发送任何帧 接收端可以发送 WINDOW_UPDATE帧给发送端,发送端以帧内指定的Window Size Increment作为增量,加到window上。

理解HTTP/2流量控制

解决方案

不要用代理