上一篇用Netty实现了简单的服务器与客户端通过TCP连接进行请求和响应,这次使用Netty来实现一个简单的Http服务器;如果要实现一个完整的Http服务器那将是十分复杂的,所以我打算实现GET和POST请求的响应。
GET/POST请求,服务器打印参数
然后返回【啦啦啦+当前服务器时间戳】
使用
启动服务
Netty的api设计的高度抽象,代码非常的通用,对于不同的功能主要在handler上作区分。
上一节实现服务端与客户端的通信使用的是自定义的handler,直接将字节数组转为字符串即可;作为Http服务器,一个Http请求包含了请求头请求体等内容,不能简单的将字节数组转为字符串,Netty对于Http请求的解析作了封装提供了通用类。
以下代码中handler使用了3个:
- HttpServerCodec:http请求编码和解码;
- HttpObjectAggregator:HttpServerCodec不会解析请求体,HttpObjectAggregator解析请求体并将HttpServerCodec解析出的HttpRequest和请求体合并成FullHttpRequest对象;
- 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()); 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()); String str = request.content().toString(CharsetUtil.UTF_8); 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)) { 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); } }
|