Netty(3):核心部件:Transport 传输
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 写进另一个线程