Netty:简易Http Webserver编写

Netty:简易Http Webserver编写

Netty 介绍

Netty 是一个基于NIO的客户、服务器端编程框架。使用Netty可以使我们快速简单的开发网络应用。

Netty有很多功能,我们可以用Netty作为RPC框架,实现远程过程的调用;也可以编写长连接的服务器,使用websocket等功能;我们还可以把Netty当做http服务器(类似Tomcat),对http请求进行接收和响应。用Netty编写服务器时,采取的编程模型和获取请求模型不遵循serlvet规范,Netty有自己一套处理方式。

下面我们进行一段简易HttpServer的编写(作者使用Intellij IDEA进行Netty项目的开发)。

创建Netty工程

打开IDEA,创建一个gradle项目。

netty工程创建

点击下一步,输入GroupID(公司域名反转)和ArtifactID(模块名)

groupid

然后一直点下一步直到FInished。

至此我们便创建了一个工程,工程目录如下图所示:

工程目录
我们在src/main/java文件夹中编写应用代码,src/test中是我们编写测试文件用的,本文忽略测试文件。

导入netty jar包

导入netty jar包的过程很简单,我们可以在build.gradle文件的dependencies下加入下面一行代码:

1
compile group: 'io.netty', name: 'netty-all', version: '4.1.36.Final'

如下图所示
dependencies
最新引入netty的代码可以在maven repository中通过搜索netty all关键词找到。

然后我们就可以进行HttpServer的编写了。

Server编写

右击src/main/java文件夹,new一个新的package,如下图所示。

newpackage
然后在这个package中创建一个class名为TestServer
TestServer

在TestServer class中创建main函数,在main函数里面我们创建两个NioEventLoopGroup,分别为bossGroup,workerGroup。

1
2
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

bossGroup接受连接请求并把请求交给workerGroup处理;workerGroup获取请求参数,进行业务处理,然后把结果返回客户端。

然后创建ServerBootstrap 类的实例,ServerBootstrap 是简化服务端启动的类。该类通过反射的形式创建NioServerSocketChannel。

channel相当于链接,同用户端进行网络连接、关闭和读写。

最后调用ChildHandler方法,里面传入一个对请求进行处理的类,在本例代码中我们传入我们编写的TestServerInitializer实例(请看下一节)。

1
2
3
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new TestServerInitializer());

然后我们把这个服务器绑定在8888端口上,就可以接受用户端的请求了。

1
2
ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();
channelFuture.channel().closeFuture().sync();

完整代码如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {

ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new TestServerInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(8888).sync();
channelFuture.channel().closeFuture().sync();

} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();

}

}

ServerInitializer编写

TestServerINitializer
在同一个package中创建TestServerInitializer类,该类需要继承ChannelInitializer类,同时需要实现initChannel方法。

在连接被注册的时候,即ChannelInitializer被创建时会立即调用initChannel方法。

我们在该方法中创建一个pipeline。ChannelHandler 类似过滤器;ChannelPipeline 是管道,由多个channel handle共同构成。ChannelHandlerChannelPipeline 用于channel事件的拦截和处理。

在这个管道中我们先加入HttpServerCodecHttpServerCodec 是一个用于对web请求进行编解码操作的Channelhandler。然后我们再加入TestHttpServerHandler(请看下一节),这个handler是一个我们自己编写用于对请求进行操作的类。

具体代码如下图所示。

1
2
3
4
5
6
7
8
public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("httpServerCodec", new HttpServerCodec());
pipeline.addLast("testHttpServerHandler", new TestHttpServerHandler());
}
}

TestHttpServerHandler编写

在同一个package中创建TestHttpServerHandler类,在该类中我们创建“Hello,World”字符串,然后当用户对服务器进行请求时,将Hello,World返回给用户。

TestHttpServerHandler需要继承SimpleChannelInboundHandler 类,并实现channelRead0方法。channelread0 读取客户端发送的请求,并向客户端返回响应的代码的函数。

我们用ByteBuf构建Hello,World字符串,ByteBuf 是Netty实现的最基本的数据缓冲。

1
ByteBuf content = Unpooled.copiedBuffer("Hello, World", CharsetUtil.UTF_8);

然后用DefaultFullHttpResponse构建响应,该类接受的参数有:协议类型,返回状态,返回内容(即helloworld)。

然后我们对响应头进行设置,设置内容类型为text/plain,以及设置内容长度为Hello,World的长度。

最后用ctx.writeAndFlush方法把内容返回给客户端。

具体代码如下所示。
TestHttpServerHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
if (msg instanceof HttpRequest) {
ByteBuf content = Unpooled.copiedBuffer("Hello, World", CharsetUtil.UTF_8);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
ctx.writeAndFlush(response);
}

}
}

运行Netty服务器

点击Run-> Run “TestServer”运行刚刚编写的程序。
runserver

打开浏览器,输入网址localhost:8888
可以看到服务器返回Hello,World

返回helloworld

至此,我们第一个netty服务器就编写成功了!