这章我们使用Netty来写一个Http服务器类似于Tomcat ,当然Netty和Tomcat是有很多的异同的,比如通信协议,Tomcat是一个基于Http协议的Web容器,而Netty能够通过codec自己来编码/解码字节流 ,因此Netty可以通过编程自定义各种协议,我们今天的目的还是对Netty练练手。
我这里要实现的案例就是客户端(浏览器或者Postmain)请求服务器,发送GET或者Post请求,服务器拿到请求中的参数,然后返回一个消息给客户端.
对于Netty服务端和之前的入门程序差不多,大致流程都是一样的,只不过这次需要给 SocketChannel中的pipeline添加编码和解码器。
对于Handler而言,我们需要继承 SimpleChannelInboundHandler
, 泛型FullHttpRequest
就是对请求对象的封装,通过它我们可以获取到请求相关的内容。
public class NettyHttpServer {
public static void main(String[] args) throws InterruptedException {
//事件循环组
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workGroup = new NioEventLoopGroup();
//启动对象
ServerBootstrap bootstrap = new ServerBootstrap() ;
try {
//配置netty
bootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer< SocketChannel >(){
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 请求解码器
ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
// 将HTTP消息的多个部分合成一条完整的HTTP消息
ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65535));
// 响应转码器
ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
// 解决大码流的问题,ChunkedWriteHandler:向客户端发送HTML5文件
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
//添加处理器到pipeline
ch.pipeline().addLast("http-server",new HttpServerHandler());
}
});
//启动服务,监听端口,同步返回
ChannelFuture channelFuture = bootstrap.bind(new InetSocketAddress("127.0.0.1", 6666)).sync();
// 当通道关闭时继续向后执行,这是一个阻塞方法
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
}
相比之前的入门案例来说,就是给 pipeline 多添加了一堆编码器,目的就是对Http请求或响应进行编码。
handler中需要根据请求Method判断是GET或者POST,对于POST还要判断是普通表单提交还是JSON提交。然后根据不同的请求方式取到请求参数。最后把“Hello Netty”返回给客户端。
package cn.itsource.nio.httpserver;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.deploy.util.StringUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.MemoryAttribute;
import io.netty.util.CharsetUtil;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
//该方法的作用是用来读取客户端的数据, FullHttpRequest 请求对象
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
System.out.println("客户端:"+ctx.channel().remoteAddress()+" 发来请求");
//读取数据
Map<String, Object> data = readData(request);
System.out.println("data:"+data);
//根据请求参数,处理不同的业务,做出不同的响应
//就返回一个Hello好了
String content = "Hello Netty";
//响应数据
writeDate(ctx,request,content);
}
//写数据给客户端
private void writeDate(ChannelHandlerContext ctx,FullHttpRequest request,String content) {
//把数据拷贝到buffer
ByteBuf buffer = Unpooled.copiedBuffer(content, CharsetUtil.UTF_8);
//创建Http响应对象,设置http版本和状态吗,以及数据
DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(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,或者post
public Map<String,Object> readData(FullHttpRequest request){
Map<String, Object> param = null;
if(request.method() == HttpMethod.GET){
param = getGetParams(request);
}else if(request.method() == HttpMethod.POST){
param = getPostParams(request);
}
System.out.println(param);
return param;
}
//获取GET方式传递的参数
private Map<String, Object> getGetParams(FullHttpRequest fullHttpRequest) {
Map<String, Object> params = new HashMap<String, Object>();
if (fullHttpRequest.method() == HttpMethod.GET) {
// 处理get请求
QueryStringDecoder decoder = new QueryStringDecoder(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));
}
return params;
}
return null;
}
//获取POST方式传递的参数
private Map<String, Object> getPostParams(FullHttpRequest fullHttpRequest) {
if (fullHttpRequest.method() == HttpMethod.POST) {
// 处理POST请求
String strContentType = fullHttpRequest.headers().get("Content-Type").trim();
if (strContentType.contains("x-www-form-urlencoded")) {
return getFormParams(fullHttpRequest);
} else if (strContentType.contains("application/json")) {
return getJSONParams(fullHttpRequest);
}
}
return null;
}
//解析JSON数据
private Map<String, Object> getJSONParams(FullHttpRequest fullHttpRequest) {
ByteBuf content = fullHttpRequest.content();
byte[] reqContent = new byte[content.readableBytes()];
content.readBytes(reqContent);
try {
return JSON.parseObject(new String(reqContent, "UTF-8"),Map.class);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
//解析from表单数据(Content-Type = x-www-form-urlencoded)
private Map<String, Object> getFormParams(FullHttpRequest fullHttpRequest) {
Map<String, Object> params = new HashMap<String, Object>();
HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), fullHttpRequest);
List<InterfaceHttpData> postData = decoder.getBodyHttpDatas();
for (InterfaceHttpData data : postData) {
if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
MemoryAttribute attribute = (MemoryAttribute) data;
params.put(attribute.getName(), attribute.getValue());
}
}
return params;
}
}
文章到这就结束了,点赞还是要求一下的,万一屏幕面前的大帅哥,或者大漂亮一不小心就一键三连了啦,那我就是熬夜到头发掉光,也出下章