diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..9d922a1c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,24 @@ +version: 2 +updates: +- package-ecosystem: maven + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + ignore: + - dependency-name: com.google.protobuf:protobuf-java + versions: + - 3.14.0 + - 3.15.0 + - 3.15.1 + - 3.15.2 + - 3.15.3 + - 3.15.4 + - 3.15.5 + - 3.15.6 + - 3.15.7 + - dependency-name: io.netty:netty-all + versions: + - 4.1.59.Final + - 4.1.60.Final + - 4.1.62.Final diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..94249a90 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: java + +sudo: false + +jdk: + - oraclejdk8 + +before_cache: + - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + +cache: + directories: + - $HOME/.gradle/caches/ + - $HOME/.gradle/wrapper/ + +script: + # need to override as the default is to test + - mvn test -B \ No newline at end of file diff --git a/README.md b/README.md index 6cf98462..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.2.3 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 1be636a5..bd2d002f 100644 --- a/chapter1/src/main/java/nia/chapter1/BlockingIoExample.java +++ b/chapter1/src/main/java/nia/chapter1/BlockingIoExample.java @@ -10,24 +10,35 @@ /** * Created by kerr. * - * Listing 1.1 Blocking I/O example + * 代码清单 1-1 阻塞 I/O 示例 */ public class BlockingIoExample { + + /** + * 代码清单 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); + //继续执行处理循环 } - response = processRequest(request); - out.println(response); } private String processRequest(String request){ diff --git a/chapter1/src/main/java/nia/chapter1/ConnectExample.java b/chapter1/src/main/java/nia/chapter1/ConnectExample.java index 8f41b7cc..9108c7c3 100644 --- a/chapter1/src/main/java/nia/chapter1/ConnectExample.java +++ b/chapter1/src/main/java/nia/chapter1/ConnectExample.java @@ -5,6 +5,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; +import io.netty.channel.socket.nio.NioSocketChannel; import java.net.InetSocketAddress; import java.nio.charset.Charset; @@ -12,30 +13,43 @@ /** * Created by kerr. * - * Listing 1.3 Asynchronous connect - * Listing 1.4 Callback in action + * 代码清单 1-3 异步地建立连接 + * + * 代码清单 1-4 回调实战 */ public class ConnectExample { + private static final Channel CHANNEL_FROM_SOMEWHERE = new NioSocketChannel(); - public static void connect(Channel channel) { + /** + * 代码清单 1-3 异步地建立连接 + * + * 代码清单 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 d16877da..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(io.netty.channel.Channel)} + * 代码清单 1-3 异步地建立连接 {@link nia.chapter1.ConnectExample#connect()} * - * Listing 1.4 Callback in action {@link nia.chapter1.ConnectExample#connect(io.netty.channel.Channel)} + * 代码清单 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 46a261b1..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 */ -public class CharToByteEncoder - extends MessageToByteEncoder { +//扩展了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 1921250e..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 */ -public class IntegerToStringDecoder - extends MessageToMessageDecoder { +//扩展了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 55c5b94a..58afa464 100644 --- a/chapter10/src/main/java/nia/chapter10/IntegerToStringEncoder.java +++ b/chapter10/src/main/java/nia/chapter10/IntegerToStringEncoder.java @@ -6,16 +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 d5738ed7..04ffe513 100644 --- a/chapter10/src/main/java/nia/chapter10/SafeByteToMessageDecoder.java +++ b/chapter10/src/main/java/nia/chapter10/SafeByteToMessageDecoder.java @@ -8,19 +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 f03d28ae..53fba55b 100644 --- a/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder2.java +++ b/chapter10/src/main/java/nia/chapter10/ToIntegerDecoder2.java @@ -7,16 +7,17 @@ import java.util.List; /** - * Listing 10.2 Class ToIntegerDecoder2 extends ReplayingDecoder + * 代码清单 10-2 ToIntegerDecoder2 类扩展了 ReplayingDecoder * * @author Norman Maurer */ -public class ToIntegerDecoder2 - extends ReplayingDecoder { +//扩展 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 7579700b..7bfba048 100644 --- a/chapter10/src/main/java/nia/chapter10/WebSocketConvertHandler.java +++ b/chapter10/src/main/java/nia/chapter10/WebSocketConvertHandler.java @@ -9,45 +9,47 @@ import java.util.List; /** - * Listing 10.7 Using MessageToMessageCodec + * 代码清单 10-7 使用 MessageToMessageCodec * * @author Norman Maurer */ @Sharable public class WebSocketConvertHandler extends - MessageToMessageCodec { - @Override - protected void encode(ChannelHandlerContext ctx, - WebSocketConvertHandler.MyWebSocketFrame msg, - List out) throws Exception { - ByteBuf payload = msg.getData().duplicate().retain(); - switch (msg.getType()) { - case BINARY: - out.add(new BinaryWebSocketFrame(payload)); - break; - case TEXT: - out.add(new TextWebSocketFrame(payload)); - break; - case CLOSE: - out.add(new CloseWebSocketFrame(true, 0, payload)); - break; - case CONTINUATION: - out.add(new ContinuationWebSocketFrame(payload)); - break; - case PONG: - out.add(new PongWebSocketFrame(payload)); - break; - case PING: - out.add(new PingWebSocketFrame(payload)); - break; - default: - throw new IllegalStateException( - "Unsupported websocket msg " + msg); - } + 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)); + break; + case TEXT: + out.add(new TextWebSocketFrame(payload)); + break; + case CLOSE: + out.add(new CloseWebSocketFrame(true, 0, payload)); + break; + case CONTINUATION: + out.add(new ContinuationWebSocketFrame(payload)); + break; + case PONG: + out.add(new PongWebSocketFrame(payload)); + break; + case PING: + out.add(new PingWebSocketFrame(payload)); + break; + default: + throw new IllegalStateException( + "Unsupported websocket msg " + msg);} } @Override + //将 WebSocketFrame 解码为 MyWebSocketFrame,并设置 FrameType protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, List out) throws Exception { ByteBuf payload = msg.content().duplicate().retain(); @@ -81,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 fb45e03a..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.IntegerToStringEncoder} + * 代码清单 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..fd909b55 100644 --- a/chapter11/pom.xml +++ b/chapter11/pom.xml @@ -12,7 +12,7 @@ chapter11 Chapter 11. Provided ChannelHandlers and codecs - 2.5.0 + 3.17.3 diff --git a/chapter11/src/main/java/nia/chapter11/ChunkedWriteHandlerInitializer.java b/chapter11/src/main/java/nia/chapter11/ChunkedWriteHandlerInitializer.java index d4ebf0a5..9df741a6 100644 --- a/chapter11/src/main/java/nia/chapter11/ChunkedWriteHandlerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/ChunkedWriteHandlerInitializer.java @@ -10,33 +10,40 @@ import java.io.FileInputStream; /** - * Listing 11.12 of Netty in Action + * 代码清单 11-12 使用 ChunkedStream 传输文件内容 * * @author Norman Maurer */ public class ChunkedWriteHandlerInitializer - extends ChannelInitializer { + extends ChannelInitializer { private final File file; private final SslContext sslCtx; public ChunkedWriteHandlerInitializer(File file, SslContext sslCtx) { this.file = file; this.sslCtx = 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()); } + public final class WriteStreamHandler - extends ChannelInboundHandlerAdapter { + extends ChannelInboundHandlerAdapter { + @Override + //当连接建立时,channelActive() 方法将使用 ChunkedInput 写文件数据 public void channelActive(ChannelHandlerContext ctx) - throws Exception { + throws Exception { super.channelActive(ctx); ctx.writeAndFlush( - new ChunkedStream(new FileInputStream(file))); + new ChunkedStream(new FileInputStream(file))); } } } diff --git a/chapter11/src/main/java/nia/chapter11/CmdHandlerInitializer.java b/chapter11/src/main/java/nia/chapter11/CmdHandlerInitializer.java index 470df607..bd4cca08 100644 --- a/chapter11/src/main/java/nia/chapter11/CmdHandlerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/CmdHandlerInitializer.java @@ -5,20 +5,22 @@ import io.netty.handler.codec.LineBasedFrameDecoder; /** - * Listing 11.9 Using a ChannelInitializer as a decoder installer + * 代码清单 11-9 使用 ChannelInitializer 安装解码器 * * @author Norman Maurer */ public class CmdHandlerInitializer extends ChannelInitializer { private static final byte SPACE = (byte)' '; - @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; @@ -41,15 +43,21 @@ public static final class CmdDecoder extends LineBasedFrameDecoder { public CmdDecoder(int maxLength) { super(maxLength); } + @Override protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) - throws Exception { + 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 b84bc62a..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(); @@ -17,22 +17,26 @@ public class FileRegionWriteHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { - File file = FILE_FROM_SOMEWHERE; - Channel channel = CHANNEL_FROM_SOMEWHERE; + 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 - } - } - }); + 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 75d5a06b..f86a5bd5 100644 --- a/chapter11/src/main/java/nia/chapter11/HttpAggregatorInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/HttpAggregatorInitializer.java @@ -8,23 +8,28 @@ import io.netty.handler.codec.http.HttpServerCodec; /** - * Listing 11.3 Automatically aggregating HTTP message fragments + * 代码清单 11-3 自动聚合 HTTP 的消息片段 * * @author Norman Maurer */ public class HttpAggregatorInitializer extends ChannelInitializer { private final boolean isClient; + public HttpAggregatorInitializer(boolean isClient) { this.isClient = isClient; } + @Override 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 0c616cbc..60182023 100644 --- a/chapter11/src/main/java/nia/chapter11/HttpCompressionInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/HttpCompressionInitializer.java @@ -9,26 +9,32 @@ import io.netty.handler.codec.http.HttpServerCodec; /** - * Listing 11.4 Automatically compressing HTTP messages + * 代码清单 11-4 自动压缩 HTTP 消息 * * @author Norman Maurer */ public class HttpCompressionInitializer extends ChannelInitializer { private final boolean isClient; + public HttpCompressionInitializer(boolean isClient) { this.isClient = isClient; } + @Override 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()); + new HttpContentDecompressor()); } else { + //如果是服务器,则添加 HttpServerCodec pipeline.addLast("codec", new HttpServerCodec()); + //如果是服务器,则添加HttpContentCompressor 来压缩数据(如果客户端支持它) pipeline.addLast("compressor", - new HttpContentCompressor()); + 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 185bb65a..6c2aee0a 100644 --- a/chapter11/src/main/java/nia/chapter11/HttpsCodecInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/HttpsCodecInitializer.java @@ -11,25 +11,31 @@ import javax.net.ssl.SSLEngine; /** - * Listing 11.5 Using HTTPS + * 代码清单 11-5 使用 HTTPS * * @author Norman Maurer */ public class HttpsCodecInitializer extends ChannelInitializer { private final SslContext context; private final boolean isClient; + public HttpsCodecInitializer(SslContext context, boolean isClient) { this.context = context; this.isClient = isClient; } + @Override 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 a9861644..d168f3d5 100644 --- a/chapter11/src/main/java/nia/chapter11/IdleStateHandlerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/IdleStateHandlerInitializer.java @@ -10,32 +10,39 @@ import java.util.concurrent.TimeUnit; /** - * Listing 11.7 Sending heartbeats + * 代码清单 11-7 发送心跳 * * @author Norman Maurer */ -public class IdleStateHandlerInitializer extends ChannelInitializer { +public class IdleStateHandlerInitializer extends ChannelInitializer + { @Override 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 { + extends ChannelInboundHandlerAdapter { + //发送到远程节点的心跳消息 private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer( - "HEARTBEAT", CharsetUtil.ISO_8859_1)); + "HEARTBEAT", CharsetUtil.ISO_8859_1)); @Override public void userEventTriggered(ChannelHandlerContext ctx, - Object evt) throws Exception { + Object evt) throws Exception { + //(2) 发送心跳消息,并在发送失败时关闭该连接 if (evt instanceof IdleStateEvent) { ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()) - .addListener( - ChannelFutureListener.CLOSE_ON_FAILURE); + .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 8e1088a7..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,15 +14,19 @@ 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()); } + public static final class FrameHandler - extends SimpleChannelInboundHandler { + extends SimpleChannelInboundHandler { @Override public void channelRead0(ChannelHandlerContext ctx, - ByteBuf msg) throws Exception { + 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 ae44ee48..e37003aa 100644 --- a/chapter11/src/main/java/nia/chapter11/LineBasedHandlerInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/LineBasedHandlerInitializer.java @@ -5,25 +5,27 @@ import io.netty.handler.codec.LineBasedFrameDecoder; /** - * Listing 11.8 Handling line-delimited frames + * 代码清单 11-8 处理由行尾符分隔的帧 * * @author Norman Maurer */ 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 { + extends SimpleChannelInboundHandler { @Override + //传入了单个帧的内容 public void channelRead0(ChannelHandlerContext ctx, - ByteBuf msg) throws Exception { + 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 8641f090..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,17 +27,20 @@ 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()); } public static final class ObjectHandler - extends SimpleChannelInboundHandler { + extends SimpleChannelInboundHandler { @Override public void channelRead0( - ChannelHandlerContext channelHandlerContext, - Serializable serializable) throws Exception { + ChannelHandlerContext channelHandlerContext, + Serializable serializable) throws Exception { // Do something } } diff --git a/chapter11/src/main/java/nia/chapter11/ProtoBufInitializer.java b/chapter11/src/main/java/nia/chapter11/ProtoBufInitializer.java index 6a2745b6..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,17 +21,21 @@ 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()); } public static final class ObjectHandler - extends SimpleChannelInboundHandler { + extends SimpleChannelInboundHandler { @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) - throws Exception { + throws Exception { // Do something with the object } } diff --git a/chapter11/src/main/java/nia/chapter11/SslChannelInitializer.java b/chapter11/src/main/java/nia/chapter11/SslChannelInitializer.java index 2f5dd0dd..cdfac579 100644 --- a/chapter11/src/main/java/nia/chapter11/SslChannelInitializer.java +++ b/chapter11/src/main/java/nia/chapter11/SslChannelInitializer.java @@ -8,22 +8,25 @@ import javax.net.ssl.SSLEngine; /** - * Listing 11.1 Adding SSL/TLS support + * 代码清单 11-1 添加 SSL/TLS 支持 * * @author Norman Maurer */ 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)); + 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 a9698749..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 */ @@ -20,37 +20,42 @@ public class WebSocketServerInitializer extends ChannelInitializer { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast( - new HttpServerCodec(), - new HttpObjectAggregator(65536), - new WebSocketServerProtocolHandler("/websocket"), - new TextFrameHandler(), - new BinaryFrameHandler(), - new ContinuationFrameHandler()); + new HttpServerCodec(), + //为握手提供聚合的 HttpRequest + new HttpObjectAggregator(65536), + //如果被请求的端点是"/websocket",则处理该升级握手 + new WebSocketServerProtocolHandler("/websocket"), + //TextFrameHandler 处理 TextWebSocketFrame + new TextFrameHandler(), + //BinaryFrameHandler 处理 BinaryWebSocketFrame + new BinaryFrameHandler(), + //ContinuationFrameHandler 处理 ContinuationWebSocketFrame + new ContinuationFrameHandler()); } public static final class TextFrameHandler extends - SimpleChannelInboundHandler { + SimpleChannelInboundHandler { @Override public void channelRead0(ChannelHandlerContext ctx, - TextWebSocketFrame msg) throws Exception { + TextWebSocketFrame msg) throws Exception { // Handle text frame } } public static final class BinaryFrameHandler extends - SimpleChannelInboundHandler { + SimpleChannelInboundHandler { @Override public void channelRead0(ChannelHandlerContext ctx, - BinaryWebSocketFrame msg) throws Exception { + BinaryWebSocketFrame msg) throws Exception { // Handle binary frame } } public static final class ContinuationFrameHandler extends - SimpleChannelInboundHandler { + SimpleChannelInboundHandler { @Override public void channelRead0(ChannelHandlerContext ctx, - ContinuationWebSocketFrame msg) throws Exception { + ContinuationWebSocketFrame msg) throws Exception { // Handle continuation frame } } diff --git a/chapter11/src/main/java/nia/chapter11/package-info.java b/chapter11/src/main/java/nia/chapter11/package-info.java index a1666bd4..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.LengthBasedInitializer} + * 代码清单 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 7e3cff17..c28e4f65 100644 --- a/chapter12/src/main/java/nia/chapter12/ChatServer.java +++ b/chapter12/src/main/java/nia/chapter12/ChatServer.java @@ -14,32 +14,36 @@ 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); + 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) - .childHandler(createInitializer(channelGroup)); + .channel(NioServerSocketChannel.class) + .childHandler(createInitializer(channelGroup)); ChannelFuture future = bootstrap.bind(address); future.syncUninterruptibly(); channel = future.channel(); return future; } + //创建 ChatServerInitializer protected ChannelInitializer createInitializer( - ChannelGroup group) { + 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 0b429e30..04dccdb8 100644 --- a/chapter12/src/main/java/nia/chapter12/ChatServerInitializer.java +++ b/chapter12/src/main/java/nia/chapter12/ChatServerInitializer.java @@ -10,12 +10,12 @@ import io.netty.handler.stream.ChunkedWriteHandler; /** - * Listing 12.3 Initializing the ChannelPipeline + * 代码清单 12-3 初始化 ChannelPipeline * * @author Norman Maurer */ -public class ChatServerInitializer - extends ChannelInitializer { +//扩展了 ChannelInitializer +public class ChatServerInitializer extends ChannelInitializer { private final ChannelGroup group; public ChatServerInitializer(ChannelGroup group) { @@ -23,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 eb5803bf..fd9adab5 100644 --- a/chapter12/src/main/java/nia/chapter12/HttpRequestHandler.java +++ b/chapter12/src/main/java/nia/chapter12/HttpRequestHandler.java @@ -11,26 +11,26 @@ import java.net.URL; /** - * Listing 12.1 HTTPRequestHandler + * 代码清单 12-1 HTTPRequestHandler * * @author Norman Maurer */ -public class HttpRequestHandler - extends SimpleChannelInboundHandler { +//扩展 SimpleChannelInboundHandler 以处理 FullHttpRequest 消息 +public class HttpRequestHandler extends SimpleChannelInboundHandler { private final String wsUri; private static final File INDEX; static { URL location = HttpRequestHandler.class - .getProtectionDomain() - .getCodeSource().getLocation(); + .getProtectionDomain() + .getCodeSource().getLocation(); try { String path = location.toURI() + "index.html"; path = !path.contains("file:") ? path : path.substring(5); INDEX = new File(path); } catch (URISyntaxException e) { throw new IllegalStateException( - "Unable to locate index.html", e); + "Unable to locate index.html", e); } } @@ -40,35 +40,43 @@ public HttpRequestHandler(String wsUri) { @Override public void channelRead0(ChannelHandlerContext ctx, - FullHttpRequest request) throws Exception { + 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); + request.getProtocolVersion(), HttpResponseStatus.OK); response.headers().set( - HttpHeaders.Names.CONTENT_TYPE, - "text/plain; charset=UTF-8"); + 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()); + HttpHeaders.Names.CONTENT_LENGTH, file.length()); response.headers().set( HttpHeaders.Names.CONNECTION, - HttpHeaders.Values.KEEP_ALIVE); + 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())); + file.getChannel(), 0, file.length())); } else { ctx.write(new ChunkedNioFile(file.getChannel())); } + //(5) 写 LastHttpContent 并冲刷至客户端 ChannelFuture future = ctx.writeAndFlush( - LastHttpContent.EMPTY_LAST_CONTENT); + LastHttpContent.EMPTY_LAST_CONTENT); + //(6) 如果没有请求keep-alive,则在写操作完成后关闭 Channel if (!keepAlive) { future.addListener(ChannelFutureListener.CLOSE); } @@ -77,12 +85,12 @@ public void channelRead0(ChannelHandlerContext ctx, private static void send100Continue(ChannelHandlerContext ctx) { FullHttpResponse response = new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE); + HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE); ctx.writeAndFlush(response); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) - throws Exception { + throws Exception { cause.printStackTrace(); ctx.close(); } diff --git a/chapter12/src/main/java/nia/chapter12/SecureChatServer.java b/chapter12/src/main/java/nia/chapter12/SecureChatServer.java index b2f4ee61..0cd8f3a7 100644 --- a/chapter12/src/main/java/nia/chapter12/SecureChatServer.java +++ b/chapter12/src/main/java/nia/chapter12/SecureChatServer.java @@ -10,20 +10,25 @@ import java.net.InetSocketAddress; /** - * Listing 12.7 of Netty in Action + * 代码清单 12-7 向 ChatServer 添加加密 * * @author Norman Maurer */ +//SecureChatServer 扩展 ChatServer 以支持加密 public class SecureChatServer extends ChatServer { private final SslContext context; + public SecureChatServer(SslContext context) { this.context = context; } + @Override protected ChannelInitializer createInitializer( - ChannelGroup group) { + ChannelGroup group) { + //返回之前创建的 SecureChatServerInitializer 以启用加密 return new SecureChatServerInitializer(group, context); } + public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("Please give port as argument"); diff --git a/chapter12/src/main/java/nia/chapter12/SecureChatServerInitializer.java b/chapter12/src/main/java/nia/chapter12/SecureChatServerInitializer.java index 9fcaad36..f2aeb059 100644 --- a/chapter12/src/main/java/nia/chapter12/SecureChatServerInitializer.java +++ b/chapter12/src/main/java/nia/chapter12/SecureChatServerInitializer.java @@ -8,23 +8,27 @@ 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; - public SecureChatServerInitializer(ChannelGroup group, SslContext context) { + public SecureChatServerInitializer(ChannelGroup group, + SslContext context) { super(group); this.context = context; } @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 da12cfb0..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,22 +20,28 @@ public TextWebSocketFrameHandler(ChannelGroup group) { this.group = group; } + //重写 userEventTriggered()方法以处理自定义事件 @Override public void userEventTriggered(ChannelHandlerContext ctx, - Object evt) throws Exception { + Object evt) throws Exception { + //如果该事件表示握手成功,则从该 ChannelPipeline 中移除HttpRequest-Handler,因为将不会接收到任何HTTP消息了 if (evt == WebSocketServerProtocolHandler - .ServerHandshakeStateEvent.HANDSHAKE_COMPLETE) { + .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); } } + @Override public void channelRead0(ChannelHandlerContext ctx, - TextWebSocketFrame msg) throws Exception { + 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; } - +