网易首页 > 网易号 > 正文 申请入驻

netty系列之:搭建客户端使用http1.1的方式连接http2服务器

0
分享至

简介

对于http2协议来说,它的底层跟http1.1是完全不同的,但是为了兼容http1.1协议,http2提供了一个从http1.1升级到http2的方式,这个方式叫做cleartext upgrade,也可以简称为h2c。

在netty中,http2的数据对应的是各种http2Frame对象,而http1的数据对应的是HttpRequest和HttpHeaders。一般来说要想从客户端发送http2消息给支持http2的服务器,那么需要发送这些http2Frame的对象,那么可不可以像http1.1这样发送HttpRequest对象呢?

今天的文章将会给大家揭秘。

使用http1.1的方式处理http2

netty当然考虑到了客户的这种需求,所以提供了两个对应的类,分别是:InboundHttp2ToHttpAdapter和HttpToHttp2ConnectionHandler。

他们是一对方法,其中InboundHttp2ToHttpAdapter将接收到的HTTP/2 frames 转换成为HTTP/1.x objects,而HttpToHttp2ConnectionHandler则是相反的将HTTP/1.x objects转换成为HTTP/2 frames。这样我们在程序中只需要处理http1的对象即可。

他们的底层实际上调用了HttpConversionUtil类中的转换方法,将HTTP2对象和HTTP1对象进行转换。

处理TLS连接

和服务器一样,客户端的连接也需要区分是TLS还是clear text,TLS简单点,只需要处理HTTP2数据即可,clear text复杂点,需要考虑http升级的情况。

先看下TLS的连接处理。

首先是创建SslContext,客户端的创建和服务器端的创建没什么两样,这里要注意的是SslContextBuilder调用的是forClient()方法:

SslProvider provider =
SslProvider.isAlpnSupported(SslProvider.OPENSSL)? SslProvider.OPENSSL : SslProvider.JDK;
sslCtx = SslContextBuilder.forClient()
.sslProvider(provider)
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
// 因为我们的证书是自生成的,所以需要信任放行
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.applicationProtocolConfig(new ApplicationProtocolConfig(
Protocol.ALPN,
SelectorFailureBehavior.NO_ADVERTISE,
SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_2,
ApplicationProtocolNames.HTTP_1_1))
.build();

然后将sslCtx的newHandler方法传入到pipeline中:

pipeline.addLast(sslCtx.newHandler(ch.alloc(), CustHttp2Client.HOST, CustHttp2Client.PORT));

最后加入ApplicationProtocolNegotiationHandler,用于TLS扩展协议的协商:

pipeline.addLast(new ApplicationProtocolNegotiationHandler("") {
@Override
protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
ChannelPipeline p = ctx.pipeline();
p.addLast(connectionHandler);
p.addLast(settingsHandler, responseHandler);
return;
ctx.close();
throw new IllegalStateException("未知协议: " + protocol);

如果是HTTP2协议,则需要向pipline中加入三个handler,分别是connectionHandler,settingsHandler和responseHandler。

connectionHandler用于处理客户端和服务器端的连接,这里使用HttpToHttp2ConnectionHandlerBuilder来构建一个上一节提到的HttpToHttp2ConnectionHandler,用来将http1.1对象转换成为http2对象。

Http2Connection connection = new DefaultHttp2Connection(false);
connectionHandler = new HttpToHttp2ConnectionHandlerBuilder()
.frameListener(new DelegatingDecompressorFrameListener(
connection,
new InboundHttp2ToHttpAdapterBuilder(connection)
.maxContentLength(maxContentLength)
.propagateSettings(true)
.build()))
.frameLogger(logger)
.connection(connection)
.build();

但是连接其实是双向的,HttpToHttp2ConnectionHandler是将http1.1转换成为http2,它实际上是一个outbound处理器,我们还需要一个inbound处理器,用来将接收到的http2对象转换成为http1.1对象,这里通过添加framelistener来实现。

frameListener传入一个DelegatingDecompressorFrameListener,其内部又传入了前一节介绍的InboundHttp2ToHttpAdapterBuilder用来对http2对象进行转换。

settingsHandler用来处理Http2Settings inbound消息,responseHandler用来处理FullHttpResponse inbound消息。

这两个是自定义的handler类。

处理h2c消息

从上面的代码可以看出,我们在TLS的ProtocolNegotiation中只处理了HTTP2协议,如果是HTTP1协议,直接会报错。如果是HTTP1协议,则可以通过clear text upgrade来实现,也就是h2c协议。

我们看下h2c需要添加的handler:

private void configureClearText(SocketChannel ch) {
HttpClientCodec sourceCodec = new HttpClientCodec();
Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(connectionHandler);
HttpClientUpgradeHandler upgradeHandler = new HttpClientUpgradeHandler(sourceCodec, upgradeCodec, 65536);

ch.pipeline().addLast(sourceCodec,
upgradeHandler,
new CustUpgradeRequestHandler(this),
new UserEventLogger());
}

首先添加的是HttpClientCodec作为source编码handler,然后添加HttpClientUpgradeHandler作为upgrade handler。最后添加自定义的CustUpgradeRequestHandler和事件记录器UserEventLogger。

自定义的CustUpgradeRequestHandler负责在channelActive的时候,创建upgradeRequest并发送到channel中。

因为upgradeCodec中已经包含了处理http2连接的connectionHandler,所以还需要手动添加settingsHandler和responseHandler。

ctx.pipeline().addLast(custHttp2ClientInitializer.settingsHandler(), custHttp2ClientInitializer.responseHandler());
发送消息

handler配置好了之后,我们就可以直接以http1的方式来发送http2消息了。

首先发送一个get请求:

// 创建一个get请求
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, GETURL, Unpooled.EMPTY_BUFFER);
request.headers().add(HttpHeaderNames.HOST, hostName);
request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), scheme.name());
request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP);
request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.DEFLATE);
responseHandler.put(streamId, channel.write(request), channel.newPromise());

然后是一个post请求:

// 创建一个post请求
FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, POSTURL,
wrappedBuffer(POSTDATA.getBytes(CharsetUtil.UTF_8)));
request.headers().add(HttpHeaderNames.HOST, hostName);
request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), scheme.name());
request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP);
request.headers().add(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.DEFLATE);
responseHandler.put(streamId, channel.write(request), channel.newPromise());

和普通的http1请求没太大区别。

总结

通过使用InboundHttp2ToHttpAdapter和HttpToHttp2ConnectionHandler可以方便的使用http1的方法来发送http2的消息,非常方便。

本文的例子可以参考:learn-netty4

本文已收录于 http://www.flydean.com/30-netty-http2client-md/

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
喜欢把家里打扫得很干净的人,往往会有这3种命运,很准!

喜欢把家里打扫得很干净的人,往往会有这3种命运,很准!

品读时刻
2026-02-12 00:06:27
从冠军搭档到对簿公堂,卢鑫首次回应:我没赌,但真扛不住了

从冠军搭档到对簿公堂,卢鑫首次回应:我没赌,但真扛不住了

喜欢历史的阿繁
2026-02-15 03:50:42
女子谈释永信过往,她们姐妹住少林寺3天2夜,争着往释永信房间跑

女子谈释永信过往,她们姐妹住少林寺3天2夜,争着往释永信房间跑

江山挥笔
2025-07-29 16:50:59
侦破!肖战站票,杨幂背锅,辛芷蕾撕牌,微博之夜谁在抢C位?

侦破!肖战站票,杨幂背锅,辛芷蕾撕牌,微博之夜谁在抢C位?

布丁树洞
2026-02-13 12:50:07
真是人走茶凉!看了三只羊传媒的现状,才明白什么叫兔死狗烹

真是人走茶凉!看了三只羊传媒的现状,才明白什么叫兔死狗烹

聚焦真实瞬间
2026-02-10 20:45:40
C罗罢赛后首次公开亮相,却在现场被沙特人集体高喊“梅西”!

C罗罢赛后首次公开亮相,却在现场被沙特人集体高喊“梅西”!

氧气是个地铁
2026-02-13 23:25:18
高金素梅100万新台币交保后喊话:我不屈服!

高金素梅100万新台币交保后喊话:我不屈服!

看看新闻Knews
2026-02-13 23:56:03
迪士尼法务部出手,字节跳动Seedance 2.0被指盗用漫威星战角色

迪士尼法务部出手,字节跳动Seedance 2.0被指盗用漫威星战角色

三言科技
2026-02-14 10:30:04
3:0!索博复出即破门,萨拉赫传射状态回暖,利物浦轻松晋级16强

3:0!索博复出即破门,萨拉赫传射状态回暖,利物浦轻松晋级16强

阿超他的体育圈
2026-02-15 05:59:00
这7个行业,真的撑不下去了!裁员、关店、转行,正在同时发生

这7个行业,真的撑不下去了!裁员、关店、转行,正在同时发生

搬砖营Z
2025-12-17 23:14:10
悲催!贵州一男子哭诉孕妻因感冒去世,连同肚子里未出世的孩子…

悲催!贵州一男子哭诉孕妻因感冒去世,连同肚子里未出世的孩子…

火山詩话
2026-02-13 05:56:49
我国每年近千万人做肠镜!医生直言:做一次肠镜,或管十年无碍

我国每年近千万人做肠镜!医生直言:做一次肠镜,或管十年无碍

白宸侃片
2026-02-01 19:13:01
内蒙古一200斤男子欠5000万不还,被债主装进铁笼沉入80米水库,谁料,2年后才被捞出...

内蒙古一200斤男子欠5000万不还,被债主装进铁笼沉入80米水库,谁料,2年后才被捞出...

品读时刻
2026-02-11 17:18:30
美论坛:若中国禁止美国加入中国空间站,美国是否有权将其击落

美论坛:若中国禁止美国加入中国空间站,美国是否有权将其击落

花魄m
2026-02-15 02:35:51
裁决结果出炉,安世归荷兰掌控。中方寸步不退,对欧盟开始征税

裁决结果出炉,安世归荷兰掌控。中方寸步不退,对欧盟开始征税

章幃户外
2026-02-13 22:37:58
一夜爆火!八千元就能买匹迷你小马?马主:隐性开销大得吓死人……

一夜爆火!八千元就能买匹迷你小马?马主:隐性开销大得吓死人……

环球网资讯
2026-02-13 20:40:28
大衣哥女儿出嫁,男方婚礼全程没笑脸,朱雪梅两百斤身材太惹眼

大衣哥女儿出嫁,男方婚礼全程没笑脸,朱雪梅两百斤身材太惹眼

科学发掘
2026-02-15 04:28:13
大量西藏人近年涌入天津东丽区,专找路边30多岁的漂亮美女问路

大量西藏人近年涌入天津东丽区,专找路边30多岁的漂亮美女问路

奇葩游戏酱
2026-02-14 18:17:16
既然给脸不要脸,那就彻底撕破脸!王毅外长已经把话挑明了

既然给脸不要脸,那就彻底撕破脸!王毅外长已经把话挑明了

安安说
2026-02-01 14:01:51
长得漂亮却坏事做尽,3次入狱,被摘除4处器官的她,如今过得怎样

长得漂亮却坏事做尽,3次入狱,被摘除4处器官的她,如今过得怎样

凡知
2026-01-27 08:21:20
2026-02-15 06:32:49
flydean程序那些事
flydean程序那些事
最通俗的解读,最深刻的干货!
356文章数 438关注度
往期回顾 全部

科技要闻

字节跳动官宣豆包大模型今日进入2.0阶段

头条要闻

泽连斯基:冲突可以结束 但首先要结束得体面

头条要闻

泽连斯基:冲突可以结束 但首先要结束得体面

体育要闻

最戏剧性的花滑男单,冠军为什么是他?

娱乐要闻

春晚第五次联排路透 明星积极饭撒互动

财经要闻

谁在掌控你的胃?起底百亿"飘香剂"江湖

汽车要闻

星光730新春促销开启 80天销量破2.6万台

态度原创

亲子
房产
健康
数码
军事航空

亲子要闻

回到“儿童友好”的家乡,很适合遛娃

房产要闻

三亚新机场,又传出新消息!

转头就晕的耳石症,能开车上班吗?

数码要闻

键盘可当触控板,Unihertz Titan 2 Elite全键盘手机MWC 2026发布

军事要闻

钓鱼岛、黄岩岛、仁爱礁已充满中国年味

无障碍浏览 进入关怀版