Netty Http2 协议序列之--初始化链接
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,客户端解析升级请求的响应,满足如下两个条件认为可以升级成功:
- status code:101
- header: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.pngHttp Frame
http2 所有的数据包都是通过一个一个的Frame 来交互的,也就是http2 交互的最小单位
Frame length(3) + Type(1) + flag(1) + streamId(4) + body
多路复用 StreamId
http2 为了支持多路复用,设计了streamid 这个来区别同一个无链接上的每个不同的请求,streamid 可以自己设置,不设置netty回自动生成
- 客户端生成的streamid需要满足奇数的条件
- 服务端生成的streamid需要满足偶数的条件
客户端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 的链接升级和初始化原理。