Netty 自带了一些传输协议的实现,虽然没有支持所有的传输协议,但是其自带的已足够我们来使用。Netty应用程序的传输协议依赖于底层协议,本节我们将学习Netty中的传输协议。
Netty中的传输方式有如下几种:
Table 4.1 Provided transports
| 方法名称 | 包 | 描述 |
| --- | --- | --- |
| NIO | io.netty.channel.socket.nio | 基于java.nio.channels的工具包,使用选择器作为基础的方法。 |
| OIO | io.netty.channel.socket.oio | 基于java.net的工具包,使用阻塞流。 |
| Local | io.netty.channel.local | 用来在虚拟机之间本地通信。 |
| Embedded | io.netty.channel.embedded | 嵌入传输,它允许在没有真正网络的运输中使用 ChannelHandler,可以非常有用的来测试ChannelHandler的实现。 |
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Included%20transports.md#nio-nonblocking-io)NIO-Nonblocking I/O
NIO传输是目前最常用的方式,它通过使用选择器提供了完全异步的方式操作所有的 I/O,NIO 从Java 1.4才被提供。
NIO 中,我们可以注册一个通道或获得某个通道的改变的状态,通道状态有下面几种改变:
* 一个新的 Channel 被接受并已准备好
* Channel 连接完成
* Channel 中有数据并已准备好读取
* Channel 发送数据出去
处理完改变的状态后需重新设置他们的状态,用一个线程来检查是否有已准备好的 Channel,如果有则执行相关事件。在这里可能只同时一个注册的事件而忽略其他的。选择器所支持的操作在 SelectionKey 中定义,具体如下:
Table 4.2 Selection operation bit-set
| 方法名称 | 描述 |
| --- | --- |
| OP_ACCEPT | 有新连接时得到通知 |
| OP_CONNECT | 连接完成后得到通知 |
| OP_REA | 准备好读取数据时得到通知 |
| OP_WRITE | 写入更多数据到通道时得到通知,大部分时间 |
这是可能的,但有时 socket 缓冲区完全填满了。这通常发生在你写数据的速度太快了超过了远程节点的处理能力。
Figure 4.2 Selecting and Processing State Changes
[![](https://box.kancloud.cn/2015-08-18_55d31927570fd.jpg)](https://github.com/waylau/essential-netty-in-action/blob/master/images/Figure%204.2%20Selecting%20and%20Processing%20State%20Changes.jpg)
1.新信道注册 WITH 选择器
2.选择处理的状态变化的通知
3.以前注册的通道
4.Selector.select()方法阻塞,直到新的状态变化接收或配置的超时 已过
5.检查是否有状态变化
6.处理所有的状态变化
7.在选择器操作的同一个线程执行其他任务
有一种功能,目前仅适用于 NIO 传输叫什么 “zero-file-copy (零文件拷贝)”,这使您能够快速,高效地通过移动数据到从文件系统传输内容 网络协议栈而无需复制从内核空间到用户空间。这可以使 FT P或 HTTP 协议有很大的不同。
然而,并非所有的操作系统都支持此功能。此外,你不能用它实现数据加密或压缩文件系统 - 仅支持文件的原生内容。另一方面,传送的文件原本已经加密的是完全有效的。
接下来,我们将讨论的是 OIO ,它提供了一个阻塞传输。
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Included%20transports.md#oio-old-blocking-io)OIO-Old blocking I/O
Netty 中,该 OIO 传输代表了一种妥协。它通过了 Netty 的通用 API 访问但不是异步,而是构建在 java.net 的阻塞实现上。任何人下面讨论这一点可能会认为,这个协议并没有很大优势。但它确实有它有效的用途。
假设你需要的端口使用该做阻塞调用库(例如 [JDBC](http://www.oracle.com/technetwork/java/javase/jdbc/index.html))。它可能不适合非阻塞。相反,你可以在短期内使用 OIO 传输,后来移植到纯异步的传输上。让我们看看它是如何工作的。
在 java.net API,你通常有一个线程接受新的连接到达监听在ServerSocket,并创建一个新的线程来处理新的 Socket 。这是必需的,因为在一个特定的 socket的每个 I/O 操作可能会阻塞在任何时间。在一个线程处理多个 socket 易造成阻塞操作,一个 socket 占用了所有的其他人。
鉴于此,你可能想知道 Netty 是如何用相同的 API 来支持 NIO 的异步传输。这里的 Netty 利用了 SO_TIMEOUT 标志,可以设置在一个 Socket。这 timeout 指定最大 毫秒 数量 用于等待 I/O 的操作完成。如果操作在指定的时间内失败,SocketTimeoutException 会被抛出。 Netty中捕获该异常并继续处理循环。在接下来的事件循环运行,它再次尝试。像 Netty 的异步架构来支持 OIO 的话,这其实是唯一的办法。当SocketTimeoutException 抛出时,执行 stack trace。
Figure 4.3 OIO-Processing logic
[![](https://box.kancloud.cn/2015-08-18_55d319299aac1.jpg)](https://github.com/waylau/essential-netty-in-action/blob/master/images/Figure%204.3%20OIO-Processing%20logic.jpg)
1.线程分配给 Socket
2.Socket 连接到远程
3.读操作(可能会阻塞)
4.读完成
5.处理可读的字节
6.执行提交到 socket 的其他任务
7.再次尝试读
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Included%20transports.md#本地-transport-与-jvm-交互)本地 Transport 与 JVM 交互
Netty 提供了“本地”运输,为运行在同一个 Java 虚拟机上的服务器和客户之间提供异步通信。此传输支持所有的 Netty 常见的传输实现的 API。
在此传输中,与服务器 Channel 关联的 SocketAddress 不是“绑定”到一个物理网络地址中,而它被存储在注册表中,只要服务器是运行的。当 Channel 关闭时它会注销。由于传输不接受“真正的”网络通信,它不能与其他传输实现互操作。因此,客户端是希望连接到使用当地的交通必须使用它,以及一个服务器。除此限制之外,它的使用是与其他的传输是相同的。
### [](https://github.com/waylau/essential-netty-in-action/blob/master/CORE%20FUNCTIONS/Included%20transports.md#内嵌-transport)内嵌 Transport
Netty中 还提供了可以嵌入 ChannelHandler 实例到其他的 ChannelHandler 的传输,使用它们就像辅助类,增加了灵活性的方法,使您可以与你的 ChannelHandler 互动。
该嵌入技术通常用于测试 ChannelHandler 的实现,但它也可用于将功能添加到现有的 ChannelHandler 而无需更改代码。嵌入传输的关键是Channel 的实现,称为“EmbeddedChannel”。
第10章描述了使用 EmbeddedChannel 来测试 ChannelHandlers。
- Introduction
- 开始
- Netty-异步和数据驱动
- Netty 介绍
- 构成部分
- 关于本书
- 第一个 Netty 应用
- 设置开发环境
- Netty 客户端/服务端 总览
- 写一个 echo 服务器
- 写一个 echo 客户端
- 编译和运行 Echo 服务器和客户端
- 总结
- Netty 总览
- Netty 快速入门
- Channel, Event 和 I/O
- 什么是 Bootstrapping 为什么要用
- ChannelHandler 和 ChannelPipeline
- 近距离观察 ChannelHandler
- 总结
- 核心功能
- Transport(传输)
- 案例研究:Transport 的迁移
- Transport API
- 包含的 Transport
- Transport 使用情况
- 总结
- Buffer(缓冲)
- Buffer API
- ByteBuf - 字节数据的容器
- 字节级别的操作
- ByteBufHolder
- ByteBuf 分配
- 总结
- ChannelHandler 和 ChannelPipeline
- ChannelHandler 家族
- ChannelPipeline
- ChannelHandlerContext
- 总结
- Codec 框架
- 什么是 Codec
- Decoder(解码器)
- Encoder(编码器)
- 抽象 Codec(编解码器)类
- 总结
- 提供了的 ChannelHandler 和 Codec
- 使用 SSL/TLS 加密 Netty 程序
- 构建 Netty HTTP/HTTPS 应用
- 空闲连接以及超时
- 解码分隔符和基于长度的协议
- 编写大型数据
- 序列化数据
- 总结
- Bootstrap 类型
- 引导客户端和无连接协议
- 引导服务器
- 从 Channel 引导客户端
- 在一个引导中添加多个 ChannelHandler
- 使用Netty 的 ChannelOption 和属性
- 关闭之前已经引导的客户端或服务器
- 总结
- 引导
- Bootstrap 类型
- 引导客户端和无连接协议
- 引导服务器
- 从 Channel 引导客户端
- 在一个引导中添加多个 ChannelHandler
- 使用Netty 的 ChannelOption 和属性
- 关闭之前已经引导的客户端或服务器
- 总结
- NETTY BY EXAMPLE
- 单元测试
- 总览
- 测试 ChannelHandler
- 测试异常处理
- 总结
- WebSocket
- WebSocket 程序示例
- 添加 WebSocket 支持
- 测试程序
- 总结
- SPDY
- SPDY 背景
- 示例程序
- 实现
- 启动 SpdyServer 并测试
- 总结
- 通过 UDP 广播事件
- UDP 基础
- UDP 广播
- UDP 示例
- EventLog 的 POJO
- 写广播器
- 写监视器
- 运行 LogEventBroadcaster 和 LogEventMonitor
- 总结
- 高级主题
- 实现自定义的编解码器
- 编解码器的范围
- 实现 Memcached 编解码器
- 了解 Memcached 二进制协议
- Netty 编码器和解码器
- 测试编解码器
- EventLoop 和线程模型
- 线程模型的总览
- EventLoop
- EventLoop
- I/O EventLoop/Thread 分配细节
- 总结
- 用例1:Droplr Firebase 和 Urban Airship
- 用例2:Facebook 和 Twitter