上一篇用Netty实现了简单的服务器与客户端通过TCP连接进行请求和响应,这次使用Netty来实现一个简单的Http服务器;如果要实现一个完整的Http服务器那将是十分复杂的,所以我打算实现GET和POST请求的响应。

GET/POST请求,服务器打印参数
然后返回【啦啦啦+当前服务器时间戳】

使用

启动服务

Netty的api设计的高度抽象,代码非常的通用,对于不同的功能主要在handler上作区分。

上一节实现服务端与客户端的通信使用的是自定义的handler,直接将字节数组转为字符串即可;作为Http服务器,一个Http请求包含了请求头请求体等内容,不能简单的将字节数组转为字符串,Netty对于Http请求的解析作了封装提供了通用类。

以下代码中handler使用了3个:

  1. HttpServerCodec:http请求编码和解码;
  2. HttpObjectAggregator:HttpServerCodec不会解析请求体,HttpObjectAggregator解析请求体并将HttpServerCodec解析出的HttpRequest和请求体合并成FullHttpRequest对象;
  3. HttpServerHandler:自定义处理类,对前面解析的FullHttpRequest进行解析和响应。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class HttpServerService {

public static void main(String[] args) throws InterruptedException {
new HttpServerService().serverRun();
}

private void serverRun() throws InterruptedException {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();

try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new HttpServerCodec()); // http请求编码和解码
ch.pipeline().addLast(new HttpObjectAggregator(1024*1024)); // 请求聚合
ch.pipeline().addLast(new HttpServerHandler()); // 自定义的请求处理类
}
})
.option(ChannelOption.SO_BACKLOG, 128)
;
ChannelFuture future = serverBootstrap.bind(80).sync();
// 等待服务端关闭
future.channel().closeFuture().sync();
}finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}

自定义请求处理HttpServerHandler

Netty对于参数解析也提供了工具类

  • QueryStringDecoder:解析uri上的参数
  • HttpPostRequestDecoder解析post请求的参数(普通参数与文件不同),如果表单类型的比如json就需要自己从请求体中解析了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class HttpServerHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof HttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
System.out.println(request.uri());
System.out.println(request.method());
System.out.println(request.headers());
//byte[] content = request.content().array();
// 请求体
String str = request.content().toString(CharsetUtil.UTF_8);
// GET请求处理
if (request.method().equals(HttpMethod.GET)){
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.content().toString(CharsetUtil.UTF_8));
Map<String, List<String>> param = queryStringDecoder.parameters();
System.out.println("get: " + param);
}else if (request.method().equals(HttpMethod.POST)) { // post请求处理
final boolean[] isForm = {false};
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(request);
decoder.offer(request);
decoder.getBodyHttpDatas().stream().forEach(s -> {
if (!isForm[0]) {
isForm[0] = true;
}
if (s.getHttpDataType().equals(InterfaceHttpData.HttpDataType.Attribute)) {
Attribute data = (Attribute) s;
try {
System.out.println(s.getName() + " " + data.getValue());
} catch (IOException e) {
e.printStackTrace();
}
} else if (s.getHttpDataType().equals(InterfaceHttpData.HttpDataType.FileUpload)) {
FileUpload fileUpload = (FileUpload) s;
// 文件上传
System.out.println("文件: "+fileUpload.getName());
System.out.println("长度: "+fileUpload.length());
}
});
// 无法解析出参数
if (!isForm[0]){
System.out.println("body: "+str);
}
}
}
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer(("啦啦啦"+System.currentTimeMillis()).getBytes()));
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}