Netty 核心部件:Transport 传输功能
JDK 中对于 NIO(java.nio)、OIO(java.net) 的网络编程 API 的差异很大,在进行程序移植时的难度比较大,而 Netty 对于 NIO、OIO 等提供了统一的 API 接口;
如对于 01. Netty 主要部件介绍 & Hello World 实例 中的 Hello World 实例,使用 NIO Channel 作为实现,假如要更换为 OIO Channel 实现,只需要做出很少量的该改动,如下:
EchoServer
......ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(workerGroup) .channel(OioServerSocketChannel.class) //使用 OIO ServerSocket 传输通道 .localAddress(new InetSocketAddress((port))) ......EchoClient
.....Bootstrap bootstrap = new Bootstrap();bootstrap.group(workerGroup) .channel(OioSocketChannel.class) //使用 OIO Socket 传输通道 .remoteAddress(new InetSocketAddress(host,port)).....Netty 支持的传输方式
NIO
由 io.netty.channel.socket.nio 包提供支持,基于 java.nio.channels 包,使用选择器作为基础实现,默认实现有如下:
- NioServerSocketChannel:用于 TCP 协议的服务端;
- NioSocketChannel:用于 TCP 协议的客户端;
- NioDatagramChannel:用于 UDP 协议;
Non Blocking I/O 是最常用的方式,通过选择器提供了完全异步的方式操作所有的 I/O ;NIO 方式通常用在高连接数的场景下;
OIO
由 io.netty.channel.socket.oio 包提供支持,基于 java.net 包,使用阻塞流作为基础实现,默认的实现如下:
- OioServerSocketChannel:用于 TCP 协议的服务端;
- OioSocketChannel:用于 TCP 协议的客户端;
- OioDatagramChannel:用于 UDP 协议;
Old Blocking I/O 即阻塞 IO 操作,为面向流的 IO 操作,基于阻塞流为基础实现的同步流操作方式,虽然是旧式的 IO 方案,但是还是有一定的使用场景;OIO 方式通常使用在需要阻塞 IO 、需要低延迟的使用场景,同时该场景为低连接数的;
Local
由 io.netty.channel.local 包提供支持,默认实现为 LocalChannel ;Local 本地传输方式用于在 JVM 虚拟机之间进行本地通信;
Embedded
由 io.netty.channel.embedded 包提供支持,默认实现为 EmbeddedChannel;Embedded 嵌入传输允许嵌入到一个 ChannelHandler 到另一个 ChannelHandler 的传输;Embedded 方式通常用于测试 ChannelHandler 的实现;
Netty 用于传输的 API
Netty Transport API 的核心为 Channel 接口,由于进行所有进出站操作,如下:

每一个 Channel 都会分配一个 ChannelPipeline 和 ChannelConfig,前者为容纳 ChannelHandler 链的容器,后者负责设置为储存 Channel 的配置,并允许在运行期更改这些配置;
ChannelHandler 负责进行出站、入站的具体逻辑处理操作,可以在运行期根据需要添加 ChannelHandler 实例到 ChannelPipeline,或者从 ChannelPipeline 删除相应的 ChannelHandler 实现高度灵活的 Netty 程序;
Channel 本身提供了很多方法,主要如下:
| eventLoop() | 返回分配给 Channel 的 EventLoop |
| pipeline() | 返回分配给 Channel 的 ChannelPipeline |
| isActive() | 返回 Channel 是否**,已**说明与远程连接对等 |
| localAddress() | 返回已绑定的本地 SocketAddress |
| remoteAddress() | 返回已绑定的远程 SocketAddress |
| write() | 写数据到远程客户端,数据通过 ChannelPipeline 传输过去 |
| flush() | 刷新先前的数据 |
| writeAndFlush() | 一个方便的方法用户调用 write()而后调用 flush() |
如以下实例,演示写数据到远程客户端:
Channel channel = ...; // 获取channel的引用ByteBuf buf = Unpooled.copiedBuffer("Hello Wrold!", CharsetUtil.UTF_8); ChannelFuture cf = channel.writeAndFlush(buf); //写入缓冲区数据到 Channel,并刷新缓冲区cf.addListener(new ChannelFutureListener() { //添加 ChannelFuture 监听器,获取在IO操作完成后的通知 public void operationComplete(ChannelFuture future) { if (future.isSuccess()) { System.out.println("Write successful"); } else { System.err.println("Write error"); future.cause().printStackTrace(); } }});Netty 的 Channel 本身是线程安全区的,可以被多个不同的线程同时操作,以下是一个实例:
final Channel channel = ...; // 获取channel的引用final ByteBuf buf = Unpooled.copiedBuffer("Hello World!",CharsetUtil.UTF_8).retain(); Runnable writer = new Runnable() { public void run() { channel.writeAndFlush(buf.duplicate()); }};Executor executor = Executors.newCachedThreadPool();executor.execute(writer); //将 Channel 写进一个线程executor.execute(writer); //将 Channel 写进另一个线程