李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
06.Netty组件Channel介绍
Leefs
2022-06-09 PM
1377℃
0条
[TOC] ### 一、概述 #### 1.1 概念 Channel是**客户端和服务端建立的一个连接通道**,是Netty抽象出来的网络I/O读写相关的接口。 客户端有一个Channel(SocketChannel),服务端也有一个Channel(NioSocketChannel),当客户端和服务端建立连接后,客户端的Channel会跟服务端的Channel进行联通。 ![06.Netty组件Channel介绍01.png](https://lilinchao.com/usr/uploads/2022/06/675483546.png) #### 1.2 Channel生命周期 **Channel的四个状态**: | 状态 | 描述 | | ------------------- | ------------------------------------------- | | channelUnregistered | Channel已经被创建,但还未注册到EventLoop | | channelRegistered | Channel已经被注册到EventLoop | | channelActive | Channel已经处理活动状态并可以接收与发送数据 | | channelInactive | Channel没有连接到远程节点 | Channel 的正常的生命周期如下图,当状态出现变化,就会触发对应的事件,这样就能与 ChannelPipeline 中的 ChannelHandler进行及时的交互。 ![06.Netty组件Channel介绍02.jpg](https://lilinchao.com/usr/uploads/2022/06/2280321468.jpg) #### 1.3 Channel常用方法 - close() 可以用来关闭Channel - closeFuture() 用来处理 Channel 的关闭 - sync 方法作用是同步等待 Channel 关闭 - 而 addListener 方法是异步等待 Channel 关闭 - pipeline() 方法用于添加处理器 - write() 方法将数据写入 - 因为缓冲机制,数据被写入到 Channel 中以后,不会立即被发送 - **只有当缓冲满了或者调用了flush()方法后**,才会将数据通过 Channel 发送出去 - writeAndFlush() 方法将数据写入并**立即发送(刷出)** ### 二、ChannelFuture + **客户端代码** ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringEncoder; import java.io.IOException; import java.net.InetSocketAddress; /** * @author lilinchao * @date 2022/6/9 * @description 客户端 **/ public class ChannelClientDemo { public static void main(String[] args) throws IOException, InterruptedException { ChannelFuture channelFuture = new Bootstrap() .group(new NioEventLoopGroup()) .channel(NioSocketChannel.class) .handler(new ChannelInitializer
() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new StringEncoder()); } }) // 该方法为异步非阻塞方法,主线程调用后不会被阻塞,真正去执行连接操作的是NIO线程 // NIO线程:NioEventLoop 中的线程 .connect(new InetSocketAddress("localhost", 8080)); channelFuture.sync(); // 获取客户端-服务器之间的Channel对象 Channel channel = channelFuture.channel(); channel.writeAndFlush("hello world"); System.in.read(); } } ``` 如果我们去掉`channelFuture.sync()`方法,会服务器无法收到`hello world` 这是因为建立连接(connect)的过程是**异步非阻塞**的,若不通过`sync()`方法阻塞主线程,等待连接真正建立,这时通过 channelFuture.channel() **拿到的 Channel 对象,并不是真正与服务器建立好连接的 Channel**,也就没法将信息正确的传输给服务器端 **实验如下:** ```java ChannelFuture channelFuture = new Bootstrap() .group(new NioEventLoopGroup()) .channel(NioSocketChannel.class) .handler(new ChannelInitializer
() { @Override protected void initChannel(Channel ch) { ch.pipeline().addLast(new StringEncoder()); } }) .connect("127.0.0.1", 8080); System.out.println(channelFuture.channel()); // 1 channelFuture.sync(); // 2 System.out.println(channelFuture.channel()); // 3 ``` * 执行到 1 时,连接未建立,打印 `[id: 0x2e1884dd]` * 执行到 2 时,sync 方法是同步等待连接建立完成 * 执行到 3 时,连接肯定建立了,打印 `[id: 0x2e1884dd, L:/127.0.0.1:57191 - R:/127.0.0.1:8080]` 所以需要通过`channelFuture.sync()`方法,阻塞主线程,**同步处理结果**,等待连接真正建立好以后,再去获得 Channel 传递数据。使用该方法,获取 Channel 和发送数据的线程**都是主线程**。 下面还有一种方法,用于**异步**获取建立连接后的 Channel 和发送数据,使得执行这些操作的线程是 NIO 线程(去执行connect操作的线程) **addListener方法** 通过这种方法可以**在NIO线程中获取 Channel 并发送数据**,而不是在主线程中执行这些操作 ```java // 当connect方法执行完毕后,也就是连接真正建立后 // 会在NIO线程中调用operationComplete方法 channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { Channel channel = channelFuture.channel(); channel.writeAndFlush("hello world"); } }); ``` ### 三、CloseFuture > 需求:当在控制台中输入'q'字符时,客户端关闭连接,当输入其他内容时正常打印 + **客户端代码** ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringEncoder; import java.net.InetSocketAddress; import java.util.Scanner; /** * @author lilinchao * @date 2022/6/9 * @description 处理关闭 -- 客户端 **/ public class CloseFutureClient { public static void main(String[] args) throws InterruptedException { //创建EventLoopGroup,使用完毕后关闭 NioEventLoopGroup group = new NioEventLoopGroup(); ChannelFuture channelFuture = new Bootstrap() .group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer
() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new StringEncoder()); } }) .connect(new InetSocketAddress("localhost", 8080)); channelFuture.sync(); Channel channel = channelFuture.channel(); Scanner scanner = new Scanner(System.in); //创建一个线程用于输入并向服务器发送 new Thread(() -> { while (true){ String msg = scanner.next(); if("q".equals(msg)){ // 关闭操作是异步的,在NIO线程中执行 channel.close(); break; } channel.writeAndFlush(msg); } },"inputThread").start(); //获得closeFuture对象 ChannelFuture closeFuture = channel.closeFuture(); System.out.println("waiting close..."); //同步等待NIO线程执行完毕close操作 closeFuture.sync(); // 关闭之后执行一些操作,可以保证执行的操作一定是在channel关闭以后执行的 System.out.println("关闭之后执行一些额外操作..."); // 关闭EventLoopGroup group.shutdownGracefully(); } } ``` **说明** 当我们要关闭channel时,可以调用channel.close()方法进行关闭。但是该方法也是一个**异步方法**。真正的关闭操作并不是在调用该方法的线程中执行的,而是**在NIO线程中执行真正的关闭操作** 如果我们想在channel**真正关闭以后**,执行一些额外的操作,可以选择以下两种方法来实现 - 通过channel.closeFuture()方法获得对应的ChannelFuture对象,然后调用**sync()方法**阻塞执行操作的线程,等待channel真正关闭后,再执行其他操作 ```java // 获得closeFuture对象 ChannelFuture closeFuture = channel.closeFuture(); // 同步等待NIO线程执行完close操作 closeFuture.sync(); ``` - 调用**closeFuture.addListener**方法,添加close的后续操作 ```java closeFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { // 等待channel关闭后才执行的操作 System.out.println("关闭之后执行一些额外操作..."); // 关闭EventLoopGroup group.shutdownGracefully(); } }); ``` *附参考文章链接* *https://nyimac.gitee.io/2021/04/25/Netty%E5%9F%BA%E7%A1%80/*
标签:
Netty
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/2148.html
上一篇
05.Netty组件EventLoop使用
下一篇
07.Netty之Future与Promise
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
二叉树
NIO
VUE
Eclipse
Spring
递归
SpringBoot
算法
机器学习
Scala
Elasticsearch
Golang
Jenkins
Stream流
JVM
高并发
Hive
随笔
Sentinel
排序
Shiro
工具
JavaWeb
数学
MyBatis
Golang基础
MyBatisX
JavaSE
SpringCloudAlibaba
nginx
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭