diff --git a/README.md b/README.md index c134145c..2a7eee0b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,66 @@ -This Repository contains the source-code for all chapters of the book [Netty in Action](http://manning.com/maurer) -by Norman Maurer and Marvin Allen Wolfthal. +# 《Netty 实战》 Netty In Action 中文版 -Latest version: https://github.com/normanmaurer/netty-in-action/tree/2.0-SNAPSHOT +>代码清单已经更新到 Netty 4.1.25.final -Enjoy! Feedback and PR's welcome! +[如何评价这本书-知乎](https://www.zhihu.com/question/58838575) +[关于本书-豆瓣](https://book.douban.com/subject/27038538/) + +[京东链接(现货发售):《Netty实战》([美]诺曼·毛瑞尔(Norman Maurer),马文·艾伦·沃尔夫泰尔(Marvin Allen Wolfthal)) ](https://union-click.jd.com/jdc?d=oN4CCW&come=appmessage) + +[不负好时光《Netty IN ACTION》中文版《Netty实战》翻译手记](http://www.epubit.com.cn/article/1171) + +[不负好时光《Netty IN ACTION》中文版《Netty实战》翻译手记(ATA内网)](https://www.atatech.org/articles/79051?flag_data_from=recommend) + +![image](https://cloud.githubusercontent.com/assets/501740/25295296/94d2ef06-2715-11e7-9a2a-916d77014cfc.png) + + +## 内容提要 + +本书是为想要或者正在使用 Java 从事高性能网络编程的人而写的,循序渐进地介绍了 Netty +各个方面的内容。 + +本书共分为 4 个部分:第一部分详细地介绍 Netty 的相关概念以及核心组件,第二部分介绍 +自定义协议经常用到的编解码器,第三部分介绍 Netty 对于应用层高级协议的支持,会覆盖常见 +的协议及其在实践中的应用,第四部分是几个案例研究。此外,附录部分还会简单地介绍 Maven, +以及如何通过使用 Maven 编译和运行本书中的示例。 + +阅读本书不需要读者精通 Java 网络和并发编程。如果想要更加深入地理解本书背后的理念 +以及 Netty 源码本身,可以系统地学习一下 Java 网络编程、 NIO、并发和异步编程以及相关的 +设计模式。 + +## 说明 + +这个仓库包含了[Netty In Action](http://www.manning.com/maurer/) 这本书的中文版 [Netty实战](http://www.epubit.com.cn) 的代码清单. +为了更好地服务于读者,进行了如下方面的改进. -Prerequisites - JDK 1.7.0u71 or better +相对于英文版本([2.0-SNAPSHOT](https://github.com/ReactivePlatform/netty-in-action-cn/tree/2.0-SNAPSHOT)分支): + +1. 更新了行文中的注释 +2. 按照中文版本的排版进行了调整 +3. 所有的代码清单以及跳转都使用了中文版书籍中的翻译 + + +## 反馈 + +上游版本的更新,请直接将PR的目标调整为本仓库的`master`分支 + +中文版本的更新,请将PR的目标调整为本仓库的`ChineseVersion`分支 + +## 使用 + +请直接克隆本项目即可,建议对照原文查看代码. + +## 勘误 +[前言: 2001 => 2011](https://github.com/ReactivePlatform/netty-in-action-cn/issues/2) + +----- + +Prerequisites - Maven 3.3.9 or better + maven 3.3.9 + JDK 8 If you want to build everything at once, from the top directory run diff --git a/chapter1/src/main/java/nia/chapter1/BlockingIoExample.java b/chapter1/src/main/java/nia/chapter1/BlockingIoExample.java index 6ac3dc14..bd2d002f 100644 --- a/chapter1/src/main/java/nia/chapter1/BlockingIoExample.java +++ b/chapter1/src/main/java/nia/chapter1/BlockingIoExample.java @@ -10,27 +10,34 @@ /** * Created by kerr. * - * Listing 1.1 Blocking I/O example + * 代码清单 1-1 阻塞 I/O 示例 */ public class BlockingIoExample { /** - * Listing 1.1 Blocking I/O example + * 代码清单 1-1 阻塞 I/O 示例 * */ public void serve(int portNumber) throws IOException { + //创建一个新的 ServerSocket,用以监听指定端口上的连接请求 ServerSocket serverSocket = new ServerSocket(portNumber); + //对accept()方法的调用将被阻塞,直到一个连接建立 Socket clientSocket = serverSocket.accept(); + //这些流对象都派生于该套接字的流对象 BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); String request, response; + //处理循环开始 while ((request = in.readLine()) != null) { if ("Done".equals(request)) { break; } + //请求被传递给服务器的处理方法 response = processRequest(request); + //服务器的响应被发送给了客户端 out.println(response); + //继续执行处理循环 } } diff --git a/chapter1/src/main/java/nia/chapter1/ConnectExample.java b/chapter1/src/main/java/nia/chapter1/ConnectExample.java index fa3ab7cf..9108c7c3 100644 --- a/chapter1/src/main/java/nia/chapter1/ConnectExample.java +++ b/chapter1/src/main/java/nia/chapter1/ConnectExample.java @@ -13,38 +13,43 @@ /** * Created by kerr. * - * Listing 1.3 Asynchronous connect + * 代码清单 1-3 异步地建立连接 * - * Listing 1.4 Callback in action + * 代码清单 1-4 回调实战 */ public class ConnectExample { private static final Channel CHANNEL_FROM_SOMEWHERE = new NioSocketChannel(); /** - * Listing 1.3 Asynchronous connect + * 代码清单 1-3 异步地建立连接 * - * Listing 1.4 Callback in action + * 代码清单 1-4 回调实战 * */ public static void connect() { Channel channel = CHANNEL_FROM_SOMEWHERE; //reference form somewhere // Does not block + //异步地连接到远程节点 ChannelFuture future = channel.connect( new InetSocketAddress("192.168.0.1", 25)); + //注册一个 ChannelFutureListener,以便在操作完成时获得通知 future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) { + //检查操作的状态 if (future.isSuccess()) { + //如果操作是成功的,则创建一个 ByteBuf 以持有数据 ByteBuf buffer = Unpooled.copiedBuffer( "Hello", Charset.defaultCharset()); + //将数据异步地发送到远程节点。返回一个 ChannelFuture ChannelFuture wf = future.channel() .writeAndFlush(buffer); // ... } else { + //如果发生错误,则访问描述原因的 Throwable Throwable cause = future.cause(); cause.printStackTrace(); } } }); - } } \ No newline at end of file diff --git a/chapter1/src/main/java/nia/chapter1/ConnectHandler.java b/chapter1/src/main/java/nia/chapter1/ConnectHandler.java index 01313d78..2e04d547 100644 --- a/chapter1/src/main/java/nia/chapter1/ConnectHandler.java +++ b/chapter1/src/main/java/nia/chapter1/ConnectHandler.java @@ -6,10 +6,11 @@ /** * Created by kerr. * - * Listing 1.2 ChannelHandler triggered by a callback + * 代码清单 1-2 被回调触发的 ChannelHandler */ public class ConnectHandler extends ChannelInboundHandlerAdapter { @Override + //当一个新的连接已经被建立时,channelActive(ChannelHandlerContext)将会被调用 public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println( diff --git a/chapter1/src/main/java/nia/chapter1/package-info.java b/chapter1/src/main/java/nia/chapter1/package-info.java index 8b659ac8..66a62fd1 100644 --- a/chapter1/src/main/java/nia/chapter1/package-info.java +++ b/chapter1/src/main/java/nia/chapter1/package-info.java @@ -1,12 +1,12 @@ /** * kerr. * - * Listing 1.1 Blocking I/O example {@link nia.chapter1.BlockingIoExample#serve(int)} + * 代码清单 1-1 阻塞 I/O 示例 {@link nia.chapter1.BlockingIoExample#serve(int)} * - * Listing 1.2 ChannelHandler triggered by a callback {@link nia.chapter1.ConnectHandler} + * 代码清单 1-2 被回调触发的 ChannelHandler {@link nia.chapter1.ConnectHandler} * - * Listing 1.3 Asynchronous connect {@link nia.chapter1.ConnectExample#connect()} + * 代码清单 1-3 异步地建立连接 {@link nia.chapter1.ConnectExample#connect()} * - * Listing 1.4 Callback in action {@link nia.chapter1.ConnectExample#connect()} + * 代码清单 1-4 回调实战 {@link nia.chapter1.ConnectExample#connect()} */ package nia.chapter1; \ No newline at end of file diff --git a/chapter10/src/main/java/nia/chapter10/ByteToCharDecoder.java b/chapter10/src/main/java/nia/chapter10/ByteToCharDecoder.java index 6a9793f9..2f4618e4 100644 --- a/chapter10/src/main/java/nia/chapter10/ByteToCharDecoder.java +++ b/chapter10/src/main/java/nia/chapter10/ByteToCharDecoder.java @@ -7,15 +7,17 @@ import java.util.List; /** - * Listing 10.8 Class ByteToCharDecoder + * 代码清单 10-8 ByteToCharDecoder 类 * * @author Norman Maurer */ +//扩展了ByteToMessageDecoder public class ByteToCharDecoder extends ByteToMessageDecoder { @Override public void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { if (in.readableBytes() >= 2) { + //将一个或者多个 Character 对象添加到传出的 List 中 out.add(in.readChar()); } } diff --git a/chapter10/src/main/java/nia/chapter10/CharToByteEncoder.java b/chapter10/src/main/java/nia/chapter10/CharToByteEncoder.java index 966b658b..3fdde074 100644 --- a/chapter10/src/main/java/nia/chapter10/CharToByteEncoder.java +++ b/chapter10/src/main/java/nia/chapter10/CharToByteEncoder.java @@ -5,15 +5,17 @@ import io.netty.handler.codec.MessageToByteEncoder; /** - * Listing 10.9 Class CharToByteEncoder + * 代码清单 9 CharToByteEncoder 类 * * @author Norman Maurer */ +//扩展了MessageToByteEncoder public class CharToByteEncoder extends MessageToByteEncoder { @Override public void encode(ChannelHandlerContext ctx, Character msg, ByteBuf out) throws Exception { + //将 Character 解码为 char,并将其写入到出站 ByteBuf 中 out.writeChar(msg); } } diff --git a/chapter10/src/main/java/nia/chapter10/CombinedByteCharCodec.java b/chapter10/src/main/java/nia/chapter10/CombinedByteCharCodec.java index 5068df53..039baf88 100644 --- a/chapter10/src/main/java/nia/chapter10/CombinedByteCharCodec.java +++ b/chapter10/src/main/java/nia/chapter10/CombinedByteCharCodec.java @@ -3,14 +3,15 @@ import io.netty.channel.CombinedChannelDuplexHandler; /** - * Listing 10.10 CombinedChannelDuplexHandler + * 代码清单 CombinedChannelDuplexHandler * * @author Norman Maurer */ - +//通过该解码器和编码器实现参数化 CombinedByteCharCodec public class CombinedByteCharCodec extends CombinedChannelDuplexHandler { public CombinedByteCharCodec() { + //将委托实例传递给父类 super(new ByteToCharDecoder(), new CharToByteEncoder()); } } diff --git a/chapter10/src/main/java/nia/chapter10/IntegerToStringDecoder.java b/chapter10/src/main/java/nia/chapter10/IntegerToStringDecoder.java index bccbf693..73d06175 100644 --- a/chapter10/src/main/java/nia/chapter10/IntegerToStringDecoder.java +++ b/chapter10/src/main/java/nia/chapter10/IntegerToStringDecoder.java @@ -6,15 +6,17 @@ import java.util.List; /** - * Listing 10.3 Class IntegerToStringDecoder + * 代码清单 10-3 IntegerToStringDecoder 类 * * @author Norman Maurer */ +//扩展了MessageToMessageDecoder public class IntegerToStringDecoder extends MessageToMessageDecoder { @Override public void decode(ChannelHandlerContext ctx, Integer msg, List out) throws Exception { + //将 Integer 消息转换为它的 String 表示,并将其添加到输出的 List 中 out.add(String.valueOf(msg)); } } diff --git a/chapter10/src/main/java/nia/chapter10/IntegerToStringEncoder.java b/chapter10/src/main/java/nia/chapter10/IntegerToStringEncoder.java index 6c6390da..58afa464 100644 --- a/chapter10/src/main/java/nia/chapter10/IntegerToStringEncoder.java +++ b/chapter10/src/main/java/nia/chapter10/IntegerToStringEncoder.java @@ -6,15 +6,17 @@ import java.util.List; /** - * Listing 10.6 Class IntegerToStringEncoder + * 代码清单 10-6 IntegerToStringEncoder 类 * * @author Norman Maurer */ +//扩展了 MessageToMessageEncoder public class IntegerToStringEncoder extends MessageToMessageEncoder { @Override public void encode(ChannelHandlerContext ctx, Integer msg, List out) throws Exception { + //将 Integer 转换为 String,并将其添加到 List 中 out.add(String.valueOf(msg)); } } diff --git a/chapter10/src/main/java/nia/chapter10/SafeByteToMessageDecoder.java b/chapter10/src/main/java/nia/chapter10/SafeByteToMessageDecoder.java index 6328cd28..04ffe513 100644 --- a/chapter10/src/main/java/nia/chapter10/SafeByteToMessageDecoder.java +++ b/chapter10/src/main/java/nia/chapter10/SafeByteToMessageDecoder.java @@ -8,18 +8,20 @@ import java.util.List; /** - * Listing 10.4 TooLongFrameException + * 代码清单 10-4 TooLongFrameException * * @author Norman Maurer */ - +//扩展 ByteToMessageDecoder 以将字节解码为消息 public class SafeByteToMessageDecoder extends ByteToMessageDecoder { private static final int MAX_FRAME_SIZE = 1024; @Override public void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { int readable = in.readableBytes(); + //检查缓冲区中是否有超过 MAX_FRAME_SIZE 个字节 if (readable > MAX_FRAME_SIZE) { + //跳过所有的可读字节,抛出 TooLongFrameException 并通知 ChannelHandler in.skipBytes(readable); throw new TooLongFrameException("Frame too big!"); } diff --git a/chapter10/src/main/java/nia/chapter10/ShortToByteEncoder.java b/chapter10/src/main/java/nia/chapter10/ShortToByteEncoder.java index 51e5b80c..5c7d3bad 100644 --- a/chapter10/src/main/java/nia/chapter10/ShortToByteEncoder.java +++ b/chapter10/src/main/java/nia/chapter10/ShortToByteEncoder.java @@ -5,14 +5,16 @@ import io.netty.handler.codec.MessageToByteEncoder; /** - * Listing 10.5 Class ShortToByteEncoder + * 代码清单 10-5 ShortToByteEncoder 类 * * @author Norman Maurer */ +//扩展了MessageToByteEncoder public class ShortToByteEncoder extends MessageToByteEncoder { @Override public void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out) throws Exception { + //将 Short 写入 ByteBuf 中 out.writeShort(msg); } } diff --git a/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder.java b/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder.java index 48235e76..10991689 100644 --- a/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder.java +++ b/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder.java @@ -7,15 +7,18 @@ import java.util.List; /** - * Listing 10.1 Class ToIntegerDecoder extends ByteToMessageDecoder + * 代码清单 10-1 ToIntegerDecoder 类扩展了 ByteToMessageDecoder * * @author Norman Maurer */ +//扩展ByteToMessageDecoder类,以将字节解码为特定的格式 public class ToIntegerDecoder extends ByteToMessageDecoder { @Override public void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + //检查是否至少有 4 字节可读(一个 int 的字节长度) if (in.readableBytes() >= 4) { + //从入站 ByteBuf 中读取一个 int,并将其添加到解码消息的 List 中 out.add(in.readInt()); } } diff --git a/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder2.java b/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder2.java index 52a0157a..53fba55b 100644 --- a/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder2.java +++ b/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder2.java @@ -7,15 +7,17 @@ import java.util.List; /** - * Listing 10.2 Class ToIntegerDecoder2 extends ReplayingDecoder + * 代码清单 10-2 ToIntegerDecoder2 类扩展了 ReplayingDecoder * * @author Norman Maurer */ +//扩展 ReplayingDecoder 以将字节解码为消息 public class ToIntegerDecoder2 extends ReplayingDecoder { @Override - public void decode(ChannelHandlerContext ctx, ByteBuf in, + public void decode(ChannelHandlerContext ctx, ByteBuf in, //传入的 ByteBuf 是 ReplayingDecoderByteBuf List out) throws Exception { + //从入站 ByteBuf 中读取 一个 int,并将其添加到解码消息的 List 中 out.add(in.readInt()); } } diff --git a/chapter10/src/main/java/nia/chapter10/WebSocketConvertHandler.java b/chapter10/src/main/java/nia/chapter10/WebSocketConvertHandler.java index dc5a3f52..7bfba048 100644 --- a/chapter10/src/main/java/nia/chapter10/WebSocketConvertHandler.java +++ b/chapter10/src/main/java/nia/chapter10/WebSocketConvertHandler.java @@ -9,7 +9,7 @@ import java.util.List; /** - * Listing 10.7 Using MessageToMessageCodec + * 代码清单 10-7 使用 MessageToMessageCodec * * @author Norman Maurer */ @@ -18,10 +18,12 @@ public class WebSocketConvertHandler extends MessageToMessageCodec { @Override + //将 MyWebSocketFrame 编码为指定的 WebSocketFrame 子类型 protected void encode(ChannelHandlerContext ctx, WebSocketConvertHandler.MyWebSocketFrame msg, List out) throws Exception { ByteBuf payload = msg.getData().duplicate().retain(); + //实例化一个指定子类型的 WebSocketFrame switch (msg.getType()) { case BINARY: out.add(new BinaryWebSocketFrame(payload)); @@ -47,6 +49,7 @@ protected void encode(ChannelHandlerContext ctx, } @Override + //将 WebSocketFrame 解码为 MyWebSocketFrame,并设置 FrameType protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, List out) throws Exception { ByteBuf payload = msg.content().duplicate().retain(); @@ -80,7 +83,9 @@ protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, } } + //声明 WebSocketConvertHandler 所使用的 OUTBOUND_IN 类型 public static final class MyWebSocketFrame { + //定义拥有被包装的有效负载的 WebSocketFrame 的类型 public enum FrameType { BINARY, CLOSE, diff --git a/chapter10/src/main/java/nia/chapter10/package-info.java b/chapter10/src/main/java/nia/chapter10/package-info.java index d5f47fa7..1df510c5 100644 --- a/chapter10/src/main/java/nia/chapter10/package-info.java +++ b/chapter10/src/main/java/nia/chapter10/package-info.java @@ -1,24 +1,24 @@ /** * Created by kerr. * - * Listing 10.1 Class ToIntegerDecoder extends ByteToMessageDecoder {@link nia.chapter10.ToIntegerDecoder} + * 代码清单 10-1 ToIntegerDecoder 类扩展了 ByteToMessageDecoder {@link nia.chapter10.ToIntegerDecoder} * - * Listing 10.2 Class ToIntegerDecoder2 extends ReplayingDecoder {@link nia.chapter10.ToIntegerDecoder2} + * 代码清单 10-2 ToIntegerDecoder2 类扩展了 ReplayingDecoder {@link nia.chapter10.ToIntegerDecoder2} * - * Listing 10.3 Class IntegerToStringDecoder {@link nia.chapter10.IntegerToStringDecoder} + * 代码清单 10-3 IntegerToStringDecoder 类 {@link nia.chapter10.IntegerToStringDecoder} * - * Listing 10.4 TooLongFrameException {@link nia.chapter10.SafeByteToMessageDecoder} + * 代码清单 10-4 TooLongFrameException {@link nia.chapter10.SafeByteToMessageDecoder} * - * Listing 10.5 Class ShortToByteEncoder {@link nia.chapter10.ShortToByteEncoder} + * 代码清单 10-5 ShortToByteEncoder 类 {@link nia.chapter10.ShortToByteEncoder} * - * Listing 10.6 Class IntegerToStringEncoder {@link nia.chapter10.IntegerToStringEncoder} + * 代码清单 10-6 IntegerToStringEncoder 类 {@link nia.chapter10.IntegerToStringEncoder} * - * Listing 10.7 Using MessageToMessageCodec {@link nia.chapter10.WebSocketConvertHandler} + * 代码清单 10-7 使用 MessageToMessageCodec {@link nia.chapter10.WebSocketConvertHandler} * - * Listing 10.8 Class ByteToCharDecoder {@link nia.chapter10.ByteToCharDecoder} + * 代码清单 10-8 ByteToCharDecoder 类 {@link nia.chapter10.ByteToCharDecoder} * - * Listing 10.9 Class CharToByteEncoder {@link nia.chapter10.CharToByteEncoder} + * 代码清单 9 CharToByteEncoder 类 {@link nia.chapter10.CharToByteEncoder} * - * Listing 10.10 CombinedChannelDuplexHandler {@link nia.chapter10.CombinedByteCharCodec} + * 代码清单 CombinedChannelDuplexHandler {@link nia.chapter10.CombinedByteCharCodec} */ package nia.chapter10; \ No newline at end of file diff --git a/chapter11/pom.xml b/chapter11/pom.xml index fd51c61f..162db5e4 100644 --- a/chapter11/pom.xml +++ b/chapter11/pom.xml @@ -12,7 +12,7 @@ chapter11 Chapter 11. Provided ChannelHandlers and codecs - 2.5.0 + 3.9.0 diff --git a/chapter11/src/main/java/nia/chapter11/ChunkedWriteHandlerInitializer.java b/chapter11/src/main/java/nia/chapter11/ChunkedWriteHandlerInitializer.java index fcde7c2d..9df741a6 100644 --- a/chapter11/src/main/java/nia/chapter11/ChunkedWriteHandlerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/ChunkedWriteHandlerInitializer.java @@ -10,7 +10,7 @@ import java.io.FileInputStream; /** - * Listing 11.12 of Netty in Action + * 代码清单 11-12 使用 ChunkedStream 传输文件内容 * * @author Norman Maurer */ @@ -26,8 +26,11 @@ public ChunkedWriteHandlerInitializer(File file, SslContext sslCtx) { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); + //将 SslHandler 添加到 ChannelPipeline 中 pipeline.addLast(new SslHandler(sslCtx.newEngine(ch.alloc()))); + //添加 ChunkedWriteHandler 以处理作为 ChunkedInput 传入的数据 pipeline.addLast(new ChunkedWriteHandler()); + //一旦连接建立,WriteStreamHandler 就开始写文件数据 pipeline.addLast(new WriteStreamHandler()); } @@ -35,6 +38,7 @@ public final class WriteStreamHandler extends ChannelInboundHandlerAdapter { @Override + //当连接建立时,channelActive() 方法将使用 ChunkedInput 写文件数据 public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); diff --git a/chapter11/src/main/java/nia/chapter11/CmdHandlerInitializer.java b/chapter11/src/main/java/nia/chapter11/CmdHandlerInitializer.java index 1ab893fc..bd4cca08 100644 --- a/chapter11/src/main/java/nia/chapter11/CmdHandlerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/CmdHandlerInitializer.java @@ -5,7 +5,7 @@ import io.netty.handler.codec.LineBasedFrameDecoder; /** - * Listing 11.9 Using a ChannelInitializer as a decoder installer + * 代码清单 11-9 使用 ChannelInitializer 安装解码器 * * @author Norman Maurer */ @@ -14,10 +14,13 @@ public class CmdHandlerInitializer extends ChannelInitializer { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); + //添加 CmdDecoder 以提取 Cmd 对象,并将它转发给下一个 ChannelInboundHandler pipeline.addLast(new CmdDecoder(64 * 1024)); + //添加 CmdHandler 以接收和处理 Cmd 对象 pipeline.addLast(new CmdHandler()); } + //Cmd POJO public static final class Cmd { private final ByteBuf name; private final ByteBuf args; @@ -44,12 +47,17 @@ public CmdDecoder(int maxLength) { @Override protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { + //从 ByteBuf 中提取由行尾符序列分隔的帧 ByteBuf frame = (ByteBuf) super.decode(ctx, buffer); if (frame == null) { + //如果输入中没有帧,则返回 null + return null; } + //查找第一个空格字符的索引。前面是命令名称,接着是参数 int index = frame.indexOf(frame.readerIndex(), frame.writerIndex(), SPACE); + //使用包含有命令名称和参数的切片创建新的 Cmd 对象 return new Cmd(frame.slice(frame.readerIndex(), index), frame.slice(index + 1, frame.writerIndex())); } @@ -61,6 +69,7 @@ public static final class CmdHandler public void channelRead0(ChannelHandlerContext ctx, Cmd msg) throws Exception { // Do something with the command + //处理传经 ChannelPipeline 的 Cmd 对象 } } } diff --git a/chapter11/src/main/java/nia/chapter11/FileRegionWriteHandler.java b/chapter11/src/main/java/nia/chapter11/FileRegionWriteHandler.java index 13f65b39..44af7204 100644 --- a/chapter11/src/main/java/nia/chapter11/FileRegionWriteHandler.java +++ b/chapter11/src/main/java/nia/chapter11/FileRegionWriteHandler.java @@ -9,7 +9,7 @@ /** * Created by kerr. * - * Listing 11.11 Transferring file contents with FileRegion + * 代码清单 11-11 使用 FileRegion 传输文件的内容 */ public class FileRegionWriteHandler extends ChannelInboundHandlerAdapter { private static final Channel CHANNEL_FROM_SOMEWHERE = new NioSocketChannel(); @@ -20,15 +20,19 @@ public void channelActive(final ChannelHandlerContext ctx) throws Exception { File file = FILE_FROM_SOMEWHERE; //get reference from somewhere Channel channel = CHANNEL_FROM_SOMEWHERE; //get reference from somewhere //... + //创建一个 FileInputStream FileInputStream in = new FileInputStream(file); + //以该文件的完整长度创建一个新的 DefaultFileRegion FileRegion region = new DefaultFileRegion( in.getChannel(), 0, file.length()); + //发送该 DefaultFileRegion,并注册一个 ChannelFutureListener channel.writeAndFlush(region).addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { + //处理失败 Throwable cause = future.cause(); // Do something } diff --git a/chapter11/src/main/java/nia/chapter11/HttpAggregatorInitializer.java b/chapter11/src/main/java/nia/chapter11/HttpAggregatorInitializer.java index 83944191..f86a5bd5 100644 --- a/chapter11/src/main/java/nia/chapter11/HttpAggregatorInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/HttpAggregatorInitializer.java @@ -8,7 +8,7 @@ import io.netty.handler.codec.http.HttpServerCodec; /** - * Listing 11.3 Automatically aggregating HTTP message fragments + * 代码清单 11-3 自动聚合 HTTP 的消息片段 * * @author Norman Maurer */ @@ -23,10 +23,13 @@ public HttpAggregatorInitializer(boolean isClient) { protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (isClient) { + //如果是客户端,则添加 HttpClientCodec pipeline.addLast("codec", new HttpClientCodec()); } else { + //如果是服务器,则添加 HttpServerCodec pipeline.addLast("codec", new HttpServerCodec()); } + //将最大的消息大小为 512 KB 的 HttpObjectAggregator 添加到 ChannelPipeline pipeline.addLast("aggregator", new HttpObjectAggregator(512 * 1024)); } diff --git a/chapter11/src/main/java/nia/chapter11/HttpCompressionInitializer.java b/chapter11/src/main/java/nia/chapter11/HttpCompressionInitializer.java index 9c6ece23..60182023 100644 --- a/chapter11/src/main/java/nia/chapter11/HttpCompressionInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/HttpCompressionInitializer.java @@ -9,7 +9,7 @@ import io.netty.handler.codec.http.HttpServerCodec; /** - * Listing 11.4 Automatically compressing HTTP messages + * 代码清单 11-4 自动压缩 HTTP 消息 * * @author Norman Maurer */ @@ -24,11 +24,15 @@ public HttpCompressionInitializer(boolean isClient) { protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (isClient) { + //如果是客户端,则添加 HttpClientCodec pipeline.addLast("codec", new HttpClientCodec()); + //如果是客户端,则添加 HttpContentDecompressor 以处理来自服务器的压缩内容 pipeline.addLast("decompressor", new HttpContentDecompressor()); } else { + //如果是服务器,则添加 HttpServerCodec pipeline.addLast("codec", new HttpServerCodec()); + //如果是服务器,则添加HttpContentCompressor 来压缩数据(如果客户端支持它) pipeline.addLast("compressor", new HttpContentCompressor()); } diff --git a/chapter11/src/main/java/nia/chapter11/HttpPipelineInitializer.java b/chapter11/src/main/java/nia/chapter11/HttpPipelineInitializer.java index aa9afd9f..fa392622 100644 --- a/chapter11/src/main/java/nia/chapter11/HttpPipelineInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/HttpPipelineInitializer.java @@ -9,7 +9,7 @@ import io.netty.handler.codec.http.HttpResponseEncoder; /** - * Listing 11.2 Adding support for HTTP + * 代码清单 11-2 添加 HTTP 支持 * * @author Norman Maurer */ @@ -25,10 +25,14 @@ public HttpPipelineInitializer(boolean client) { protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); if (client) { + //如果是客户端,则添加 HttpResponseDecoder 以处理来自服务器的响应 pipeline.addLast("decoder", new HttpResponseDecoder()); + //如果是客户端,则添加 HttpRequestEncoder 以向服务器发送请求 pipeline.addLast("encoder", new HttpRequestEncoder()); } else { + //如果是服务器,则添加 HttpRequestDecoder 以接收来自客户端的请求 pipeline.addLast("decoder", new HttpRequestDecoder()); + //如果是服务器,则添加 HttpResponseEncoder 以向客户端发送响应 pipeline.addLast("encoder", new HttpResponseEncoder()); } } diff --git a/chapter11/src/main/java/nia/chapter11/HttpsCodecInitializer.java b/chapter11/src/main/java/nia/chapter11/HttpsCodecInitializer.java index 78e1f7ac..6c2aee0a 100644 --- a/chapter11/src/main/java/nia/chapter11/HttpsCodecInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/HttpsCodecInitializer.java @@ -11,7 +11,7 @@ import javax.net.ssl.SSLEngine; /** - * Listing 11.5 Using HTTPS + * 代码清单 11-5 使用 HTTPS * * @author Norman Maurer */ @@ -28,11 +28,14 @@ public HttpsCodecInitializer(SslContext context, boolean isClient) { protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); SSLEngine engine = context.newEngine(ch.alloc()); + //将 SslHandler 添加到ChannelPipeline 中以使用 HTTPS pipeline.addFirst("ssl", new SslHandler(engine)); if (isClient) { + //如果是客户端,则添加 HttpClientCodec pipeline.addLast("codec", new HttpClientCodec()); } else { + //如果是服务器,则添加 HttpServerCodec pipeline.addLast("codec", new HttpServerCodec()); } } diff --git a/chapter11/src/main/java/nia/chapter11/IdleStateHandlerInitializer.java b/chapter11/src/main/java/nia/chapter11/IdleStateHandlerInitializer.java index f00b8e05..d168f3d5 100644 --- a/chapter11/src/main/java/nia/chapter11/IdleStateHandlerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/IdleStateHandlerInitializer.java @@ -10,7 +10,7 @@ import java.util.concurrent.TimeUnit; /** - * Listing 11.7 Sending heartbeats + * 代码清单 11-7 发送心跳 * * @author Norman Maurer */ @@ -20,23 +20,29 @@ public class IdleStateHandlerInitializer extends ChannelInitializer protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast( + //(1) IdleStateHandler 将在被触发时发送一个IdleStateEvent 事件 new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS)); + //将一个 HeartbeatHandler 添加到ChannelPipeline中 pipeline.addLast(new HeartbeatHandler()); } + //实现 userEventTriggered() 方法以发送心跳消息 public static final class HeartbeatHandler extends ChannelInboundHandlerAdapter { + //发送到远程节点的心跳消息 private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer( "HEARTBEAT", CharsetUtil.ISO_8859_1)); @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + //(2) 发送心跳消息,并在发送失败时关闭该连接 if (evt instanceof IdleStateEvent) { ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()) .addListener( ChannelFutureListener.CLOSE_ON_FAILURE); } else { + //不是 IdleStateEvent 事件,所以将它传递给下一个 ChannelInboundHandler super.userEventTriggered(ctx, evt); } } diff --git a/chapter11/src/main/java/nia/chapter11/LengthBasedInitializer.java b/chapter11/src/main/java/nia/chapter11/LengthBasedInitializer.java index a1d0fb14..143efda5 100644 --- a/chapter11/src/main/java/nia/chapter11/LengthBasedInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/LengthBasedInitializer.java @@ -5,7 +5,7 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; /** - * Listing 11.10 Decoder for the command and the handler + * 代码清单 11-10 使用 LengthFieldBasedFrameDecoder 解码器基于长度的协议 * * @author Norman Maurer */ @@ -14,7 +14,9 @@ public class LengthBasedInitializer extends ChannelInitializer { protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast( + //使用 LengthFieldBasedFrameDecoder 解码将帧长度编码到帧起始的前 8 个字节中的消息 new LengthFieldBasedFrameDecoder(64 * 1024, 0, 8)); + //添加 FrameHandler 以处理每个帧 pipeline.addLast(new FrameHandler()); } @@ -24,6 +26,7 @@ public static final class FrameHandler public void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { // Do something with the frame + //处理帧的数据 } } } diff --git a/chapter11/src/main/java/nia/chapter11/LineBasedHandlerInitializer.java b/chapter11/src/main/java/nia/chapter11/LineBasedHandlerInitializer.java index 652d020b..e37003aa 100644 --- a/chapter11/src/main/java/nia/chapter11/LineBasedHandlerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/LineBasedHandlerInitializer.java @@ -5,7 +5,7 @@ import io.netty.handler.codec.LineBasedFrameDecoder; /** - * Listing 11.8 Handling line-delimited frames + * 代码清单 11-8 处理由行尾符分隔的帧 * * @author Norman Maurer */ @@ -14,13 +14,16 @@ public class LineBasedHandlerInitializer extends ChannelInitializer @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); + //该 LineBasedFrameDecoder 将提取的帧转发给下一个 ChannelInboundHandler pipeline.addLast(new LineBasedFrameDecoder(64 * 1024)); + //添加 FrameHandler 以接收帧 pipeline.addLast(new FrameHandler()); } public static final class FrameHandler extends SimpleChannelInboundHandler { @Override + //传入了单个帧的内容 public void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { // Do something with the data extracted from the frame diff --git a/chapter11/src/main/java/nia/chapter11/MarshallingInitializer.java b/chapter11/src/main/java/nia/chapter11/MarshallingInitializer.java index c027a629..d7a3c3e5 100644 --- a/chapter11/src/main/java/nia/chapter11/MarshallingInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/MarshallingInitializer.java @@ -9,7 +9,7 @@ import java.io.Serializable; /** - * Listing 11.13 Using JBoss Marshalling + * 代码清单 11-13 使用 JBoss Marshalling * * @author Norman Maurer */ @@ -27,8 +27,11 @@ public MarshallingInitializer( @Override protected void initChannel(Channel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); + //添加 MarshallingDecoder 以将 ByteBuf 转换为 POJO pipeline.addLast(new MarshallingDecoder(unmarshallerProvider)); + //添加 MarshallingEncoder 以将POJO 转换为 ByteBuf pipeline.addLast(new MarshallingEncoder(marshallerProvider)); + //添加 ObjectHandler,以处理普通的实现了Serializable 接口的 POJO pipeline.addLast(new ObjectHandler()); } diff --git a/chapter11/src/main/java/nia/chapter11/ProtoBufInitializer.java b/chapter11/src/main/java/nia/chapter11/ProtoBufInitializer.java index e5b77e06..b7aea095 100644 --- a/chapter11/src/main/java/nia/chapter11/ProtoBufInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/ProtoBufInitializer.java @@ -7,7 +7,7 @@ import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; /** - * Listing 11.14 Using protobuf + * 代码清单 11-14 使用 protobuf * * @author Norman Maurer */ @@ -21,9 +21,13 @@ public ProtoBufInitializer(MessageLite lite) { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); + //添加 ProtobufVarint32FrameDecoder 以分隔帧 pipeline.addLast(new ProtobufVarint32FrameDecoder()); + //添加 ProtobufEncoder 以处理消息的编码 pipeline.addLast(new ProtobufEncoder()); + //添加 ProtobufDecoder 以解码消息 pipeline.addLast(new ProtobufDecoder(lite)); + //添加 ObjectHandler 以处理解码消息 pipeline.addLast(new ObjectHandler()); } diff --git a/chapter11/src/main/java/nia/chapter11/SslChannelInitializer.java b/chapter11/src/main/java/nia/chapter11/SslChannelInitializer.java index 55f95aac..cdfac579 100644 --- a/chapter11/src/main/java/nia/chapter11/SslChannelInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/SslChannelInitializer.java @@ -8,7 +8,7 @@ import javax.net.ssl.SSLEngine; /** - * Listing 11.1 Adding SSL/TLS support + * 代码清单 11-1 添加 SSL/TLS 支持 * * @author Norman Maurer */ @@ -16,14 +16,16 @@ public class SslChannelInitializer extends ChannelInitializer { private final SslContext context; private final boolean startTls; - public SslChannelInitializer(SslContext context, - boolean startTls) { + public SslChannelInitializer(SslContext context, //传入要使用的 SslContext + boolean startTls) { //如果设置为 true,第一个写入的消息将不会被加密(客户端应该设置为 true) this.context = context; this.startTls = startTls; } @Override protected void initChannel(Channel ch) throws Exception { + //对于每个 SslHandler 实例,都使用 Channel 的 ByteBufAllocator 从 SslContext 获取一个新的 SSLEngine SSLEngine engine = context.newEngine(ch.alloc()); + //将 SslHandler 作为第一个 ChannelHandler 添加到 ChannelPipeline 中 ch.pipeline().addFirst("ssl", new SslHandler(engine, startTls)); } diff --git a/chapter11/src/main/java/nia/chapter11/WebSocketServerInitializer.java b/chapter11/src/main/java/nia/chapter11/WebSocketServerInitializer.java index cbced5ef..3bc635ce 100644 --- a/chapter11/src/main/java/nia/chapter11/WebSocketServerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/WebSocketServerInitializer.java @@ -12,7 +12,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; /** - * Listing 11.6 Supporting WebSocket on the server + * 代码清单 11-6 在服务器端支持 WebSocket * * @author Norman Maurer */ @@ -21,10 +21,15 @@ public class WebSocketServerInitializer extends ChannelInitializer { protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast( new HttpServerCodec(), + //为握手提供聚合的 HttpRequest new HttpObjectAggregator(65536), + //如果被请求的端点是"/websocket",则处理该升级握手 new WebSocketServerProtocolHandler("/websocket"), + //TextFrameHandler 处理 TextWebSocketFrame new TextFrameHandler(), + //BinaryFrameHandler 处理 BinaryWebSocketFrame new BinaryFrameHandler(), + //ContinuationFrameHandler 处理 ContinuationWebSocketFrame new ContinuationFrameHandler()); } diff --git a/chapter11/src/main/java/nia/chapter11/package-info.java b/chapter11/src/main/java/nia/chapter11/package-info.java index 1f9c3a47..04ca3330 100644 --- a/chapter11/src/main/java/nia/chapter11/package-info.java +++ b/chapter11/src/main/java/nia/chapter11/package-info.java @@ -1,32 +1,32 @@ /** * Created by kerr. * - * Listing 11.1 Adding SSL/TLS support {@link nia.chapter11.SslChannelInitializer} + * 代码清单 11-1 添加 SSL/TLS 支持 {@link nia.chapter11.SslChannelInitializer} * - * Listing 11.2 Adding support for HTTP {@link nia.chapter11.HttpPipelineInitializer} + * 代码清单 11-2 添加 HTTP 支持 {@link nia.chapter11.HttpPipelineInitializer} * - * Listing 11.3 Automatically aggregating HTTP message fragments {@link nia.chapter11.HttpAggregatorInitializer} + * 代码清单 11-3 自动聚合 HTTP 的消息片段 {@link nia.chapter11.HttpAggregatorInitializer} * - * Listing 11.4 Automatically compressing HTTP messages {@link nia.chapter11.HttpCompressionInitializer} + * 代码清单 11-4 自动压缩 HTTP 消息 {@link nia.chapter11.HttpCompressionInitializer} * - * Listing 11.5 Using HTTPS {@link nia.chapter11.HttpsCodecInitializer} + * 代码清单 11-5 使用 HTTPS {@link nia.chapter11.HttpsCodecInitializer} * - * Listing 11.6 Supporting WebSocket on the server {@link nia.chapter11.WebSocketServerInitializer} + * 代码清单 11-6 在服务器端支持 WebSocket {@link nia.chapter11.WebSocketServerInitializer} * - * Listing 11.7 Sending heartbeats {@link nia.chapter11.IdleStateHandlerInitializer} + * 代码清单 11-7 发送心跳 {@link nia.chapter11.IdleStateHandlerInitializer} * - * Listing 11.8 Handling line-delimited frames {@link nia.chapter11.LineBasedHandlerInitializer} + * 代码清单 11-8 处理由行尾符分隔的帧 {@link nia.chapter11.LineBasedHandlerInitializer} * - * Listing 11.9 Using a ChannelInitializer as a decoder installer {@link nia.chapter11.CmdHandlerInitializer} + * 代码清单 11-9 使用 ChannelInitializer 安装解码器 {@link nia.chapter11.CmdHandlerInitializer} * - * Listing 11.10 Decoder for the command and the handler {@link nia.chapter11.LengthBasedInitializer} + * 代码清单 11-10 使用 LengthFieldBasedFrameDecoder 解码器基于长度的协议 {@link nia.chapter11.LengthBasedInitializer} * - * Listing 11.11 Transferring file contents with FileRegion {@link nia.chapter11.FileRegionWriteHandler} + * 代码清单 11-11 使用 FileRegion 传输文件的内容 {@link nia.chapter11.FileRegionWriteHandler} * - * Listing 11.12 Transferring file contents with ChunkedStream {@link nia.chapter11.ChunkedWriteHandlerInitializer} + * 代码清单 11-12 使用 ChunkedStream 传输文件内容 {@link nia.chapter11.ChunkedWriteHandlerInitializer} * - * Listing 11.13 Using JBoss Marshalling {@link nia.chapter11.MarshallingInitializer} + * 代码清单 11-13 使用 JBoss Marshalling {@link nia.chapter11.MarshallingInitializer} * - * Listing 11.14 Using protobuf {@link nia.chapter11.ProtoBufInitializer} + * 代码清单 11-14 使用 protobuf {@link nia.chapter11.ProtoBufInitializer} */ package nia.chapter11; \ No newline at end of file diff --git a/chapter12/pom.xml b/chapter12/pom.xml index f22a818c..07be1f93 100644 --- a/chapter12/pom.xml +++ b/chapter12/pom.xml @@ -33,7 +33,7 @@ org.codehaus.mojo exec-maven-plugin - 1.2.1 + 1.6.0 java @@ -58,7 +58,7 @@ org.codehaus.mojo exec-maven-plugin - 1.2.1 + 1.6.0 java diff --git a/chapter12/src/main/java/nia/chapter12/ChatServer.java b/chapter12/src/main/java/nia/chapter12/ChatServer.java index 837e5811..c28e4f65 100644 --- a/chapter12/src/main/java/nia/chapter12/ChatServer.java +++ b/chapter12/src/main/java/nia/chapter12/ChatServer.java @@ -14,17 +14,19 @@ import java.net.InetSocketAddress; /** - * Listing 12.4 Bootstrapping the server + * 代码清单 12-4 引导服务器 * * @author Norman Maurer */ public class ChatServer { + //创建 DefaultChannelGroup,其将保存所有已经连接的 WebSocket Channel private final ChannelGroup channelGroup = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE); private final EventLoopGroup group = new NioEventLoopGroup(); private Channel channel; public ChannelFuture start(InetSocketAddress address) { + //引导服务器 ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(group) .channel(NioServerSocketChannel.class) @@ -35,11 +37,13 @@ public ChannelFuture start(InetSocketAddress address) { return future; } + //创建 ChatServerInitializer protected ChannelInitializer createInitializer( ChannelGroup group) { return new ChatServerInitializer(group); } + //处理服务器关闭,并释放所有的资源 public void destroy() { if (channel != null) { channel.close(); diff --git a/chapter12/src/main/java/nia/chapter12/ChatServerInitializer.java b/chapter12/src/main/java/nia/chapter12/ChatServerInitializer.java index 3316a8d8..04dccdb8 100644 --- a/chapter12/src/main/java/nia/chapter12/ChatServerInitializer.java +++ b/chapter12/src/main/java/nia/chapter12/ChatServerInitializer.java @@ -10,10 +10,11 @@ import io.netty.handler.stream.ChunkedWriteHandler; /** - * Listing 12.3 Initializing the ChannelPipeline + * 代码清单 12-3 初始化 ChannelPipeline * * @author Norman Maurer */ +//扩展了 ChannelInitializer public class ChatServerInitializer extends ChannelInitializer { private final ChannelGroup group; @@ -22,6 +23,7 @@ public ChatServerInitializer(ChannelGroup group) { } @Override + //将所有需要的 ChannelHandler 添加到 ChannelPipeline 中 protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); diff --git a/chapter12/src/main/java/nia/chapter12/HttpRequestHandler.java b/chapter12/src/main/java/nia/chapter12/HttpRequestHandler.java index cd67de6f..fd9adab5 100644 --- a/chapter12/src/main/java/nia/chapter12/HttpRequestHandler.java +++ b/chapter12/src/main/java/nia/chapter12/HttpRequestHandler.java @@ -11,10 +11,11 @@ import java.net.URL; /** - * Listing 12.1 HTTPRequestHandler + * 代码清单 12-1 HTTPRequestHandler * * @author Norman Maurer */ +//扩展 SimpleChannelInboundHandler 以处理 FullHttpRequest 消息 public class HttpRequestHandler extends SimpleChannelInboundHandler { private final String wsUri; private static final File INDEX; @@ -40,12 +41,15 @@ public HttpRequestHandler(String wsUri) { @Override public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { + //(1) 如果请求了 WebSocket 协议升级,则增加引用计数(调用 retain()方法),并将它传递给下一 个 ChannelInboundHandler if (wsUri.equalsIgnoreCase(request.getUri())) { ctx.fireChannelRead(request.retain()); } else { + //(2) 处理 100 Continue 请求以符合 HTTP 1.1 规范 if (HttpHeaders.is100ContinueExpected(request)) { send100Continue(ctx); } + //读取 index.html RandomAccessFile file = new RandomAccessFile(INDEX, "r"); HttpResponse response = new DefaultHttpResponse( request.getProtocolVersion(), HttpResponseStatus.OK); @@ -53,21 +57,26 @@ public void channelRead0(ChannelHandlerContext ctx, HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=UTF-8"); boolean keepAlive = HttpHeaders.isKeepAlive(request); + //如果请求了keep-alive,则添加所需要的 HTTP 头信息 if (keepAlive) { response.headers().set( HttpHeaders.Names.CONTENT_LENGTH, file.length()); response.headers().set( HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); } + //(3) 将 HttpResponse 写到客户端 ctx.write(response); + //(4) 将 index.html 写到客户端 if (ctx.pipeline().get(SslHandler.class) == null) { ctx.write(new DefaultFileRegion( file.getChannel(), 0, file.length())); } else { ctx.write(new ChunkedNioFile(file.getChannel())); } + //(5) 写 LastHttpContent 并冲刷至客户端 ChannelFuture future = ctx.writeAndFlush( LastHttpContent.EMPTY_LAST_CONTENT); + //(6) 如果没有请求keep-alive,则在写操作完成后关闭 Channel if (!keepAlive) { future.addListener(ChannelFutureListener.CLOSE); } diff --git a/chapter12/src/main/java/nia/chapter12/SecureChatServer.java b/chapter12/src/main/java/nia/chapter12/SecureChatServer.java index a7779ede..0cd8f3a7 100644 --- a/chapter12/src/main/java/nia/chapter12/SecureChatServer.java +++ b/chapter12/src/main/java/nia/chapter12/SecureChatServer.java @@ -10,10 +10,11 @@ import java.net.InetSocketAddress; /** - * Listing 12.7 Adding encryption to the ChatServer + * 代码清单 12-7 向 ChatServer 添加加密 * * @author Norman Maurer */ +//SecureChatServer 扩展 ChatServer 以支持加密 public class SecureChatServer extends ChatServer { private final SslContext context; @@ -24,6 +25,7 @@ public SecureChatServer(SslContext context) { @Override protected ChannelInitializer createInitializer( ChannelGroup group) { + //返回之前创建的 SecureChatServerInitializer 以启用加密 return new SecureChatServerInitializer(group, context); } diff --git a/chapter12/src/main/java/nia/chapter12/SecureChatServerInitializer.java b/chapter12/src/main/java/nia/chapter12/SecureChatServerInitializer.java index a4b669ac..f2aeb059 100644 --- a/chapter12/src/main/java/nia/chapter12/SecureChatServerInitializer.java +++ b/chapter12/src/main/java/nia/chapter12/SecureChatServerInitializer.java @@ -8,10 +8,11 @@ import javax.net.ssl.SSLEngine; /** - * Listing 12.6 Adding encryption to the ChannelPipeline + * 代码清单 12-6 为 ChannelPipeline 添加加密 * * @author Norman Maurer */ +//扩展 ChatServerInitializer 以添加加密 public class SecureChatServerInitializer extends ChatServerInitializer { private final SslContext context; @@ -23,9 +24,11 @@ public SecureChatServerInitializer(ChannelGroup group, @Override protected void initChannel(Channel ch) throws Exception { + //调用父类的 initChannel() 方法 super.initChannel(ch); SSLEngine engine = context.newEngine(ch.alloc()); engine.setUseClientMode(false); + //将 SslHandler 添加到 ChannelPipeline 中 ch.pipeline().addFirst(new SslHandler(engine)); } } diff --git a/chapter12/src/main/java/nia/chapter12/TextWebSocketFrameHandler.java b/chapter12/src/main/java/nia/chapter12/TextWebSocketFrameHandler.java index e781e9c6..5898c6df 100644 --- a/chapter12/src/main/java/nia/chapter12/TextWebSocketFrameHandler.java +++ b/chapter12/src/main/java/nia/chapter12/TextWebSocketFrameHandler.java @@ -7,10 +7,11 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; /** - * Listing 12.2 Handling text frames + * 代码清单 12-2 处理文本帧 * * @author Norman Maurer */ +//扩展 SimpleChannelInboundHandler,并处理 TextWebSocketFrame 消息 public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler { private final ChannelGroup group; @@ -19,14 +20,18 @@ public TextWebSocketFrameHandler(ChannelGroup group) { this.group = group; } + //重写 userEventTriggered()方法以处理自定义事件 @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + //如果该事件表示握手成功,则从该 ChannelPipeline 中移除HttpRequest-Handler,因为将不会接收到任何HTTP消息了 if (evt == WebSocketServerProtocolHandler .ServerHandshakeStateEvent.HANDSHAKE_COMPLETE) { ctx.pipeline().remove(HttpRequestHandler.class); + //(1) 通知所有已经连接的 WebSocket 客户端新的客户端已经连接上了 group.writeAndFlush(new TextWebSocketFrame( "Client " + ctx.channel() + " joined")); + //(2) 将新的 WebSocket Channel 添加到 ChannelGroup 中,以便它可以接收到所有的消息 group.add(ctx.channel()); } else { super.userEventTriggered(ctx, evt); @@ -36,6 +41,7 @@ public void userEventTriggered(ChannelHandlerContext ctx, @Override public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { + //(3) 增加消息的引用计数,并将它写到 ChannelGroup 中所有已经连接的客户端 group.writeAndFlush(msg.retain()); } } diff --git a/chapter12/src/main/java/nia/chapter12/package-info.java b/chapter12/src/main/java/nia/chapter12/package-info.java index 439b8f09..dae65b22 100644 --- a/chapter12/src/main/java/nia/chapter12/package-info.java +++ b/chapter12/src/main/java/nia/chapter12/package-info.java @@ -2,16 +2,16 @@ * Created by kerr. * * - * Listing 12.1 HTTPRequestHandler {@link nia.chapter12.HttpRequestHandler} + * 代码清单 12-1 HTTPRequestHandler {@link nia.chapter12.HttpRequestHandler} * - * Listing 12.2 Handling text frames {@link nia.chapter12.TextWebSocketFrameHandler} + * 代码清单 12-2 处理文本帧 {@link nia.chapter12.TextWebSocketFrameHandler} * - * Listing 12.3 Initializing the ChannelPipeline {@link nia.chapter12.ChatServerInitializer} + * 代码清单 12-3 初始化 ChannelPipeline {@link nia.chapter12.ChatServerInitializer} * - * Listing 12.4 Bootstrapping the server {@link nia.chapter12.ChatServer} + * 代码清单 12-4 引导服务器 {@link nia.chapter12.ChatServer} * - * Listing 12.6 Adding encryption to the ChannelPipeline {@link nia.chapter12.SecureChatServerInitializer} + * 代码清单 12-6 为 ChannelPipeline 添加加密 {@link nia.chapter12.SecureChatServerInitializer} * - * Listing 12.7 Adding encryption to the ChatServer {@link nia.chapter12.SecureChatServer} + * 代码清单 12-7 向 ChatServer 添加加密 {@link nia.chapter12.SecureChatServer} */ package nia.chapter12; \ No newline at end of file diff --git a/chapter12/src/main/resources/index.html b/chapter12/src/main/resources/index.html index 65ad646b..805764a3 100644 --- a/chapter12/src/main/resources/index.html +++ b/chapter12/src/main/resources/index.html @@ -16,7 +16,7 @@ height: 15em; } - +