Netty引导流程解读

本文围绕Netty展开,介绍了Channel的生命周期状态,如未注册、已注册等;阐述了ChannelHandler的生命周期及子类。详细说明了ChannelInboundHandler和ChannelOutboundHandler接口的方法,还提及资源泄漏检测、ChannelPipeline接口、异常处理以及AbstractBootstrap的相关方法。

Channel的生命周期状态【状态转换将变为相应的事件,转发给ChannelPipeline中的ChannelHandler进行处理】
  ChannelUnregistered:Channel已经被创建,但还未注册到EventLoop
  ChannelRegistered:Channel已经被注册到了EventLoop
  ChannelActive:Channel处于活动状态(已经连接到它的远程节点)。它现在可以接收和发送数据了
  ChannelInactive:Channel没有连接到远程节点


ChannelHandler的生命周期【加入或者移除ChannelPipeline时调用这些方法,其有2个子类ChannelInboundHandler ChannelOutboundHandler】
  handlerAdded:当把ChannelHandler添加到ChannelPipeline中时被调用
  handlerRemoved:当从ChannelPipeline中移除ChannelHandler时被调用
  exceptionCaught:当处理过程中在ChannelPipeline中有错误产生时被调用


ChannelInboundHandler接口【入站】
  channelRegistered:当Channel已经注册到它的EventLoop并且能够处理I/O时被调用
  channelUnregistered:当Channel从它的EventLoop注销并且无法处理任何I/O时被调用
  channelActive:当Channel处于活动状态时被调用;Channel已经连接/绑定并且已经就绪
  channelInactive:当Channel离开活动状态并且不再连接它的远程节点时被调用
  channelReadComplete:当Channel上的一个读操作完成时被调用
  channelRead:当从Channel读取数据时被调用 【channelRead0是通过SimpleChannelInboundHandler的方法】
  ChannelWritability Changed:当Channel的可写状态发生改变时被调用。用户可以确保写操作不会完成得太快(以避免发生OutOfMemoryError)或者可以在Channel变为再次可写时恢复写入。可以通过调用Channel的isWritable()方法来检测Channel的可写性。与可写性相关的阈值可以通过Channel.config(). setWriteHighWaterMark()和    Channel.config().setWriteLowWater- Mark()方法来设置
  userEventTriggered:当ChannelnboundHandler.fireUserEventTriggered()方法被调用时被调用,因为一个POJO被传经了ChannelPipeline


ChannelOutboundHandler接口【出站 按需推迟操作或者事件(下载文件被暂停)】 channel->ChannelPipeline->ChannelOutboundHandler
  bind(ChannelHandlerContext,SocketAddress,ChannelPromise):当请求将Channel绑定到本地地址时被调用
  connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise):当请求将Channel连接到远程节点时被调用
  disconnect(ChannelHandlerContext,ChannelPromise):当请求将Channel从远程节点断开时被调用
  close(ChannelHandlerContext,ChannelPromise):当请求关闭Channel时被调用
  deregister(ChannelHandlerContext,ChannelPromise):当请求将Channel从它的EventLoop注销时被调用
  read(ChannelHandlerContext):当请求从Channel读取更多的数据时被调用
  flush(ChannelHandlerContext):当请求通过Channel将入队数据冲刷到远程节点时被调用
  write(ChannelHandlerContext,Object,ChannelPromise):当请求通过Channel将数据写到远程节点时被调用

资源泄漏:Netty提供了class ResourceLeakDetector 【java -Dio.netty.leakDetectionLevel=ADVANCED】

ChannelPipeline接口【每一个新创建的Channel都将会被分配一个新的ChannelPipeline】
ChannelPipeline保存了与Channel相关联的ChannelHandler;
ChannelPipeline可以根据需要,通过添加或者删除ChannelHandler来动态地修改;
ChannelPipeline有着丰富的API用以被调用,以响应入站和出站事件
ChannelHanlderContext【每加入一个Handler,就会绑定一个新建的ChannelHanlderContext】
可以通过调用ChannelHandlerContext上的pipeline()方法来获得被封闭的ChannelPipeline的引用。这使得运行时得以操作ChannelPipeline的ChannelHandler,我们可以利用这一点来实现一些复杂的设计。例如,你可以通过将ChannelHandler添加到ChannelPipeline中来实现动态的协议切换。
异常处理
ChannelHandler.exceptionCaught()的默认实现是简单地将当前异常转发给ChannelPipeline中的下一个ChannelHandler;
如果异常到达了ChannelPipeline的尾端,它将会被记录为未被处理;
要想定义自定义的处理逻辑,你需要重写exceptionCaught()方法。然后你需要决定是否需要将该异常传播出去。

每个EventLoop都由一个Thread支撑
EventLoopGroup bossGroup = new NioEventLoopGroup();//默认创建的Thread数值为Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

  

EventLoopGroup oiobossGroup = new OioEventLoopGroup();

  

AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel>
B extends AbstractBootstarp<B,C> 子类型是父类型的一个参数类型,可以在运行时返回实例的引用以支持链式方式的调用Bootstrap 与 ServerBootstrap方法

  Bootstrap group(EventLoopGroup):设置用于处理Channel所有事件的EventLoopGroup
  Bootstrap channel(Class<? extends C>)Bootstrap channelFactory(ChannelFactory<? extends C>):channel()方法指定了Channel的实现类。如果该实现类没提供默认的构造函数[7],可以通过调用channel- Factory()方法来指定一个工厂类,它将会被bind()方法调用
<T> Bootstrap option(ChannelOption<T> option,T value):设置ChannelOption,其将被应用到每个新创建的Channel的ChannelConfig。这些选项将会通过bind()或者connect()方法设置到Channel,不管哪个先被调用。这个方法在Channel已经被创建后再调用将不会有任何的效果。支持的ChannelOption取决于使用的Channel类型。
<T> Bootstrap attr(Attribute<T> key, T value):指定新创建的Channel的属性值。这些属性值是通过bind()或者connect()方法设置到Channel的,具体取决于谁最先被调用。这个方法在Channel被创建后将不会有任何的效果。
  Bootstrap handler(ChannelHandler):设置将被添加到ChannelPipeline以接收事件通知的ChannelHandler
  Bootstrap clone():创建一个当前Bootstrap的克隆,其具有和原始的Bootstrap相同的设置信息
  Bootstrap remoteAddress(SocketAddress):设置远程地址。或者,也可以通过connect()方法来指定它
  ChannelFuture connect():连接到远程节点并返回一个ChannelFuture,其将会在连接操作完成后接收到通知
  ChannelFuture bind():绑定Channel并返回一个ChannelFuture,其将会在绑定操作完成后接收到通知,在那之后必须调用Channel. connect()方法来建立连接

 

EventLoopGroup bossGroup = new NioEventLoopGroup();//处理channel所有事件的group
ServerBootstrap serverBootstrap = new ServerBootstrap();//创建和连接新的服务端channel
serverBootstrap.group(bossGroup, workerGroup)//提供处理新链接channel事件的事件组
      .channel(NioServerSocketChannel.class)//指定所用的channel
      .childHandler(new ChannelInitializer<SocketChannel>() {//设置处理channel事件和数据的handler
         @Override
         public void initChannel(SocketChannel ch) throws Exception {

  

ServerBootstrap bootstrap = new ServerBootstrap();  // ← --  创建ServerBootstrap 以创建ServerSocketChannel,并绑定它
bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())// ← --  设置EventLoopGroup,其将提供用以处理Channel 事件的EventLoop
        .channel(NioServerSocketChannel.class)// ← --  指定要使用的Channel 实现
        .childHandler(// ← --  设置用于处理已被接受的子Channel 的I/O 和数据的ChannelInboundHandler
                new SimpleChannelInboundHandler<ByteBuf>() {
                    ChannelFuture connectFuture;
                    @Override
                    public void channelActive(ChannelHandlerContext ctx) throws Exception {
                        Bootstrap bootstrap = new Bootstrap();// ← --  创建一个Bootstrap类的实例以连接到远程主机
                        bootstrap.channel(NioSocketChannel.class).handler(//  ← --  指定Channel的实现
                                new SimpleChannelInboundHandler<ByteBuf>() {  // ← -- 为入站I/O 设置ChannelInboundHandler
                                    @Override
                                    protected void channelRead0(
                                            ChannelHandlerContext ctx, ByteBuf in)
                                            throws Exception {
                                        System.out.println("Received data");
                                    }
                                });
                        bootstrap.group(ctx.channel().eventLoop());// ← --  使用与分配给已被接受的子Channel 相同的EventLoop
                        connectFuture = bootstrap.connect(
                                new InetSocketAddress("www.123.com", 80)); //  ← --  连接到远程节点
                    }

                    @Override
                    protected void channelRead0(
                            ChannelHandlerContext channelHandlerContext,
                            ByteBuf byteBuf) throws Exception {
                        if (connectFuture.isDone()) {//当连接完成时,执行一些数据操作(如代理)
                        }
                    }
                });
ChannelFuture future = bootstrap.bind(new InetSocketAddress(8080));// ← --  通过配置好的ServerBootstrap绑定该Server-SocketChannel
future.addListener(new ChannelFutureListener() {
    @Override
    public void operationComplete(ChannelFuture channelFuture)
            throws Exception {
        if (channelFuture.isSuccess()) {
            System.out.println("Server bound");
        } else {
            System.err.println("Bind attempt failed");
            channelFuture.cause().printStackTrace();
        }
    }
});

  

 

转载于:https://www.cnblogs.com/htkj/p/10932637.html

Netty 是一个高性能的网络通信框架,广泛用于构建网络服务器和客户端。握手流程通常指的是 TCP 连接建立过程中涉及的三次握手(Three-Way Handshake),以及在某些协议(如 WebSocket 或 SSL/TLS)中进一步的协议握手。以下是 Netty 中握手流程的详细说明: ### TCP 三次握手流程 Netty 基于 Java NIO 的 `ServerSocketChannel` 和 `SocketChannel` 实现 TCP 通信。在建立连接,TCP 协议本身负责三次握手,具体流程如下: 1. **客户端发送 SYN 报文**:客户端向服务器发送一个 SYN(同步)标志位为 1 的 TCP 报文段,表示请求建立连接。 2. **服务器响应 SYN-ACK 报文**:服务器收到 SYN 报文后,会回复一个 SYN 和 ACK(确认)标志位均为 1 的报文段,表示同意建立连接。 3. **客户端发送 ACK 报文**:客户端收到服务器的 SYN-ACK 报文后,再发送一个 ACK 标志位为 1 的报文段,完成连接建立。 Netty 在底层通过操作系统的 TCP/IP 协议栈完成这一过程,开发者通常不需要直接处理这一阶段。然而,Netty 提供了 `ChannelHandler` 接口,允许在连接建立后执行自定义逻辑,例如触发 `channelActive()` 方法[^1]。 ### WebSocket 握手流程 如果使用 WebSocket 协议,Netty 提供了专门的握手流程支持。WebSocket 握手是基于 HTTP 协议的,具体步骤如下: 1. **HTTP 升级请求**:客户端发送一个带有 `Upgrade: websocket` 头的 HTTP 请求,表示希望升级到 WebSocket 协议。 2. **服务器响应升级**:服务器收到请求后,验证 `Sec-WebSocket-Key` 等字段,并返回 `101 Switching Protocols` 状态码,表示协议切换成功。 3. **WebSocket 数据帧传输**:一旦握手完成,客户端和服务器之间就可以通过 WebSocket 数据帧进行双向通信。 Netty 提供了 `WebSocketServerHandshaker` 和 `WebSocketClientHandshaker` 类来简化这一过程。例如,服务器端可以通过以下方式处理握手请求: ```java public class WebSocketServerHandler extends SimpleChannelInboundHandler<HttpObject> { private WebSocketServerHandshaker handshaker; @Override protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { if (msg instanceof FullHttpRequest) { FullHttpRequest request = (FullHttpRequest) msg; // 处理 WebSocket 握手请求 WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( getWebSocketLocation(request), null, true, 5 * 1024 * 1024); handshaker = wsFactory.newHandshaker(request); if (handshaker == null) { WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { handshaker.handshake(ctx.channel(), request); } } } private String getWebSocketLocation(FullHttpRequest req) { String location = req.headers().get(HttpHeaderNames.HOST) + "/websocket"; return "ws://" + location; } } ``` 客户端则可以使用 `WebSocketClientHandshaker` 来发起握手请求,并等待握手完成事件: ```java public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> { private final WebSocketClientHandshaker handshaker; private ChannelPromise handshakeFuture; public WebSocketClientHandler(WebSocketClientHandshaker handshaker) { this.handshaker = handshaker; } public ChannelFuture handshakeFuture() { return handshakeFuture; } @Override public void handlerAdded(ChannelHandlerContext ctx) { handshakeFuture = ctx.newPromise(); } @Override public void channelActive(ChannelHandlerContext ctx) { handshaker.handshake(ctx.channel()); } @Override public void handleUpstream(ChannelHandlerContext ctx, Object evt) { if (evt == WebSocketClientHandshaker.HANDSHAKE_COMPLETE) { handshakeFuture.setSuccess(); } } @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) { // 处理 WebSocket 数据帧 } } ``` ### SSL/TLS 握手流程 如果使用 SSL/TLS 加密通信,Netty 也提供了对 SSL 握手的支持。SSL 握手过程包括: 1. **客户端发送 ClientHello**:客户端发送 `ClientHello` 消息,包含支持的加密套件、协议版本等信息。 2. **服务器响应 ServerHello**:服务器选择合适的加密套件,并发送 `ServerHello` 消息,同发送证书等信息。 3. **密钥交换与验证**:双方交换密钥材料,并验证彼此的身份(如证书验证)。 4. **完成握手**:双方发送 `Finished` 消息,表示握手完成,后续通信将使用协商的密钥进行加密。 Netty 通过 `SslHandler` 实现 SSL/TLS 握手。开发者只需在 `ChannelPipeline` 中添加 `SslHandler`,即可自动处理握手过程。例如: ```java public class SecureChatClientInitializer extends ChannelInitializer<SocketChannel> { private final SslContext sslContext; public SecureChatClientInitializer(SslContext sslContext) { this.sslContext = sslContext; } @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 添加 SslHandler 以启用 SSL/TLS 握手 SSLEngine engine = sslContext.newEngine(ch.alloc()); pipeline.addFirst("ssl", new SslHandler(engine)); // 添加其他处理器 pipeline.addLast("handler", new SecureChatClientHandler()); } } ``` ### 握手流程的事件监听 Netty 提供了丰富的事件监听机制,允许开发者在握手完成执行特定逻辑。例如,在 WebSocket 握手完成后,可以触发 `userEventTriggered()` 方法,以执行初始化操作: ```java @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { if (evt == WebSocketClientHandshaker.HANDSHAKE_COMPLETE) { System.out.println("WebSocket handshake completed"); // 执行握手完成后的操作 } } ``` ### 总结 Netty 中的握手流程涵盖了 TCP 三次握手、WebSocket 握手以及 SSL/TLS 握手等多个层次。开发者可以通过 `ChannelHandler` 和 `SslHandler` 等组件灵活地控制握手行为,并在握手完成后执行自定义逻辑。此外,Netty 提供了事件监听机制,方便开发者在握手完成进行相应的处理。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值