文章

1. 从一个简单的CS demo说起

1. 从一个简单的CS demo说起

从一个简单的CS demo说起

CS模式源码

接下来提供用于演示Netty的源码

  • 服务端
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
      public class NettyServer {
          public static void main(String[] args) {
              ServerBootstrap serverBootstrap = new ServerBootstrap();
              try (EventLoopGroup bossGroup = new NioEventLoopGroup(1);
                   EventLoopGroup workerGroup = new NioEventLoopGroup()) {
                  serverBootstrap
                          .group(bossGroup, workerGroup)
                          .channel(NioServerSocketChannel.class)
                          .option(ChannelOption.SO_BACKLOG, 1024)
                          .option(ChannelOption.SO_REUSEADDR, true)
                          .handler(new LoggingHandler(LogLevel.INFO))
                          .childOption(ChannelOption.TCP_NODELAY, true)
                          .childOption(ChannelOption.SO_RCVBUF, 64 * 1024)
                          .childOption(ChannelOption.SO_SNDBUF, 64 * 1024)
                          .childHandler(new ChannelInitializer() {
                              @Override
                              protected void initChannel(Channel channel) throws Exception {
                                  ChannelPipeline pipeline = channel.pipeline();
                                  pipeline.addLast(new NettyServerHandler());
                              }
                          });
                  ChannelFuture channelFuture = serverBootstrap.bind(10009).sync();
                  channelFuture.channel().closeFuture().sync();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      public class NettyServerHandler extends ChannelInboundHandlerAdapter {
        
          @Override
          public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
              ByteBuf byteBuf = (ByteBuf) msg;
              byte[] body = new byte[byteBuf.readableBytes()];
              byteBuf.readBytes(body);
              System.out.println(new String(body));
              byte[] responseBytes = "hi,客户端,我是服务端".getBytes();
              ctx.channel().writeAndFlush(Unpooled.wrappedBuffer(responseBytes));
          }
        
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
              cause.printStackTrace();
          }
      }
    

    服务端执行流程详解

    1. 初始化ServerBootstrap:

      ServerBootstrap 是 Netty 提供的一个辅助类,用于简化服务器端的启动过程。通过它可以设置服务器的各种参数和选项。

    2. 创建两个事件循环组(EventLoopGroup):

      • bossGroup:负责处理客户端的连接请求。通常只需要一个线程。
      • workerGroup:负责处理 I/O 操作,如读写数据。可以根据需要配置多个线程。
    3. 配置服务器选项:

      • SO_BACKLOG:指定了内核为此套接口排队的最大连接个数。
      • SO_REUSEADDR:允许重用本地地址和端口。
    4. 配置子通道选项:

      • TCP_NODELAY:禁用 Nagle 算法,减少延迟。
      • SO_RCVBUF 和 SO_SNDBUF:设置接收和发送缓冲区的大小。
    5. 添加管道处理器:

      通过 ChannelPipeline 添加自定义的 NettyServerHandler,用于处理业务逻辑。ChannelPipeline 是一个处理器链,所有的 I/O 事件都会在这个链上传递。

    6. 绑定端口并启动服务器:

      服务器绑定到端口 10009,开始监听客户端连接。bind 方法返回一个 ChannelFuture 对象,通过调用 sync 方法等待绑定操作完成。

    7. 关闭服务器: 主线程等待,直到服务器关闭。通过调用 closeFuture().sync() 方法,确保服务器在关闭前不会退出。

  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
      public class NettyClient {
          public static void main(String[] args) {
              Bootstrap bootstrap = new Bootstrap();
              try (EventLoopGroup bossGroup = new NioEventLoopGroup()) {
                  bootstrap
                          .group(bossGroup)
                          .channel(NioSocketChannel.class)
                          .option(ChannelOption.SO_RCVBUF, 64 * 1024)
                          .option(ChannelOption.SO_SNDBUF, 64 * 1024)
                          .option(ChannelOption.SO_REUSEADDR, true)
                          .option(ChannelOption.TCP_NODELAY, true)
                          .handler(new ChannelInitializer<Channel>() {
                              @Override
                              protected void initChannel(Channel channel) throws Exception {
                                  ChannelPipeline pipeline = channel.pipeline();
                                  pipeline.addLast(new NettyClientHandler());
                              }
                          });
                  ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 10009).sync();
                  channelFuture.channel().closeFuture().sync();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
        
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
      public class NettyClientHandler extends ChannelInboundHandlerAdapter {
            
          @Override
          public void channelActive(ChannelHandlerContext ctx) throws Exception {
              byte[] bytes = "hi,服务端,我是客户端!".getBytes();
              ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
              ctx.channel().writeAndFlush(byteBuf);
          }
            
          @Override
          public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
              ByteBuf responseByteBuf = (ByteBuf) msg;
              byte[] responseByte = new byte[responseByteBuf.readableBytes()];
              responseByteBuf.readBytes(responseByte);
              System.out.println(new String(responseByte));
          }
        
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
              cause.printStackTrace();
          }
      }
    

    客户端执行流程详解

    1. 初始化Bootstrap:
    2. 创建事件循环组(EventLoopGroup) EventLoopGroup 负责处理所有的 I/O 事件。客户端通常只需要一个事件循环组。
    3. 配置客户端选项:
    4. 添加管道处理器:
    5. 连接服务器:
    6. 关闭连接:
本文由作者按照 CC BY 4.0 进行授权