Netty Http2 协议序列之--初始化链接

2018-07-08  本文已影响0人  绝尘驹

H2

H2C 是代表运行在ssl安全协议之上,https 升级用该协议标示

H2c

h2c 代表运行在名为tcp协议之上,这种时候没有https的链接。

协议切换

1 发生初始化http get 请求

客户端通过http1 发送一个带有如下的header:

GET / HTTP/1.1
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>

服务端如果支持http2,则通过响应吗101 来响应客户端,并在响应头带上upgrade:h2c,客户端解析升级请求的响应,满足如下两个条件认为可以升级成功:

2 Connect Preface

http2 升级成功后,必须先发送一个24自己的字符串Preface,perface就像一个暗号,如果客户端没有这个Preface,则认为是伪装的,服务端会关闭链接,不和你玩了。

private void sendPreface(ChannelHandlerContext ctx) throws Exception {
            if (prefaceSent || !ctx.channel().isActive()) {
                return;
            }

            prefaceSent = true;

            final boolean isClient = !connection().isServer();
             //在升级完成后,客户端会发送preface
            if (isClient) {
                // Clients must send the preface string as the first bytes on the connection.
     ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            }
             //客户端和服务端都会发送inital setting frame         
            // Both client and server must send their initial settings.
            encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener(
                    ChannelFutureListener.CLOSE_ON_FAILURE);
           
            if (isClient) {
                // If this handler is extended by the user and we directly fire the userEvent from this context then
                // the user will not see the event. We should fire the event starting with this handler so this class
                // (and extending classes) have a chance to process the event.
                userEventTriggered(ctx, Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE);
            }
        }

3 Init Setting Frame

客户端在发送完Preface后,可以立即发生init setting frame,不需要等服务端确认后再发,服务端在升级到http2 协议后,读第一个数据包时先检查Preface,然后检查是否时setting frame,如果不是则报链接错误。

public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
           try {
                //1 检查客户端的Preface,如果时客户端,则直接返回true。
                //2 检查第一个是否时setting frame,
               if (ctx.channel().isActive() && readClientPrefaceString(in) && verifyFirstFrameIsSettings(in)) {
                   // After the preface is read, it is time to hand over control to the post initialized decoder.
                   //在读完第一个请求后,会切换byteDecoder,即后面的请求就不校验Preface了,而是直接读frame
                   byteDecoder = new FrameDecoder();
                   byteDecoder.decode(ctx, in, out);
               }
           } catch (Throwable e) {
               onError(ctx, false, e);
           }
}

完整过程如下图所示:

netty-http2-communition.png

Http Frame

http2 所有的数据包都是通过一个一个的Frame 来交互的,也就是http2 交互的最小单位

Frame length(3) + Type(1) + flag(1) + streamId(4) + body

多路复用 StreamId

http2 为了支持多路复用,设计了streamid 这个来区别同一个无链接上的每个不同的请求,streamid 可以自己设置,不设置netty回自动生成

客户端streamid 看如下代码:高手是怎么判断奇数的

public boolean isValidStreamId(int streamId) {
     return streamId > 0 && server == ((streamId & 1) == 0);
}

客户端server为false,服务端server为true,所以客户端为奇数时,streamId & 1 的结果为1,1不等于0,即 ((streamId & 1) == 0) 为false。

http2 协议还是蛮复杂的一个协议,打算写一个序列,这是开篇,简单的分析了下http2 的链接升级和初始化原理。

上一篇下一篇

猜你喜欢

热点阅读