Skip to content

Commit 586d337

Browse files
committed
2 parents 947004c + 98c5124 commit 586d337

6 files changed

+315
-2
lines changed

CORE FUNCTIONS/Building Netty HTTPHTTPS applications.md

Lines changed: 230 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ Table 8.2 HTTP decoder and encoder
3535

3636
名称 | 描述
3737
-----|----
38-
HttpRequestEncoder |Encodes HttpRequest , HttpContent and
39-
LastHttpContent messages to bytes.
38+
HttpRequestEncoder |Encodes HttpRequest , HttpContent and LastHttpContent messages to bytes.
4039
HttpResponseEncoder | Encodes HttpResponse, HttpContent and LastHttpContent messages to bytes.
4140
HttpRequestDecoder | Decodes bytes into HttpRequest, HttpContent and LastHttpContent messages.
4241
HttpResponseDecoder | Decodes bytes into HttpResponse, HttpContent and LastHttpContent messages.
@@ -71,3 +70,232 @@ Listing 8.2 Add support for HTTP
7170
2. client: 添加 HttpRequestEncoder 用于发送请求到 server
7271
3. server: 添加 HttpRequestDecoder 用于接收来自 client 的请求
7372
4. server: 添加 HttpResponseEncoder 用来发送响应给 client
73+
74+
### HTTP消息聚合
75+
76+
安装 ChannelPipeline 中的初始化之后,你能够对不同 HttpObject 消息进行操作。但由于 HTTP 请求和响应可以由许多部分组合而成,你需要聚合他们形成完整的消息。为了消除这种繁琐任务, Netty 提供了一个聚合器,合并消息部件到 FullHttpRequest 和 FullHttpResponse 消息。这样您总是能够看到完整的消息内容。
77+
78+
这个操作有一个轻微的成本,消息段需要缓冲,直到完全可以将消息转发到下一个 ChannelInboundHandler 管道。但好处是,你不必担心消息碎片。
79+
80+
实现自动聚合只需添加另一个 ChannelHandler 到 ChannelPipeline。清单8.3显示了这是如何实现的。
81+
82+
Listing 8.3 Automatically aggregate HTTP message fragments
83+
84+
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {
85+
86+
private final boolean client;
87+
88+
public HttpAggregatorInitializer(boolean client) {
89+
this.client = client;
90+
}
91+
92+
@Override
93+
protected void initChannel(Channel ch) throws Exception {
94+
ChannelPipeline pipeline = ch.pipeline();
95+
if (client) {
96+
pipeline.addLast("codec", new HttpClientCodec()); //1
97+
} else {
98+
pipeline.addLast("codec", new HttpServerCodec()); //2
99+
}
100+
pipeline.addLast("aggegator", new HttpObjectAggregator(512 * 1024)); //3
101+
}
102+
}
103+
104+
1. client: 添加 HttpClientCodec
105+
2. server: 添加 HttpServerCodec 作为我们是 server 模式时
106+
3. 添加 HttpObjectAggregator 到 ChannelPipeline, 使用最大消息值是 512kb
107+
108+
109+
### HTTP 压缩
110+
111+
使用 HTTP 时建议压缩数据以减少传输流量,压缩数据会增加 CPU 负载,现在的硬件设施都很强大,大多数时候压缩数据时一个好主意。Netty 支持“gzip”和“deflate”,为此提供了两个 ChannelHandler 实现分别用于压缩和解压。看下面代码:
112+
113+
#### HTTP Request Header
114+
115+
客户端可以通过提供下面的头显示支持加密模式。然而服务器不是,所以不得不压缩它发送的数据。
116+
117+
GET /encrypted-area HTTP/1.1
118+
Host: www.example.com
119+
Accept-Encoding: gzip, deflate
120+
121+
下面是一个例子
122+
123+
Listing 8.4 Automatically compress HTTP messages
124+
125+
public class HttpAggregatorInitializer extends ChannelInitializer<Channel> {
126+
127+
private final boolean isClient;
128+
public HttpAggregatorInitializer(boolean isClient) {
129+
this.isClient = isClient;
130+
}
131+
@Override
132+
protected void initChannel(Channel ch) throws Exception {
133+
ChannelPipeline pipeline = ch.pipeline();
134+
if (isClient) {
135+
pipeline.addLast("codec", new HttpClientCodec()); //1
136+
pipeline.addLast("decompressor",new HttpContentDecompressor()); //2
137+
} else {
138+
pipeline.addLast("codec", new HttpServerCodec()); //3
139+
pipeline.addLast("compressor",new HttpContentCompressor()); //4
140+
}
141+
}
142+
}
143+
144+
1. client: 添加 HttpClientCodec
145+
2. client: 添加 HttpContentDecompressor 用于处理来自服务器的压缩的内容
146+
3. server: HttpServerCodec
147+
4. server: HttpContentCompressor 用于压缩来自 client 支持的 HttpContentCompressor
148+
149+
*压缩与依赖*
150+
151+
*注意,Java 6或者更早版本,如果要压缩数据,需要添加 [jzlib](http://www.jcraft.com/jzlib/) 到 classpath*
152+
153+
<dependency>
154+
<groupId>com.jcraft</groupId>
155+
<artifactId>jzlib</artifactId>
156+
<version>1.1.3</version>
157+
</dependency>
158+
159+
### 使用 HTTPS
160+
161+
启用 HTTPS,只需添加 SslHandler
162+
163+
Listing 8.5 Using HTTPS
164+
165+
public class HttpsCodecInitializer extends ChannelInitializer<Channel> {
166+
167+
private final SslContext context;
168+
private final boolean client;
169+
170+
public HttpsCodecInitializer(SslContext context, boolean client) {
171+
this.context = context;
172+
this.client = client;
173+
}
174+
175+
@Override
176+
protected void initChannel(Channel ch) throws Exception {
177+
ChannelPipeline pipeline = ch.pipeline();
178+
SSLEngine engine = context.newEngine(ch.alloc());
179+
pipeline.addFirst("ssl", new SslHandler(engine)); //1
180+
181+
if (client) {
182+
pipeline.addLast("codec", new HttpClientCodec()); //2
183+
} else {
184+
pipeline.addLast("codec", new HttpServerCodec()); //3
185+
}
186+
}
187+
}
188+
189+
1. 添加 SslHandler 到 pipeline 来启用 HTTPS
190+
2. client: 添加 HttpClientCodec
191+
3. server: 添加 HttpServerCodec ,如果是 server 模式的话
192+
193+
上面的代码就是一个很好的例子,解释了 Netty 的架构是如何让“重用”变成了“杠杆”。我们可以添加一个新的功能,甚至是一样重要的加密支持,几乎没有工作量,只需添加一个ChannelHandler 到 ChannelPipeline。
194+
195+
### WebSocket
196+
197+
HTTP 是不错的协议,但是如果需要实时发布信息怎么做?有个做法就是客户端一直轮询请求服务器,这种方式虽然可以达到目的,但是其缺点很多,也不是优秀的解决方案,为了解决这个问题,便出现了 WebSocket。
198+
199+
WebSocket 允许数据双向传输,而不需要请求-响应模式。早期的WebSocket 只能发送文本数据,然后现在不仅可以发送文本数据,也可以发送二进制数据,这使得可以使用 WebSocket 构建你想要的程序。下图是WebSocket 的通信示例图:
200+
201+
WebSocket 规范及其实现是为了一个更有效的解决方案。简单的说,
202+
一个WebSocket 提供一个 TCP 连接两个方向的交通。结合 WebSocket API 它提供了一个替代 HTTP 轮询双向通信从页面到远程服务器。
203+
204+
也就是说,WebSocket 提供真正的双向客户机和服务器之间的数据交换。
205+
我们不会对内部太多的细节,但我们应该提到,虽然最早实现仅限于文本数据,但现在不再是这样,WebSocket可以用于任意数据,就像一个正常的套接字。
206+
207+
图8.4给出了一个通用的 WebSocket 协议。在这种情况下的通信开始于普通 HTTP ,并“升级”为双向 WebSocket。
208+
209+
![](../iamges/Figure 8.4 WebSocket protocol.jpg)
210+
211+
212+
1. Client (HTTP) 与 Server 通讯
213+
2. Server (HTTP) 与 Client 通讯
214+
3. Client 通过 HTTP(s) 来进行 WebSocket 握手,并等待确认
215+
4. 连接协议升级至 WebSocket
216+
217+
Figure 8.4 WebSocket protocol
218+
219+
添加应用程序支持 WebSocket 只需要添加适当的客户端或服务器端WebSocket ChannelHandler 到管道。这个类将处理特殊 WebSocket 定义的消息类型,称为“帧。“如表8.3所示,这些可以归类为“数据”和“控制”帧。
220+
221+
Table 8.3 WebSocketFrame types
222+
223+
名称 | 描述
224+
-----|----
225+
BinaryWebSocketFrame | Data frame: binary data
226+
TextWebSocketFrame | Data frame: text data
227+
ContinuationWebSocketFrame | Data frame: text or binary data that belongs to a previous BinaryWebSocketFrame or TextWebSocketFrame
228+
CloseWebSocketFrame | Control frame: a CLOSE request, close status code and a phrase
229+
PingWebSocketFrame | Control frame: requests the send of a PongWebSocketFrame
230+
PongWebSocketFrame | Control frame: sent as response to a PingWebSocketFrame
231+
232+
由于 Netty 的主要是一个服务器端技术重点在这里创建一个 WebSocket server 。清单8.6使用 WebSocketServerProtocolHandler 提出了一个简单的例子。该类处理协议升级握手以及三个“控制”帧 Close, Ping 和 Pong。Text 和 Binary 数据帧将被传递到下一个处理程序(由你实现)进行处理。
233+
234+
Listing 8.6 Support WebSocket on the server
235+
236+
public class WebSocketServerInitializer extends ChannelInitializer<Channel> {
237+
@Override
238+
protected void initChannel(Channel ch) throws Exception {
239+
ch.pipeline().addLast(
240+
new HttpServerCodec(),
241+
new HttpObjectAggregator(65536), //1
242+
new WebSocketServerProtocolHandler("/websocket"), //2
243+
new TextFrameHandler(), //3
244+
new BinaryFrameHandler(), //4
245+
new ContinuationFrameHandler()); //5
246+
}
247+
248+
public static final class TextFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
249+
@Override
250+
public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
251+
// Handle text frame
252+
}
253+
}
254+
255+
public static final class BinaryFrameHandler extends SimpleChannelInboundHandler<BinaryWebSocketFrame> {
256+
@Override
257+
public void channelRead0(ChannelHandlerContext ctx, BinaryWebSocketFrame msg) throws Exception {
258+
// Handle binary frame
259+
}
260+
}
261+
262+
public static final class ContinuationFrameHandler extends SimpleChannelInboundHandler<ContinuationWebSocketFrame> {
263+
@Override
264+
public void channelRead0(ChannelHandlerContext ctx, ContinuationWebSocketFrame msg) throws Exception {
265+
// Handle continuation frame
266+
}
267+
}
268+
}
269+
270+
271+
1. 添加 HttpObjectAggregator 用于提供在握手时聚合 HttpRequest
272+
2. 添加 WebSocketServerProtocolHandler 用于处理色好给你寄握手如果请求是发送到"/websocket." 端点,当升级完成后,它将会处理Ping, Pong 和 Close 帧
273+
3. TextFrameHandler 将会处理 TextWebSocketFrames
274+
4. BinaryFrameHandler 将会处理 BinaryWebSocketFrames
275+
5. ContinuationFrameHandler 将会处理ContinuationWebSocketFrames
276+
277+
*加密 WebSocket*
278+
*只需插入 SslHandler 到作为 pipline 第一个 ChannelHandler*
279+
280+
详见 [Chapter 11 WebSocket](../NETTY BY EXAMPLE/WebSockets.md)
281+
282+
### SPDY
283+
284+
[SPDY](http://www.chromium.org/spdy)(读作“SPeeDY”)是Google 开发的基于 TCP 的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY 并不是一种用于替代 HTTP 的协议,而是对 HTTP 协议的增强。SPDY 实现技术:
285+
286+
* 压缩报头
287+
* 加密所有
288+
* 多路复用连接
289+
* 提供支持不同的传输优先级
290+
291+
SPDY 主要有5个版本:
292+
293+
* 1 - 初始化版本,但没有使用
294+
* 2 - 新特性,包含服务器推送
295+
* 3 - 新特性包含流控制和更新压缩
296+
* 3.1 - 会话层流程控制
297+
* 4.0 - 流量控制,并与 HTTP 2.0 更加集成
298+
299+
SPDY 被很多浏览器支持,包括 Google Chrome, Firefox, 和 Opera
300+
301+
Netty 支持 版本 2 和 3 (包含3.1)的支持。这些版本被广泛应用,可以支持更多的用户。更多内容详见 [Chapter 12](../NETTY BY EXAMPLE/SPDY.md)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
解码分隔符和基于长度的协议
2+
====
3+
4+
使用 Netty 时会遇到需要解码以分隔符和长度为基础的协议,本节讲解Netty 如何解码这些协议。
5+
6+
### 分隔符协议
7+
8+
经常需要处理分隔符协议或创建基于它们的协议,例如[SMTP](http://www.ietf.org/rfc/rfc2821.txt)[POP3](http://www.ietf.org/rfc/rfc1939.txt)[IMAP](http://tools.ietf.org/html/rfc3501)[Telnet](http://tools.ietf.org/search/rfc854)等等。Netty 附带的解码器可以很容易的提取一些序列分隔:
9+
10+
Table 8.5 Decoders for handling delimited and length-based protocols
11+
12+
名称 | 描述
13+
-----|----
14+
DelimiterBasedFrameDecoder | 接收ByteBuf由一个或多个分隔符拆分,如NUL或换行符
15+
LineBasedFrameDecoder| 接收ByteBuf以分割线结束,如"\n"和"\r\n"
16+
17+
下图显示了使用"\r\n"分隔符的处理:
18+
19+
![](../images/Figure 8.5 Handling delimited frames.jpg)
20+
21+
1. 字节流
22+
2. 第一帧
23+
3. 第二帧
24+
25+
Figure 8.5 Handling delimited frames
26+
27+
下面展示了如何用 LineBasedFrameDecoder 处理
28+
29+
Listing 8.8 Handling line-delimited frames
30+
31+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
空闲连接以及超时
2+
====
3+
4+
检测空闲连接和超时是为了及时释放资源。常见的方法发送消息用于测试一个不活跃的连接来,通常称为“心跳”,到远端来确定它是否还活着。(一个更激进的方法是简单地断开那些指定的时间间隔的不活跃的连接)。
5+
6+
处理空闲连接是一项常见的任务,Netty 提供了几个 ChannelHandler 实现此目的。表8.4概述。
7+
8+
Table 8.4 ChannelHandlers for idle connections and timeouts
9+
10+
名称 | 描述
11+
-----|----
12+
IdleStateHandler | fires an IdleStateEvent if the connection idles too long. You can then handle the IdleStateEvent by overriding the userEventTriggered(...) method in your ChannelInboundHandler.
13+
ReadTimeoutHandler |throws a ReadTimeoutException and closes the Channel when no inbound
14+
data is received for a specified interval. The ReadTimeoutException can be detected by overriding the exceptionCaught(...) method of your ChannelHandler.
15+
WriteTimeoutHandler | throws a WriteTimeoutException and closes the Channel when no inbound
16+
data is received for a specified interval. The WriteTimeoutException can be detected by overriding the exceptionCaught(...) method of your ChannelHandler.
17+
18+
详细看下 IdleStateHandler,下面是一个例子,当超过60秒没有数据收到时,就会得到通知,此时就发送心跳到远端,如果没有回应,连接就关闭。
19+
20+
Listing 8.7 Sending heartbeats
21+
22+
public class IdleStateHandlerInitializer extends ChannelInitializer<Channel> {
23+
24+
@Override
25+
protected void initChannel(Channel ch) throws Exception {
26+
ChannelPipeline pipeline = ch.pipeline();
27+
pipeline.addLast(new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS)); //1
28+
pipeline.addLast(new HeartbeatHandler());
29+
}
30+
31+
public static final class HeartbeatHandler extends ChannelInboundHandlerAdapter {
32+
private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(
33+
Unpooled.copiedBuffer("HEARTBEAT", CharsetUtil.ISO_8859_1)); //2
34+
35+
@Override
36+
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
37+
if (evt instanceof IdleStateEvent) {
38+
ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate())
39+
.addListener(ChannelFutureListener.CLOSE_ON_FAILURE); //3
40+
} else {
41+
super.userEventTriggered(ctx, evt); //4
42+
}
43+
}
44+
}
45+
}
46+
47+
1. IdleStateHandler 将通过 IdleStateEvent 调用 userEventTriggered ,如果连接没有接收或发送数据超过60秒钟
48+
2. 心跳发送到远端
49+
3. 发送的心跳并添加一个侦听器,如果发送操作失败将关闭连接
50+
4. 事件不是一个 IdleStateEvent 的话,就将它传递给下一个处理程序
51+
52+
总而言之,这个例子说明了如何使用 IdleStateHandler 测试远端是否还活着,如果不是就关闭连接释放资源。

SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ This is the summary of my book.
5050
* [提供了的 ChannelHandler 和 Codec](CORE FUNCTIONS/Provided ChannelHandlers and Codecs.md)
5151
* [使用 SSL/TLS 加密 Netty 程序](CORE FUNCTIONS/Securing Netty applications with SSLTLS.md)
5252
* [构建 Netty HTTP/HTTPS 应用](CORE FUNCTIONS/Building Netty HTTPHTTPS applications.md)
53+
* [空闲连接以及超时](CORE FUNCTIONS/Idle connections and Timeouts.md)
54+
* [解码分隔符和基于长度的协议](CORE FUNCTIONS/Decoding delimited and length-based protocols.md)
5355
* [Bootstrapping](CORE FUNCTIONS/Bootstrapping.md)
5456
* NETTY BY EXAMPLE
5557
* [单元测试](NETTY BY EXAMPLE/Unit Testing.md)
21.4 KB
Loading
11.5 KB
Loading

0 commit comments

Comments
 (0)