使用浏览器访问服务端,返回消息给前台展示,Netty做Http服务需要指定编码解码器
每个客户端对应一个Channel,其 PipeLine 和 Handler 都是独享的

服务端代码:

  1. package http;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.EventLoopGroup;
  5. import io.netty.channel.nio.NioEventLoopGroup;
  6. import io.netty.channel.socket.nio.NioServerSocketChannel;
  7. public class MyServer {
  8. public static void main(String[] args) throws Exception {
  9. /* 创建 BossGroup 和 WorkerGroup 线程组
  10. * BossGroup 只处理连接请求,真正的和客户端业务处理会交给 WorkGrouop 完成
  11. * bossGroup 和 workerGroup 含有的子线程的个数默认为 CPU最大线程数 X 2,可以手动指定
  12. */
  13. EventLoopGroup bossGroup = new NioEventLoopGroup(1); //创建子线程的个数为1的 bossGroup
  14. EventLoopGroup workerGroup = new NioEventLoopGroup(8); //创建子线程的个数为8的 workerGroup
  15. try{
  16. ServerBootstrap serverBootstrap = new ServerBootstrap();
  17. serverBootstrap.group(bossGroup,workerGroup)
  18. .channel(NioServerSocketChannel.class)
  19. .childHandler(new ServerInitializer()); //一个客户端对应一个PipeLine和一个Handler,PipeLine和Handler是独享的
  20. ChannelFuture sync = serverBootstrap.bind(6668).sync();
  21. sync.channel().closeFuture().sync(); //对关闭通道进行监听(当有关闭通道的消息时才进行监听)
  22. } finally {
  23. bossGroup.shutdownGracefully(); //关闭资源
  24. workerGroup.shutdownGracefully(); //关闭
  25. }
  26. }
  27. }

自定义管道Handler:

  1. package http;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.Unpooled;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.channel.SimpleChannelInboundHandler;
  6. import io.netty.handler.codec.http.*;
  7. import io.netty.util.CharsetUtil;
  8. import java.net.URI;
  9. /*
  10. * 1. SimpleChannelInboundHandler 是 ChannelInboundHandlerAdapter 的子类
  11. * 2. 客户端和服务器端相互通讯的数据被封装为 HttpObject
  12. *
  13. * */
  14. public class MyHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {
  15. /* 读取客户端数据 */
  16. @Override
  17. protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
  18. //判断msg是不是HttpRequest请求
  19. if(msg instanceof HttpRequest){
  20. System.out.println("msg类型="+msg.getClass());
  21. System.out.println("客户端地址:"+ctx.channel().remoteAddress());
  22. System.out.println("当前的PipeLine HashCode是: "+ctx.pipeline().hashCode()+" ;当前的ServerHandler HashCode是: "+this.hashCode());
  23. //获取请求头,由于浏览器会请求网站ICO图标,服务端会将其当做请求,需要进行剔除
  24. HttpRequest httpRequest = (HttpRequest) msg;
  25. URI uri = new URI(httpRequest.uri());
  26. if("/favicon.ico".equals(uri.getPath())){
  27. System.out.println("浏览器请求了网站ICO,不响应");
  28. return;
  29. }
  30. //回复信息给浏览器(需要Http协议)
  31. ByteBuf content = Unpooled.copiedBuffer("hello,我是服务器", CharsetUtil.UTF_8);
  32. //创建Http响应并设置头
  33. DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
  34. response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
  35. response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
  36. //将构建好的response返回
  37. ctx.writeAndFlush(response);
  38. }
  39. }
  40. }

服务端初始化Handler:

  1. package http;
  2. import io.netty.channel.ChannelInitializer;
  3. import io.netty.channel.ChannelPipeline;
  4. import io.netty.channel.socket.SocketChannel;
  5. import io.netty.handler.codec.http.HttpServerCodec;
  6. /* 服务端初始化Handler */
  7. public class ServerInitializer extends ChannelInitializer<SocketChannel> {
  8. @Override
  9. protected void initChannel(SocketChannel ch) throws Exception {
  10. ChannelPipeline pipeline = ch.pipeline(); //得到管道
  11. pipeline.addLast("MyHttpServerCodec",new HttpServerCodec()); //向管道加入Netty提供的 HttpServerCodec(编码&解码器)
  12. pipeline.addLast("MyHttpServerHandler",new MyHttpServerHandler()); //添加一个自定义的Handler
  13. }
  14. }

测试:

使用两个浏览器访问,可以看到每个客户端其Channel对应的 PipeLine 和 Handler 都是自己独享的
image.png
多浏览器访问测试
image.png
浏览器默认会请求网站ICO图标,服务端屏蔽后不返回响应结果