netty系列之:轻轻松松搭个支持中文的服务器
简介
之前讲了那么多关于netty的文章,都是讲netty的底层原理和实现,各位小伙伴一定都在想了,看了这么多篇文章,netty到底能干啥呢?今天让我们来使用netty简简单单搭一个支持中文的服务器,展示一下netty的威力。
netty的HTTP支持
今天我们搭的服务器是支持HTTP1.1的服务器。在netty中搭建服务器就像是拼房子,找到合适的工具就可以事半功倍。那么要搭建HTTP的房子,netty提供了什么样的工具呢?
在讲解netty对HTTP的支持之前,我们先看一下HTTP的版本发展情况。
HTTP的全称是Hypertext Transfer Protocol,是在1989年World Wide Web发展起来之后出现的标准协议,用来在WWW上传输数据。HTTP/1.1是1997年在原始的HTTP协议基础上进行的补充和优化。
到了2015年,为了适应快速发送的web应用和现代浏览器的需求,发展出了新的HTTP/2协议,主要在手机浏览器、延时处理、图像处理和视频处理方面进行了优化。
基本上所有的现代浏览器都支持HTTP/2协议了,但是还有很多应用程序使用的是老的HTTP/1.1协议。netty为HTTP2和HTTP1提供了不同的支持包,对于HTTP1的支持包叫做netty-codec-http,对HTTP2支持的包叫做netty-codec-http2。
本文会讲解netty对HTTP1的支持,将会在后续的文章中继续HTTP2的介绍。
netty-codec-http提供了对HTTP的非常有用的一些封装。
首先是代表HTTP中传输对象的类HttpObject,这个类代表着传输中的所有对象。继承这个类的对象有两个非常重要的对象,分别是HttpMessage和HttpContent。
HttpMessage可能跟我想象的不太一样,它实际上只包含了两部分内容,分别是HttpVersion和HttpHeaders,但是并不包含任何内容。
public interface HttpMessage extends HttpObject {
HttpVersion protocolVersion();
HttpMessage setProtocolVersion(HttpVersion version);
HttpHeaders headers();
}
这里HttpVersion只支持HTTP/1.0和HTTP/1.1协议。而HttpHeaders就是对HTTP请求中头对象的封装。
HttpMessage的子类是HttpRequest和HttpResponse,所以这两个类本身是不带请求内容的。
而具体请求的内容是在HttpContent中,HttpContent继承自ByteBufHolder,表示它中间可以带有ByteBuf的内容信息。
而HttpContent真正的实现类就是DefaultFullHttpRequest和DefaultFullHttpResponse,这两个内包含了HTTP头和HTTP请求响应内容信息。
那么问题来了,为什么要把HTTP头和HTTP内容分开呢?
这就涉及到HTTP1.1中消息传输中的压缩机制了。为了提升传输的效率,一般来说在传输的的过程中都会对象消息进行压缩,但是对于HTTP1.1来说,头部的内容是没办法压缩的,只能压缩content部分,所以需要区别对待。
netty中使用HTTP的原理
我们知道netty底层是客户端和服务器端构建通道,通过通道来传输ByteBuf消息。那么netty是怎么支持HTTP请求呢?
当客户端向服务器端发送HTTP请求之后,服务器端需要把接收到的数据使用解码器解码成为可以被应用程序使用的各种HttpObject对象,从而能够在应用程序中对其解析。
netty提供了HttpResponseEncoder和HttpRequestDecoder类,来对HTTP的消息进行编码和解码。
如果不想分别使用两个类来进行编码和解码,netty还提供了HttpServerCodec类来进行编码和解码工作。
这个类包含了HttpRequestDecoder和HttpResponseEncoder两部分的工作,可以同时用来进行编码和解码。
100 (Continue) Status
在HTTP中有一个独特的功能叫做,100 (Continue) Status,就是说client在不确定server端是否会接收请求的时候,可以先发送一个请求头,并在这个头上加一个"100-continue"字段,但是暂时还不发送请求body。直到接收到服务器端的响应之后再发送请求body。
为了处理这种请求,netty提供了一个HttpServerExpectContinueHandler对象,用来处理100 Status的情况。
当然,如果你的客户端没有这种请求,那么可以直接使用HttpObjectAggregator来将HttpMessage和HttpContent和合并成为FullHttpRequest或者FullHttpResponse。
为netty搭建HTTP服务器
有了上面的工作,我们就可以使用netty搭建http服务器了。最关键的一点就是在HttpRequestServerInitializer添加对应的codec和自定义handler。
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpServerCodec());
p.addLast(new HttpServerExpectContinueHandler());
p.addLast(new HttpRequestServerHandler());
}
在自定义的handler中,我们需要实现一个功能,就是当收到客户端的请求时候,需要返回给客户端一段欢迎语。
首先将获得的HttpObject转换成为HttpRequest对象,然后根据请求对象构建一个DefaultFullHttpResponse对象,然后设置该response对象的头,最后将该对象写到channel中。
对应的关键代码如下:
private static final byte[] CONTENT = "欢迎来到www.flydean.com!".getBytes(StandardCharsets.UTF_8);
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
if (msg instanceof HttpRequest) {
HttpRequest req = (HttpRequest) msg;
boolean keepAlive = HttpUtil.isKeepAlive(req);
FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,
Unpooled.wrappedBuffer(CONTENT));
response.headers()
// .set(CONTENT_TYPE, TEXT_PLAIN)
.set(CONTENT_TYPE, "text/plain;charset=utf-8")
.setInt(CONTENT_LENGTH, response.content().readableBytes());
if (keepAlive) {
if (!req.protocolVersion().isKeepAliveDefault()) {
//设置header connection=keep-alive
response.headers().set(CONNECTION, KEEP_ALIVE);
}
} else {
// 如果keepAlive是false,则设置header connection=close
response.headers().set(CONNECTION, CLOSE);
}
ChannelFuture f = ctx.write(response);
if (!keepAlive) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
}
上面的关键代码中CONTENT包含了中文字符串,我们使用getBytes将其转换成了UTF-8编码的byte数组。那么如果要想客户端能够正确识别UTF-8编码,需要在response的header中设置内容类型文件为:"text/plain;charset=utf-8"。
最后,使用下面的代码启动server:
// server配置
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.option(ChannelOption.SO_BACKLOG, 1024);
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new HttpRequestServerInitializer());
Channel ch = b.bind(PORT).sync().channel();
log.info("请打开你的浏览器,访问 http://127.0.0.1:8000/");
ch.closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
总结
现在,使用你的浏览器访问你搭建的服务器地址,你就可以得到"欢迎来到www.flydean.com!"。 到此一个简单的netty服务器就完成了。
本文的例子可以参考:learn-netty4
本文已收录于 http://www.flydean.com/19-netty-http-client-request/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
netty系列之:轻轻松松搭个支持中文的服务器的更多相关文章
- netty系列之:性能为王!创建多路复用http2服务器
目录 简介 多路复用的基础 多路复用在server端的使用 配置TLS处理器 配置clear text upgrade 总结 简介 在之前的文章中,我们提到了在netty的客户端通过使用Http2Fr ...
- Linux 服务器设置成支持中文
Linux 服务器设置成支持中文 由于服务器默认是不支持中文的.所以一般需要单独设置一下. 检查本机已有的语言包 locale -a 默认是没有中文的,所以会显示: C C.UTF-8 POSIX e ...
- 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?
[读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...
- Netty系列之源码解析(一)
本文首发于微信公众号[猿灯塔],转载引用请说明出处 接下来的时间灯塔君持续更新Netty系列一共九篇 当前:Netty 源码解析(一)开始 Netty 源码解析(二): Netty 的 Channel ...
- Azure SQL Database (22) Azure SQL Database支持中文值
<Windows Azure Platform 系列文章目录> 在笔者之前的文章里,已经介绍了如何使Azure SQL Database支持中文: SQL Azure(七) 在SQL Az ...
- Netty 系列目录
Netty 系列目录 二 Netty 源码分析(4.1.20) 1.1 Netty 源码(一)Netty 组件简介 2.1 Netty 源码(一)服务端启动 2.2 Netty 源码(二)客户端启动 ...
- Netty系列(四)TCP拆包和粘包
Netty系列(四)TCP拆包和粘包 一.拆包和粘包问题 (1) 一个小的Socket Buffer问题 在基于流的传输里比如 TCP/IP,接收到的数据会先被存储到一个 socket 接收缓冲里.不 ...
- Netty 系列(三)Netty 入门
Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ...
- 3. 彤哥说netty系列之Java BIO NIO AIO进化史
你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...
随机推荐
- 关于hive核心
一.DDL数据定义 1.创建数据库 1)创建一个数据库,数据库在 HDFS 上的默认存储路径是/user/hive/warehouse/*.db. hive (default)> create ...
- .net core番外第2篇:Autofac的3种依赖注入方式(构造函数注入、属性注入和方法注入),以及在过滤器里面实现依赖注入
本篇文章接前一篇,建议可以先看前篇文章,再看本文,会有更好的效果. 前一篇跳转链接:https://www.cnblogs.com/weskynet/p/15046999.html 正文: Autof ...
- P6106 [Ynoi2010] Self Adjusting Top Tree
P6106 [Ynoi2010] Self Adjusting Top Tree 题意 给出平面直角坐标系上若干不与坐标轴平行的处于第一象限的互不相交的线段,多次询问平面中一个第一象限的矩形与这些线段 ...
- Navicat Premium 12安装及破解
特别提醒,Navicat Premium 12安装包请用我给的链接下载,不然会无法破解 下载Navicat Premium 12地址:https://pan.baidu.com/s/1AQsryKpJ ...
- jvm源码解读--15 oop对象详解
(gdb) p obj $15 = (oopDesc *) 0xf3885d08 (gdb) p * obj $16 = { _mark = 0x70dea4e01, _metadata = { _k ...
- vue源码解析之observe
一. vue文档中有"由于 JavaScript 的限制,Vue 不能检测以下数组的变动",是否真是由于JavaScript的限制,还是出于其他原因考虑 当你利用索引直接设置一个数 ...
- 单片机学习(二)开发板LED灯的控制
目录 开发板上LED灯相关的电路图 点灯 LED闪烁 LED流水灯 其他效果 灯光二进制计数器 进阶版流水灯 开发板上LED灯相关的电路图 这是P2相关7个引脚的电路图,在默认情况下它是直接接着VCC ...
- 自学vue第二天,从入门到放弃(生命周期的理解)
生命周期的理解 beforeCreate 创建前 数据还没有监听,没有绑定到vue对象实例,同时也没有挂载对象 没有数据也没有方法 created 在创建后 数据和方法已经 data数据已经绑定好了 ...
- TCP 才不傻!
大家好,我是小林. 之前收到个读者的问题,对于 TCP 三次握手和四次挥手的一些疑问: 第一次握手,如果客户端发送的SYN一直都传不到被服务器,那么客户端是一直重发SYN到永久吗?客户端停止重发SYN ...
- “入职一年,那个被高薪挖来的Android开发被劝退了。”
其实,在很多小伙伴的想法中,是希望通过跳槽实现薪酬涨幅,可是跳槽不是冲动后决定,应该谨慎啊~ 01 我的学弟,最近向我吐槽,2020 年上半年入职一家公司,当时是高薪挖走的他,所谓钱到位,工作也是充满 ...