使用浏览器访问服务端,返回消息给前台展示,Netty做Http服务需要指定编码解码器
每个客户端对应一个Channel,其 PipeLine 和 Handler 都是独享的
服务端代码:
package http;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class MyServer {
public static void main(String[] args) throws Exception {
/* 创建 BossGroup 和 WorkerGroup 线程组
* BossGroup 只处理连接请求,真正的和客户端业务处理会交给 WorkGrouop 完成
* bossGroup 和 workerGroup 含有的子线程的个数默认为 CPU最大线程数 X 2,可以手动指定
*/
EventLoopGroup bossGroup = new NioEventLoopGroup(1); //创建子线程的个数为1的 bossGroup
EventLoopGroup workerGroup = new NioEventLoopGroup(8); //创建子线程的个数为8的 workerGroup
try{
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerInitializer()); //一个客户端对应一个PipeLine和一个Handler,PipeLine和Handler是独享的
ChannelFuture sync = serverBootstrap.bind(6668).sync();
sync.channel().closeFuture().sync(); //对关闭通道进行监听(当有关闭通道的消息时才进行监听)
} finally {
bossGroup.shutdownGracefully(); //关闭资源
workerGroup.shutdownGracefully(); //关闭
}
}
}
自定义管道Handler:
package 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;
/*
* 1. SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter 的子类
* 2. 客户端和服务器端相互通讯的数据被封装为 HttpObject
*
* */
public class MyHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
/* 读取客户端数据 */
@Override
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());
System.out.println("当前的PipeLine HashCode是: "+ctx.pipeline().hashCode()+" ;当前的ServerHandler HashCode是: "+this.hashCode());
//获取请求头,由于浏览器会请求网站ICO图标,服务端会将其当做请求,需要进行剔除
HttpRequest httpRequest = (HttpRequest) msg;
URI uri = new URI(httpRequest.uri());
if("/favicon.ico".equals(uri.getPath())){
System.out.println("浏览器请求了网站ICO,不响应");
return;
}
//回复信息给浏览器(需要Http协议)
ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器", CharsetUtil.UTF_8);
//创建Http响应并设置头
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
//将构建好的response返回
ctx.writeAndFlush(response);
}
}
}
服务端初始化Handler:
package http;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
/* 服务端初始化Handler */
public class ServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); //得到管道
pipeline.addLast("MyHttpServerCodec",new HttpServerCodec()); //向管道加入Netty提供的 HttpServerCodec(编码&解码器)
pipeline.addLast("MyHttpServerHandler",new MyHttpServerHandler()); //添加一个自定义的Handler
}
}
测试:
使用两个浏览器访问,可以看到每个客户端其Channel对应的 PipeLine 和 Handler 都是自己独享的
多浏览器访问测试
浏览器默认会请求网站ICO图标,服务端屏蔽后不返回响应结果