前言
这章我们使用Netty来写一个Http服务器类似于Tomcat ,当然Netty和Tomcat是有很多的异同的,比如通信协议,Tomcat是一个基于Http协议的Web容器,而Netty能够通过codec自己来编码/解码字节流 ,因此Netty可以通过编程自定义各种协议,我们今天的目的还是对Netty练练手。
基于Netty的Http服务器
我这里要实现的案例就是客户端(浏览器或者Postmain)请求服务器,发送GET或者Post请求,服务器拿到请求中的参数,然后返回一个消息给客户端.
对于Netty服务端和之前的入门程序差不多,大致流程都是一样的,只不过这次需要给 SocketChannel中的pipeline添加编码和解码器。
- HttpRequestDecoder 请求解码器
- HttpResponseEncoder 响应转码器
对于Handler而言,我们需要继承 SimpleChannelInboundHandler<FullHttpRequest>
, 泛型FullHttpRequest
就是对请求对象的封装,通过它我们可以获取到请求相关的内容。
服务器代码
publicclassNettyHttpServer { publicstaticvoidmain(String[] args) throwsInterruptedException { //事件循环组NioEventLoopGroupbossGroup=newNioEventLoopGroup(); NioEventLoopGroupworkGroup=newNioEventLoopGroup(); //启动对象ServerBootstrapbootstrap=newServerBootstrap() ; try { //配置nettybootstrap.group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .childHandler(newChannelInitializer<SocketChannel>(){ protectedvoidinitChannel(SocketChannelch) throwsException { // 请求解码器ch.pipeline().addLast("http-decoder", newHttpRequestDecoder()); // 将HTTP消息的多个部分合成一条完整的HTTP消息ch.pipeline().addLast("http-aggregator", newHttpObjectAggregator(65535)); // 响应转码器ch.pipeline().addLast("http-encoder", newHttpResponseEncoder()); // 解决大码流的问题,ChunkedWriteHandler:向客户端发送HTML5文件ch.pipeline().addLast("http-chunked", newChunkedWriteHandler()); //添加处理器到pipelinech.pipeline().addLast("http-server",newHttpServerHandler()); } }); //启动服务,监听端口,同步返回ChannelFuturechannelFuture=bootstrap.bind(newInetSocketAddress("127.0.0.1", 6666)).sync(); // 当通道关闭时继续向后执行,这是一个阻塞方法channelFuture.channel().closeFuture().sync(); } catch (InterruptedExceptione) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
相比之前的入门案例来说,就是给 pipeline 多添加了一堆编码器,目的就是对Http请求或响应进行编码。
处理器代码
handler中需要根据请求Method判断是GET或者POST,对于POST还要判断是普通表单提交还是JSON提交。然后根据不同的请求方式取到请求参数。最后把“Hello Netty”返回给客户端。
packagecn.itsource.nio.httpserver; importcom.alibaba.fastjson.JSON; importcom.alibaba.fastjson.JSONObject; importcom.sun.deploy.util.StringUtils; importio.netty.buffer.ByteBuf; importio.netty.buffer.Unpooled; importio.netty.channel.ChannelHandlerContext; importio.netty.channel.SimpleChannelInboundHandler; importio.netty.handler.codec.http.*; importio.netty.handler.codec.http.multipart.DefaultHttpDataFactory; importio.netty.handler.codec.http.multipart.HttpPostRequestDecoder; importio.netty.handler.codec.http.multipart.InterfaceHttpData; importio.netty.handler.codec.http.multipart.MemoryAttribute; importio.netty.util.CharsetUtil; importjava.io.UnsupportedEncodingException; importjava.util.HashMap; importjava.util.List; importjava.util.Map; publicclassHttpServerHandlerextendsSimpleChannelInboundHandler<FullHttpRequest> { //该方法的作用是用来读取客户端的数据, FullHttpRequest 请求对象protectedvoidchannelRead0(ChannelHandlerContextctx, FullHttpRequestrequest) throwsException { System.out.println("客户端:"+ctx.channel().remoteAddress()+" 发来请求"); //读取数据Map<String, Object>data=readData(request); System.out.println("data:"+data); //根据请求参数,处理不同的业务,做出不同的响应//就返回一个Hello好了Stringcontent="Hello Netty"; //响应数据writeDate(ctx,request,content); } //写数据给客户端privatevoidwriteDate(ChannelHandlerContextctx,FullHttpRequestrequest,Stringcontent) { //把数据拷贝到bufferByteBufbuffer=Unpooled.copiedBuffer(content, CharsetUtil.UTF_8); //创建Http响应对象,设置http版本和状态吗,以及数据DefaultFullHttpResponsehttpResponse=newDefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,buffer); //响应头信息httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plan"); httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH, buffer.readableBytes()); //响应结果ctx.writeAndFlush(httpResponse); } //读取数据,get,或者postpublicMap<String,Object>readData(FullHttpRequestrequest){ Map<String, Object>param=null; if(request.method() ==HttpMethod.GET){ param=getGetParams(request); }elseif(request.method() ==HttpMethod.POST){ param=getPostParams(request); } System.out.println(param); returnparam; } //获取GET方式传递的参数privateMap<String, Object>getGetParams(FullHttpRequestfullHttpRequest) { Map<String, Object>params=newHashMap<String, Object>(); if (fullHttpRequest.method() ==HttpMethod.GET) { // 处理get请求QueryStringDecoderdecoder=newQueryStringDecoder(fullHttpRequest.uri()); Map<String, List<String>>paramList=decoder.parameters(); for (Map.Entry<String, List<String>>entry : paramList.entrySet()) { params.put(entry.getKey(), entry.getValue().get(0)); } returnparams; } returnnull; } //获取POST方式传递的参数privateMap<String, Object>getPostParams(FullHttpRequestfullHttpRequest) { if (fullHttpRequest.method() ==HttpMethod.POST) { // 处理POST请求StringstrContentType=fullHttpRequest.headers().get("Content-Type").trim(); if (strContentType.contains("x-www-form-urlencoded")) { returngetFormParams(fullHttpRequest); } elseif (strContentType.contains("application/json")) { returngetJSONParams(fullHttpRequest); } } returnnull; } //解析JSON数据privateMap<String, Object>getJSONParams(FullHttpRequestfullHttpRequest) { ByteBufcontent=fullHttpRequest.content(); byte[] reqContent=newbyte[content.readableBytes()]; content.readBytes(reqContent); try { returnJSON.parseObject(newString(reqContent, "UTF-8"),Map.class); } catch (UnsupportedEncodingExceptione) { e.printStackTrace(); } returnnull; } //解析from表单数据(Content-Type = x-www-form-urlencoded)privateMap<String, Object>getFormParams(FullHttpRequestfullHttpRequest) { Map<String, Object>params=newHashMap<String, Object>(); HttpPostRequestDecoderdecoder=newHttpPostRequestDecoder(newDefaultHttpDataFactory(false), fullHttpRequest); List<InterfaceHttpData>postData=decoder.getBodyHttpDatas(); for (InterfaceHttpDatadata : postData) { if (data.getHttpDataType() ==InterfaceHttpData.HttpDataType.Attribute) { MemoryAttributeattribute= (MemoryAttribute) data; params.put(attribute.getName(), attribute.getValue()); } } returnparams; } }