前言
前面写了一个TCP服务的案例
Netty也可以完成HTTP服务
目录
HTTP服务案例
要求:打开端口9999,服务器建立连接,发送hello
服务器
package Netty.Http;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class TestServer {
public static void main(String[] args) throws Exception{
//两个线程池
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try{
//ServerBootstrap
ServerBootstrap serverBootstrap = new ServerBootstrap();
//配置参数
serverBootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new TestServerInit());//将设置的类放入
ChannelFuture channelFuture = serverBootstrap.bind(9999);
channelFuture.channel().closeFuture().sync();
}finally {
//关闭线程池
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
其中与自定义了一个TestServerInit类,用于放置多个管道处理器
TestServerInit
package Netty.Http;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
public class TestServerInit extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel ch) throws Exception {
//向管道加入处理器
//得到管道
ChannelPipeline pipeline = ch.pipeline();
//加入netty提供的httpServerCodec codec=>[coder - decoder]
//HttpServerCodec是netty提供的编解码器
pipeline.addLast("MyTestHTTPCodec",new HttpServerCodec());
//增加一个自定义的handler
pipeline.addLast("MyTestHttpServerHandler",new TestHttpServerHandler());
}
}
自定义处理器:TestHttpServerHandler
package Netty.Http;
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.util.CharsetUtil;
import java.net.URI;
/*
自定义管道处理器的另一种写法:SimpleChannelInboundHandler
SimpleChannelInboundHandler是ChannelInboundHandlerAdapter的子类,方法更多
HttpObject 客户端和服务器通讯的数据封装成HttpObject
*/
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
//读取客户端数据
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
//判断msg 是不是 httpRequest请求
if (msg instanceof HttpRequest){
System.out.println("msg类型 = "+msg.getClass());
System.out.println("客户端地址 :"+ctx.channel().remoteAddress());
//回复信息给浏览器[http协议信息]
ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器", CharsetUtil.UTF_8);
//构造一http的响应,HttpResponse
//设置响应头信息:版本+状态码+内容
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
//长度
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
//将构建好的响应信息返回
ctx.writeAndFlush(response);
}
}
}
火狐浏览器打开:http://localhost:9999/
存在的问题
令人头大的中文乱码
响应的中文是正确的格式,在浏览器运行就出问题了,可以确定是浏览器显示的问题
火狐浏览器处理方法:
先在页面按Alt键:
查看 -》 文字编码 -》选择Unicode(默认是西文,编码格式不相同会乱码)
解决
但是肯定不能总是让客户端设置编码
我们可以在服务器端设置好客户端的编码方式,设置响应头HttpHeaderNames.CONTENT_TYPE
//设置响应头信息:返回类型CONTENT_TYPE
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/html; charset=UTF-8");
设置浏览器的编码格式
两个请求
这是网页图标请求,可以用过滤方法过滤资源
过滤资源
在TestHttpServerHandler类中,可以根据URI资源标识将该请求过滤
package Netty.Http;
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.util.CharsetUtil;
import java.net.URI;
/*
自定义管道的另一种写法:SimpleChannelInboundHandler
SimpleChannelInboundHandler是ChannelInboundHandlerAdapter的子类,方法更多
HttpObject 客户端和服务器通讯的数据封装成HttpObject
*/
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
//读取客户端数据
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
//判断msg 是不是 httpRequest请求
if (msg instanceof HttpRequest){
System.out.println("msg类型 = "+msg.getClass());
System.out.println("客户端地址 :"+ctx.channel().remoteAddress());
//过滤信息
HttpRequest httpRequest = (HttpRequest) msg;
//获取URI资源标识符,过滤指定值
URI uri = new URI(httpRequest.uri());
if ("/favicon.ico".equals(uri.getPath())){
System.out.println("请求了favicon.ico资源,不做响应");
return;
}
//回复信息给浏览器[http协议信息]
ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器", CharsetUtil.UTF_8);
//构造一http的响应,HttpResponse
//设置响应头信息:版本+状态码+内容
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
//设置响应头信息:返回类型CONTENT_TYPE
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain;CharsetUtil.UTF_8");
//长度
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
//将构建好的响应信息返回
ctx.writeAndFlush(response);
}
}
}
请求网址是http://localhost:9999/favicon.ico
用URI获得请求标识,根据网址过滤掉
特有的Pipeline和handler
前面学习Netty模型知道了pipeline中的handler是负责处理业务的对象
对于多个业务,会创建多个pipeline和handler
在TestHttpServerHandler类中的channelRead0方法加上:
System.out.println("PipeLine hashCode :"+ctx.channel().pipeline().hashCode());
System.out.println("handler hashCode :"+this.hashCode());
针对一个浏览器请求,会创建pipeline和handler处理业务
再来一个浏览器请求:
pipeline和handler是不同的
刷新网页,pipeline和handler也会重新创建
HTTP1.1以前是短连接
在HTTP1.1已经改成了默认长连接