├── .gitignore
├── .scalafix.conf
├── LICENSE
├── README.md
├── build.sbt
├── project
├── Formatting.scala
├── build.properties
└── plugins.sbt
└── src
├── main
├── java
│ └── nia
│ │ └── chapter1
│ │ └── javadsl
│ │ ├── BlockingIoExample.java
│ │ ├── ConnectExample.java
│ │ ├── ConnectHandler.java
│ │ └── package-info.java
└── scala
│ ├── io
│ └── netty
│ │ └── channel
│ │ ├── DummyChannelHandlerContext.scala
│ │ └── DummyChannelPipeline.scala
│ └── nia
│ ├── chapter1
│ └── scaladsl
│ │ └── BlockingIoExample.scala
│ ├── chapter13
│ ├── LogEvent.scala
│ ├── LogEventBroadcaster.scala
│ ├── LogEventDecoder.scala
│ ├── LogEventEncoder.scala
│ ├── LogEventHandler.scala
│ └── LogEventMonitor.scala
│ ├── chapter2
│ ├── echoclient
│ │ ├── EchoClient.scala
│ │ └── EchoClientHandler.scala
│ └── echoserver
│ │ ├── EchoServer.scala
│ │ └── EchoServerHandler.scala
│ ├── chapter4
│ ├── ChannelOperationExamples.scala
│ ├── NettyNioServer.scala
│ ├── NettyOioServer.scala
│ ├── PlainNioServer.scala
│ └── PlainOioServer.scala
│ ├── chapter5
│ └── ByteBufExamples.scala
│ ├── chapter6
│ ├── ChannelFutures.scala
│ ├── DiscardHandler.scala
│ ├── DiscardInboundHandler.scala
│ ├── DiscardOutboundHandler.scala
│ ├── InboundExceptionHandler.scala
│ ├── ModifyChannelPipeline.scala
│ ├── OutboundExceptionHandler.scala
│ ├── SharableHandler.scala
│ ├── SimpleDiscardHandler.scala
│ ├── UnsharableHandler.scala
│ ├── WriteHandler.scala
│ └── WriteHandlers.scala
│ ├── chapter7
│ ├── EventLoopExamples.scala
│ └── ScheduleExamples.scala
│ └── chapter8
│ ├── BootstrapClient.scala
│ ├── BootstrapClientWithOptionsAndAttrs.scala
│ ├── BootstrapDatagramChannel.scala
│ ├── BootstrapServer.scala
│ ├── BootstrapSharingEventLoopGroup.scala
│ ├── BootstrapWithInitializer.scala
│ ├── GracefulShutdown.scala
│ └── InvalidBootstrapClient.scala
└── paradox
├── chapter-01
└── index.md
├── contents.md
├── errata
└── index.md
└── index.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### JetBrains template
3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5 |
6 | # User-specific stuff:
7 | .idea/**/workspace.xml
8 | .idea/**/tasks.xml
9 | .idea/dictionaries
10 |
11 | # Sensitive or high-churn files:
12 | .idea/**/dataSources/
13 | .idea/**/dataSources.ids
14 | .idea/**/dataSources.xml
15 | .idea/**/dataSources.local.xml
16 | .idea/**/sqlDataSources.xml
17 | .idea/**/dynamic.xml
18 | .idea/**/uiDesigner.xml
19 |
20 | # Gradle:
21 | .idea/**/gradle.xml
22 | .idea/**/libraries
23 |
24 | # CMake
25 | cmake-build-debug/
26 | cmake-build-release/
27 |
28 | # Mongo Explorer plugin:
29 | .idea/**/mongoSettings.xml
30 |
31 | ## File-based project format:
32 | *.iws
33 |
34 | ## Plugin-specific files:
35 |
36 | # IntelliJ
37 | out/
38 |
39 | # mpeltonen/sbt-idea plugin
40 | .idea_modules/
41 |
42 | # JIRA plugin
43 | atlassian-ide-plugin.xml
44 |
45 | # Cursive Clojure plugin
46 | .idea/replstate.xml
47 |
48 | # Crashlytics plugin (for Android Studio and IntelliJ)
49 | com_crashlytics_export_strings.xml
50 | crashlytics.properties
51 | crashlytics-build.properties
52 | fabric.properties
53 | ### Scala template
54 | *.class
55 | *.log
56 | ### SBT template
57 | # Simple Build Tool
58 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
59 |
60 | dist/*
61 | target/
62 | lib_managed/
63 | src_managed/
64 | project/boot/
65 | project/plugins/project/
66 | .history
67 | .cache
68 | .lib/
69 | ### Eclipse template
70 |
71 | .metadata
72 | bin/
73 | tmp/
74 | *.tmp
75 | *.bak
76 | *.swp
77 | *~.nib
78 | local.properties
79 | .settings/
80 | .loadpath
81 | .recommenders
82 |
83 | # External tool builders
84 | .externalToolBuilders/
85 |
86 | # Locally stored "Eclipse launch configurations"
87 | *.launch
88 |
89 | # PyDev specific (Python IDE for Eclipse)
90 | *.pydevproject
91 |
92 | # CDT-specific (C/C++ Development Tooling)
93 | .cproject
94 |
95 | # CDT- autotools
96 | .autotools
97 |
98 | # Java annotation processor (APT)
99 | .factorypath
100 |
101 | # PDT-specific (PHP Development Tools)
102 | .buildpath
103 |
104 | # sbteclipse plugin
105 | .target
106 |
107 | # Tern plugin
108 | .tern-project
109 |
110 | # TeXlipse plugin
111 | .texlipse
112 |
113 | # STS (Spring Tool Suite)
114 | .springBeans
115 |
116 | # Code Recommenders
117 | .recommenders/
118 |
119 | # Scala IDE specific (Scala & Java development for Eclipse)
120 | .cache-main
121 | .scala_dependencies
122 | .worksheet
123 | /.idea/
--------------------------------------------------------------------------------
/.scalafix.conf:
--------------------------------------------------------------------------------
1 | // Built in rules
2 | rules = [
3 | ExplicitResultTypes
4 | RemoveUnusedImports
5 | // RemoveXmlLiterals
6 | ProcedureSyntax
7 | DottyVolatileLazyVal
8 | ExplicitUnit
9 | // DottyVarArgPattern
10 | NoAutoTupling
11 | NoValInForComprehension
12 | Sbt1
13 | // NoInfer
14 | Disable
15 | ]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # netty-in-action-scala
2 | 《Netty实战》源代码——Scala版
3 |
4 | ## Random Notes
5 | ### Chapter 13
6 | ``` bash
7 | $ sbt "runMain nia.chapter13.LogEventBroadcaster 8888 /var/log/system.log"
8 | $ sbt "runMain nia.chapter13.LogEventMonitor 8888"
9 | ```
10 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | import com.typesafe.sbt.SbtGit.GitKeys
2 | import com.typesafe.sbt.SbtScalariform.ScalariformKeys
3 | import com.typesafe.sbt.git.DefaultReadableGit
4 |
5 | import scalariform.formatter.preferences._
6 |
7 | name := "netty-in-action-scala"
8 |
9 | version := "1.0.0"
10 |
11 | scalaVersion := "2.12.4"
12 |
13 | scalacOptions in Compile ++= Seq("-encoding", "UTF-8", "-target:jvm-1.8", "-feature", "-unchecked", "-Xlog-reflective-calls", "-Xlint")
14 |
15 | javacOptions in compile ++= Seq("-encoding", "UTF-8", "-source", "1.8", "-target", "1.8", "-Xlint:unchecked", "-XDignore.symbol.file")
16 |
17 | resolvers += "akka" at "https://dl.bintray.com/akka/maven/"
18 |
19 | enablePlugins(ParadoxSitePlugin)
20 |
21 | enablePlugins(GhpagesPlugin)
22 |
23 | enablePlugins(AutomateHeaderPlugin)
24 |
25 | git.remoteRepo := "https://github.com/ReactivePlatform/netty-in-action-scala.git"
26 |
27 | GitKeys.gitReader in ThisProject := baseDirectory(base => new DefaultReadableGit(base)).value
28 |
29 | excludeFilter in ghpagesCleanSite :=
30 | new FileFilter{
31 | def accept(f: File) = (ghpagesRepository.value / "CNAME").getCanonicalPath == f.getCanonicalPath
32 | } || "versions.html"
33 |
34 | enablePlugins(ParadoxMaterialThemePlugin)
35 |
36 | ParadoxMaterialThemePlugin.paradoxMaterialThemeSettings(Paradox)
37 |
38 |
39 | paradoxProperties in Compile ++= Map(
40 | "project.name" -> "NettyInAction",
41 | "github.base_url" -> "https://github.com/ReactivePlatform/netty-in-action-scala"
42 | )
43 |
44 | paradoxMaterialTheme in Compile ~= {
45 | _.withColor("red", "pink")
46 | .withLogoIcon("cloud")
47 | .withCopyright("Copyleft © 2017 netty.reactiveplatform.xyz")
48 | .withRepository(uri("https://github.com/ReactivePlatform/netty-in-action-scala.git"))
49 | .withSearch(tokenizer = "[\\s\\-\\.]+")
50 | .withSocial(
51 | uri("https://github.com/hepin1989")
52 | )
53 | }
54 |
55 | organizationName := "netty.reactiveplatform.xyz"
56 | startYear := Some(2018)
57 | licenses += ("Apache-2.0", new URL("https://www.apache.org/licenses/LICENSE-2.0.txt"))
58 |
59 | def setPreferences(preferences: IFormattingPreferences): IFormattingPreferences = preferences
60 | .setPreference(RewriteArrowSymbols, true)
61 | .setPreference(AlignParameters, true)
62 | .setPreference(AlignSingleLineCaseStatements, true)
63 | .setPreference(DoubleIndentConstructorArguments, false)
64 | .setPreference(DoubleIndentMethodDeclaration, false)
65 | .setPreference(DanglingCloseParenthesis, Preserve)
66 | .setPreference(NewlineAtEndOfFile, true)
67 |
68 | ScalariformKeys.preferences := setPreferences(ScalariformKeys.preferences.value)
69 | ScalariformKeys.preferences in Compile := setPreferences(ScalariformKeys.preferences.value)
70 | ScalariformKeys.preferences in Test := setPreferences(ScalariformKeys.preferences.value)
71 |
72 | libraryDependencies += "io.netty" % "netty-all" % "4.1.19.Final"
73 | libraryDependencies += "junit" % "junit" % "4.12"
74 |
75 | scalafixSettings
76 |
77 | scalafixConfigure(Compile)
--------------------------------------------------------------------------------
/project/Formatting.scala:
--------------------------------------------------------------------------------
1 | import com.typesafe.sbt.SbtScalariform.ScalariformKeys
2 | import sbt._
3 |
4 | object Formatting {
5 | import scalariform.formatter.preferences._
6 |
7 | lazy val formatSettings = Seq(
8 | ScalariformKeys.preferences := setPreferences(ScalariformKeys.preferences.value),
9 | ScalariformKeys.preferences in Compile := setPreferences(ScalariformKeys.preferences.value),
10 | ScalariformKeys.preferences in Test := setPreferences(ScalariformKeys.preferences.value)
11 | )
12 |
13 | def setPreferences(preferences: IFormattingPreferences): IFormattingPreferences = preferences
14 | .setPreference(RewriteArrowSymbols, true)
15 | .setPreference(AlignParameters, true)
16 | .setPreference(AlignSingleLineCaseStatements, true)
17 | .setPreference(DoubleIndentConstructorArguments, false)
18 | .setPreference(DoubleIndentMethodDeclaration, false)
19 | .setPreference(DanglingCloseParenthesis, Preserve)
20 | .setPreference(NewlineAtEndOfFile, true)
21 | }
22 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version = 1.1.1
2 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | resolvers += "Bintray Jcenter" at "https://jcenter.bintray.com/"
2 |
3 | addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.1")
4 |
5 | addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.1")
6 |
7 | addSbtPlugin("io.github.jonas" % "sbt-paradox-material-theme" % "0.4.0")
8 |
9 | addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2")
10 |
11 | addSbtPlugin("de.heikoseeberger" % "sbt-header" % "4.0.0")
12 |
13 | addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.1")
14 |
15 | addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.7")
16 |
17 | addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0")
18 |
19 | addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.3.3")
20 |
--------------------------------------------------------------------------------
/src/main/java/nia/chapter1/javadsl/BlockingIoExample.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter1.javadsl;
18 |
19 | import java.io.BufferedReader;
20 | import java.io.IOException;
21 | import java.io.InputStreamReader;
22 | import java.io.PrintWriter;
23 | import java.net.ServerSocket;
24 | import java.net.Socket;
25 |
26 | /**
27 | * Created by kerr.
28 | *
29 | * 代码清单 1-1 阻塞 I/O 示例
30 | */
31 | public class BlockingIoExample {
32 |
33 | /**
34 | * 代码清单 1-1 阻塞 I/O 示例
35 | * */
36 | // #snip
37 | public void serve(int portNumber) throws IOException {
38 | //创建一个新的 ServerSocket,用以监听指定端口上的连接请求
39 | ServerSocket serverSocket = new ServerSocket(portNumber);
40 | //对accept()方法的调用将被阻塞,直到一个连接建立
41 | Socket clientSocket = serverSocket.accept();
42 | //这些流对象都派生于该套接字的流对象
43 | BufferedReader in = new BufferedReader(
44 | new InputStreamReader(clientSocket.getInputStream()));
45 | PrintWriter out =
46 | new PrintWriter(clientSocket.getOutputStream(), true);
47 | String request, response;
48 | //处理循环开始
49 | while ((request = in.readLine()) != null) {
50 | if ("Done".equals(request)) {
51 | break;
52 | }
53 | //请求被传递给服务器的处理方法
54 | response = processRequest(request);
55 | //服务器的响应被发送给了客户端
56 | out.println(response);
57 | //继续执行处理循环
58 | }
59 | }
60 | // #snip
61 |
62 | private String processRequest(String request){
63 | return "Processed";
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/nia/chapter1/javadsl/ConnectExample.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter1.javadsl;
18 |
19 | import io.netty.buffer.ByteBuf;
20 | import io.netty.buffer.Unpooled;
21 | import io.netty.channel.Channel;
22 | import io.netty.channel.ChannelFuture;
23 | import io.netty.channel.ChannelFutureListener;
24 | import io.netty.channel.socket.nio.NioSocketChannel;
25 |
26 | import java.net.InetSocketAddress;
27 | import java.nio.charset.Charset;
28 |
29 | /**
30 | * Created by kerr.
31 | *
32 | * 代码清单 1-3 异步地建立连接
33 | *
34 | * 代码清单 1-4 回调实战
35 | */
36 | public class ConnectExample {
37 | private static final Channel CHANNEL_FROM_SOMEWHERE = new NioSocketChannel();
38 |
39 | /**
40 | * 代码清单 1-3 异步地建立连接
41 | *
42 | * 代码清单 1-4 回调实战
43 | * */
44 | public static void connect() {
45 | Channel channel = CHANNEL_FROM_SOMEWHERE; //reference form somewhere
46 | // Does not block
47 | //异步地连接到远程节点
48 | ChannelFuture future = channel.connect(
49 | new InetSocketAddress("192.168.0.1", 25));
50 | //注册一个 ChannelFutureListener,以便在操作完成时获得通知
51 | future.addListener(new ChannelFutureListener() {
52 | @Override
53 | public void operationComplete(ChannelFuture future) {
54 | //检查操作的状态
55 | if (future.isSuccess()) {
56 | //如果操作是成功的,则创建一个 ByteBuf 以持有数据
57 | ByteBuf buffer = Unpooled.copiedBuffer(
58 | "Hello", Charset.defaultCharset());
59 | //将数据异步地发送到远程节点。返回一个 ChannelFuture
60 | ChannelFuture wf = future.channel()
61 | .writeAndFlush(buffer);
62 | // ...
63 | } else {
64 | //如果发生错误,则访问描述原因的 Throwable
65 | Throwable cause = future.cause();
66 | cause.printStackTrace();
67 | }
68 | }
69 | });
70 | }
71 | }
--------------------------------------------------------------------------------
/src/main/java/nia/chapter1/javadsl/ConnectHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter1.javadsl;
18 |
19 | import io.netty.channel.ChannelHandlerContext;
20 | import io.netty.channel.ChannelInboundHandlerAdapter;
21 |
22 | /**
23 | * Created by kerr.
24 | *
25 | * 代码清单 1-2 被回调触发的 ChannelHandler
26 | */
27 | public class ConnectHandler extends ChannelInboundHandlerAdapter {
28 | @Override
29 | //当一个新的连接已经被建立时,channelActive(ChannelHandlerContext)将会被调用
30 | public void channelActive(ChannelHandlerContext ctx)
31 | throws Exception {
32 | System.out.println(
33 | "Client " + ctx.channel().remoteAddress() + " connected");
34 | }
35 | }
--------------------------------------------------------------------------------
/src/main/java/nia/chapter1/javadsl/package-info.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter1.javadsl;
--------------------------------------------------------------------------------
/src/main/scala/io/netty/channel/DummyChannelHandlerContext.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.netty.channel
18 |
19 | import io.netty.util.concurrent.EventExecutor
20 |
21 | object DummyChannelHandlerContext {
22 | var DUMMY_INSTANCE = new DummyChannelHandlerContext(null, null, "dummy", true, true)
23 | }
24 |
25 | class DummyChannelHandlerContext(
26 | pipeline_ :DefaultChannelPipeline,
27 | executor_ :EventExecutor,
28 | name_ : String,
29 | inbound: Boolean,
30 | outbound: Boolean)
31 | extends AbstractChannelHandlerContext(pipeline_, executor_, name_, inbound, outbound) {
32 |
33 | override def handler: ChannelHandler = null
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/scala/io/netty/channel/DummyChannelPipeline.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.netty.channel
18 |
19 | object DummyChannelPipeline {
20 | val DUMMY_INSTANCE = new DummyChannelPipeline(null)
21 | }
22 |
23 | class DummyChannelPipeline(channel_ :Channel)
24 | extends DefaultChannelPipeline(channel_)
25 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter1/scaladsl/BlockingIoExample.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter1.scaladsl
18 |
19 | import java.io.{ BufferedReader, IOException, InputStreamReader, PrintWriter }
20 | import java.net.ServerSocket
21 |
22 | /**
23 | * Created by kerr.
24 | *
25 | * 代码清单 1-1 阻塞 I/O 示例
26 | */
27 | object BlockingIoExample {
28 | /**
29 | * 代码清单 1-1 阻塞 I/O 示例
30 | */
31 | // #snip
32 | @throws[IOException]
33 | def serve(portNumber: Int): Unit = {
34 | //创建一个新的 ServerSocket,用以监听指定端口上的连接请求
35 | val serverSocket = new ServerSocket(portNumber)
36 | //对accept()方法的调用将被阻塞,直到一个连接建立
37 | val clientSocket = serverSocket.accept
38 | //这些流对象都派生于该套接字的流对象
39 | val in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream))
40 | val out = new PrintWriter(clientSocket.getOutputStream, true)
41 | var request: String = in.readLine
42 | var response: String = null
43 | //处理循环开始
44 | while (request ne null) {
45 | if ("Done" != request) {
46 | //请求被传递给服务器的处理方法
47 | response = processRequest(request)
48 | //服务器的响应被发送给了客户端
49 | out.println(response)
50 | //继续执行处理循环
51 | }
52 | request = in.readLine
53 | }
54 | // #snip
55 | }
56 | private def processRequest(request: String): String = "Processed"
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter13/LogEvent.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter13
18 |
19 | import java.net.InetSocketAddress
20 |
21 | /**
22 | * 代码清单 13-1 LogEvent 消息
23 | *
24 | * @author Norman Maurer
25 | */
26 | object LogEvent {
27 | val SEPARATOR: Byte = ':'.toByte
28 |
29 | def apply(logfile: String, msg: String): LogEvent = {
30 | LogEvent(null, -1, logfile, msg)
31 | }
32 | }
33 |
34 | case class LogEvent(
35 | source: InetSocketAddress,
36 | received: Long,
37 | logfile: String,
38 | msg: String
39 | ) {
40 | override def toString: String = {
41 | s"$received [${source.toString}] [$logfile] : $msg"
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter13/LogEventBroadcaster.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter13
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.channel.{ ChannelOption, EventLoopGroup }
21 | import io.netty.channel.nio.NioEventLoopGroup
22 | import io.netty.channel.socket.nio.NioDatagramChannel
23 | import java.io.File
24 | import java.io.RandomAccessFile
25 | import java.net.InetSocketAddress
26 | import java.lang.{ Boolean ⇒ JBoolean }
27 | import java.util.Objects
28 |
29 | import scala.util.control.Breaks._
30 |
31 | /**
32 | * 代码清单 13-3 LogEventBroadcaster
33 | *
34 | * @author Norman Maurer
35 | */
36 | object LogEventBroadcaster {
37 |
38 | @throws[Exception]
39 | def main(args: Array[String]): Unit = {
40 | if (args.length != 2)
41 | throw new IllegalArgumentException
42 |
43 | //创建并启动一个新的 LogEventBroadcaster 的实例
44 | val broadcaster =
45 | new LogEventBroadcaster(new InetSocketAddress("255.255.255.255", args(0).toInt), new File(args(1)))
46 |
47 | try {
48 | broadcaster.run()
49 | } finally {
50 | broadcaster.stop()
51 | }
52 | }
53 | }
54 |
55 | class LogEventBroadcaster(address: InetSocketAddress, file: File) {
56 | val group: EventLoopGroup = new NioEventLoopGroup
57 | val bootstrap = new Bootstrap
58 |
59 | //引导该 NioDatagramChannel(无连接的)
60 | bootstrap
61 | .group(group)
62 | .channel(classOf[NioDatagramChannel])
63 | //设置 SO_BROADCAST 套接字选项
64 | .option[JBoolean](ChannelOption.SO_BROADCAST, true)
65 | .handler(new LogEventEncoder(address))
66 |
67 | @throws[Exception]
68 | def run(): Unit = { //绑定 Channel
69 | val ch = bootstrap.bind(0).sync.channel
70 | var pointer: Long = 0
71 | //启动主处理循环
72 |
73 | breakable {
74 | while (true) {
75 | val len = file.length
76 | if (len < pointer) { // file was reset
77 | //如果有必要,将文件指针设置到该文件的最后一个字节
78 | pointer = len
79 | } else if (len > pointer) { // Content was added
80 | val raf = new RandomAccessFile(file, "r")
81 | //设置当前的文件指针,以确保没有任何的旧日志被发送
82 | raf.seek(pointer)
83 | Iterator.continually(raf.readLine())
84 | .takeWhile(Objects.nonNull)
85 | .foreach { line ⇒
86 | ch.writeAndFlush(LogEvent(file.getAbsolutePath, line))
87 | }
88 | //存储其在文件中的当前位置
89 | pointer = raf.getFilePointer
90 | raf.close()
91 | }
92 | try {
93 | //休眠 1 秒,如果被中断,则退出循环;否则重新处理它
94 | Thread.sleep(1000)
95 | } catch {
96 | case e: InterruptedException ⇒
97 | Thread.interrupted
98 | break
99 | }
100 | }
101 | }
102 | }
103 |
104 | def stop(): Unit = {
105 | group.shutdownGracefully()
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter13/LogEventDecoder.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter13
18 |
19 | import io.netty.channel.ChannelHandlerContext
20 | import io.netty.channel.socket.DatagramPacket
21 | import io.netty.handler.codec.MessageToMessageDecoder
22 | import io.netty.util.CharsetUtil
23 | import java.util
24 |
25 | /**
26 | * 代码清单 13-6 LogEventDecoder
27 | *
28 | * @author Norman Maurer
29 | */
30 | class LogEventDecoder extends MessageToMessageDecoder[DatagramPacket] {
31 | @throws[Exception]
32 | override protected def decode(
33 | ctx: ChannelHandlerContext,
34 | datagramPacket: DatagramPacket,
35 | out: util.List[AnyRef]): Unit = {
36 |
37 | //获取对 DatagramPacket 中的数据(ByteBuf)的引用
38 | val data = datagramPacket.content
39 |
40 | //获取该 SEPARATOR 的索引
41 | val idx = data.indexOf(0, data.readableBytes, LogEvent.SEPARATOR)
42 |
43 | //提取文件名
44 | val filename = data.slice(0, idx).toString(CharsetUtil.UTF_8)
45 |
46 | //提取日志消息
47 | val logMsg = data.slice(idx + 1, data.readableBytes).toString(CharsetUtil.UTF_8)
48 | //构建一个新的 LogEvent 对象,并且将它添加到(已经解码的消息的)列表中
49 |
50 | val event = LogEvent(datagramPacket.sender, System.currentTimeMillis, filename, logMsg)
51 | out.add(event)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter13/LogEventEncoder.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter13
18 |
19 | import io.netty.channel.ChannelHandlerContext
20 | import io.netty.channel.socket.DatagramPacket
21 | import io.netty.handler.codec.MessageToMessageEncoder
22 | import io.netty.util.CharsetUtil
23 | import java.net.InetSocketAddress
24 | import java.util
25 |
26 | /**
27 | * 代码清单 13-2 LogEventEncoder
28 | *
29 | * @author Norman Maurer
30 | */
31 | // LogEventEncoder 创建了即将被发送到指定的 InetSocketAddress 的 DatagramPacket 消息
32 | class LogEventEncoder(remoteAddress: InetSocketAddress)
33 | extends MessageToMessageEncoder[LogEvent] {
34 |
35 | @throws[Exception]
36 | override protected def encode(
37 | channelHandlerContext: ChannelHandlerContext,
38 | logEvent: LogEvent,
39 | out: util.List[AnyRef]): Unit = {
40 | val file = logEvent.logfile.getBytes(CharsetUtil.UTF_8)
41 | val msg = logEvent.msg.getBytes(CharsetUtil.UTF_8)
42 | val buf = channelHandlerContext.alloc.buffer(file.length + msg.length + 1)
43 | //将文件名写入到 ByteBuf 中
44 | buf.writeBytes(file)
45 | //添加一个 SEPARATOR
46 | buf.writeByte(LogEvent.SEPARATOR)
47 | //将日志消息写入 ByteBuf 中
48 | buf.writeBytes(msg)
49 | //将一个拥有数据和目的地地址的新 DatagramPacket 添加到出站的消息列表中
50 | out.add(new DatagramPacket(buf, remoteAddress))
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter13/LogEventHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter13
18 |
19 | import io.netty.channel.ChannelHandlerContext
20 | import io.netty.channel.SimpleChannelInboundHandler
21 |
22 | /**
23 | * 代码清单 13-7 LogEventHandler
24 | *
25 | * @author Norman Maurer
26 | */
27 | //扩展 SimpleChannelInboundHandler 以处理 LogEvent 消息
28 | class LogEventHandler extends SimpleChannelInboundHandler[LogEvent] {
29 | @throws[Exception]
30 | override def exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable): Unit = {
31 | //当异常发生时,打印栈跟踪信息,并关闭对应的 Channel
32 | cause.printStackTrace()
33 | ctx.close()
34 | }
35 |
36 | @throws[Exception]
37 | override def channelRead0(ctx: ChannelHandlerContext, event: LogEvent): Unit = {
38 | //打印 LogEvent 的数据
39 | println(event)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter13/LogEventMonitor.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter13
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.channel._
21 | import io.netty.channel.nio.NioEventLoopGroup
22 | import io.netty.channel.socket.nio.NioDatagramChannel
23 | import java.net.InetSocketAddress
24 | import java.lang.{ Boolean ⇒ JBoolean }
25 |
26 | /**
27 | * 代码清单 13-8 LogEventMonitor
28 | *
29 | * @author Norman Maurer
30 | */
31 | object LogEventMonitor {
32 |
33 | @throws[Exception]
34 | def main(args: Array[String]): Unit = {
35 | if (args.length != 1)
36 | throw new IllegalArgumentException("Usage: LogEventMonitor ")
37 |
38 | //构造一个新的 LogEventMonitor
39 | val monitor = new LogEventMonitor(new InetSocketAddress(args(0).toInt))
40 | try {
41 | val channel = monitor.bind()
42 | println("LogEventMonitor running")
43 | channel.closeFuture.sync()
44 | } finally {
45 | monitor.stop()
46 | }
47 | }
48 | }
49 |
50 | class LogEventMonitor(address: InetSocketAddress) {
51 | val group: EventLoopGroup = new NioEventLoopGroup
52 | val bootstrap = new Bootstrap
53 | //引导该 NioDatagramChannel
54 | bootstrap.group(group)
55 | .channel(classOf[NioDatagramChannel])
56 | //设置套接字选项 SO_BROADCAST
57 | .option[JBoolean](ChannelOption.SO_BROADCAST, true)
58 | .handler(new ChannelInitializer[Channel]() {
59 | @throws[Exception]
60 | override protected def initChannel(channel: Channel): Unit = {
61 | val pipeline = channel.pipeline
62 | //将 LogEventDecoder 和 LogEventHandler 添加到 ChannelPipeline 中
63 | pipeline.addLast(new LogEventDecoder)
64 | pipeline.addLast(new LogEventHandler)
65 | }
66 | }).localAddress(address)
67 |
68 | def bind(): Channel = { //绑定 Channel。注意,DatagramChannel 是无连接的
69 | bootstrap.bind.syncUninterruptibly.channel
70 | }
71 |
72 | def stop(): Unit = {
73 | group.shutdownGracefully
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter2/echoclient/EchoClient.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter2.echoclient
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.channel.ChannelInitializer
21 | import io.netty.channel.EventLoopGroup
22 | import io.netty.channel.nio.NioEventLoopGroup
23 | import io.netty.channel.socket.SocketChannel
24 | import io.netty.channel.socket.nio.NioSocketChannel
25 | import java.net.InetSocketAddress
26 |
27 | /**
28 | * 代码清单 2-4 客户端的主类
29 | *
30 | * @author Norman Maurer
31 | */
32 | object EchoClient {
33 | @throws[Exception]
34 | def main(args: Array[String]): Unit = {
35 | if (args.length != 2) {
36 | System.err.println("Usage: " + classOf[EchoClient].getSimpleName + " ")
37 | } else {
38 | val host = args(0)
39 | val port = args(1).toInt
40 | new EchoClient(host, port).start()
41 | }
42 | }
43 | }
44 |
45 | class EchoClient(val host: String, val port: Int) {
46 | @throws[Exception]
47 | def start(): Unit = {
48 | val group: EventLoopGroup = new NioEventLoopGroup
49 | try {
50 | //创建 Bootstrap
51 | val b = new Bootstrap
52 | //指定 EventLoopGroup 以处理客户端事件;需要适用于 NIO 的实现
53 | b.group(group)
54 | //适用于 NIO 传输的Channel 类型
55 | .channel(classOf[NioSocketChannel])
56 | //设置服务器的InetSocketAddress
57 | .remoteAddress(new InetSocketAddress(host, port))
58 | //在创建Channel时,向 ChannelPipeline中添加一个 EchoClientHandler实例
59 | .handler {
60 | new ChannelInitializer[SocketChannel]() {
61 | @throws[Exception]
62 | override def initChannel(ch: SocketChannel): Unit = {
63 | ch.pipeline.addLast(new EchoClientHandler)
64 | }
65 | }
66 | }
67 | //连接到远程节点,阻塞等待直到连接完成
68 | val f = b.connect.sync()
69 | //阻塞,直到Channel 关闭
70 | f.channel.closeFuture.sync()
71 | } finally {
72 | //关闭线程池并且释放所有的资源
73 | group.shutdownGracefully.sync()
74 | }
75 | }
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter2/echoclient/EchoClientHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter2.echoclient
18 |
19 | import io.netty.buffer.{ ByteBuf, Unpooled }
20 | import io.netty.channel.ChannelHandler.Sharable
21 | import io.netty.channel.{ ChannelHandlerContext, SimpleChannelInboundHandler }
22 | import io.netty.util.CharsetUtil
23 |
24 | @Sharable //标记该类的实例可以被多个 Channel 共享
25 | class EchoClientHandler extends SimpleChannelInboundHandler[ByteBuf] {
26 | override def channelActive(ctx: ChannelHandlerContext): Unit = {
27 | //当被通知 Channel是活跃的时候,发送一条消息
28 | ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8))
29 | }
30 |
31 | override def channelRead0(ctx: ChannelHandlerContext, in: ByteBuf): Unit = {
32 | //记录已接收消息的转储
33 | System.out.println("Client received: " + in.toString(CharsetUtil.UTF_8))
34 | }
35 |
36 | override def exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable): Unit = {
37 | //在发生异常时,记录错误并关闭Channel
38 | cause.printStackTrace()
39 | ctx.close()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter2/echoserver/EchoServer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter2.echoserver
18 |
19 | import java.net.InetSocketAddress
20 |
21 | import io.netty.bootstrap.ServerBootstrap
22 | import io.netty.channel.{ ChannelInitializer, EventLoopGroup }
23 | import io.netty.channel.nio.NioEventLoopGroup
24 | import io.netty.channel.socket.SocketChannel
25 | import io.netty.channel.socket.nio.NioServerSocketChannel
26 |
27 | object EchoServer {
28 |
29 | @throws[Exception]
30 | def main(args: Array[String]): Unit = {
31 | if (args.length != 1) {
32 | System.err.println("Usage: " + classOf[EchoServer].getSimpleName + " ")
33 | } else {
34 | //设置端口值(如果端口参数的格式不正确,则抛出一个NumberFormatException)
35 | val port = args(0).toInt
36 | //调用服务器的 start()方法
37 | new EchoServer(port).start()
38 | }
39 | }
40 | }
41 |
42 | class EchoServer(val port: Int) {
43 |
44 | @throws[Exception]
45 | def start(): Unit = {
46 | val serverHandler = new EchoServerHandler
47 | //(1) 创建EventLoopGroup
48 | val group: EventLoopGroup = new NioEventLoopGroup
49 |
50 | try {
51 | //(2) 创建ServerBootstrap
52 | val b = new ServerBootstrap
53 | b.group(group)
54 | //(3) 指定所使用的 NIO 传输 Channel
55 | .channel(classOf[NioServerSocketChannel])
56 | //(4) 使用指定的端口设置套接字地址
57 | .localAddress(new InetSocketAddress(port))
58 | //(5) 添加一个EchoServerHandler到于Channel的 ChannelPipeline
59 | .childHandler {
60 | new ChannelInitializer[SocketChannel]() {
61 | @throws[Exception]
62 | override def initChannel(ch: SocketChannel): Unit = {
63 | //EchoServerHandler 被标注为@Shareable,所以我们可以总是使用同样的实例
64 | //这里对于所有的客户端连接来说,都会使用同一个 EchoServerHandler,因为其被标注为@Sharable,
65 | //这将在后面的章节中讲到。
66 | ch.pipeline.addLast(serverHandler)
67 | }
68 | }
69 | }
70 | //(6) 异步地绑定服务器;调用 sync()方法阻塞等待直到绑定完成
71 | val f = b.bind.sync()
72 | System.out.println(classOf[EchoServer].getName + " started and listening for connections on " + f.channel.localAddress)
73 | //(7) 获取 Channel 的CloseFuture,并且阻塞当前线程直到它完成
74 | f.channel.closeFuture.sync()
75 | } finally {
76 | //(8) 关闭 EventLoopGroup,释放所有的资源
77 | group.shutdownGracefully.sync()
78 | }
79 | }
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter2/echoserver/EchoServerHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter2.echoserver
18 |
19 | import io.netty.buffer.{ ByteBuf, Unpooled }
20 | import io.netty.channel.ChannelHandler.Sharable
21 | import io.netty.channel.{ ChannelFutureListener, ChannelHandlerContext, ChannelInboundHandlerAdapter }
22 | import io.netty.util.CharsetUtil
23 |
24 | @Sharable
25 | class EchoServerHandler extends ChannelInboundHandlerAdapter {
26 | override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
27 | val in = msg.asInstanceOf[ByteBuf]
28 | //将消息记录到控制台
29 | System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8))
30 | //将接收到的消息写给发送者,而不冲刷出站消息
31 | ctx.write(in)
32 | }
33 |
34 | @throws[Exception]
35 | override def channelReadComplete(ctx: ChannelHandlerContext): Unit = {
36 | //将未决消息冲刷到远程节点,并且关闭该 Channel
37 | ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE)
38 | }
39 |
40 | override def exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable): Unit = {
41 | //打印异常栈跟踪
42 | cause.printStackTrace()
43 | //关闭该Channel
44 | ctx.close()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter4/ChannelOperationExamples.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter4
18 |
19 | import io.netty.buffer.Unpooled
20 | import io.netty.channel.ChannelFuture
21 | import io.netty.channel.ChannelFutureListener
22 | import io.netty.channel.socket.nio.NioSocketChannel
23 | import io.netty.util.CharsetUtil
24 | import java.util.concurrent.Executors
25 |
26 | /**
27 | * 代码清单 4-5 写出到 Channel
28 | *
29 | * 代码清单 4-6 从多个线程使用同一个 Channel
30 | *
31 | * @author Norman Maurer
32 | */
33 | object ChannelOperationExamples {
34 | private val CHANNEL_FROM_SOMEWHERE = new NioSocketChannel
35 |
36 | /**
37 | * 代码清单 4-5 写出到 Channel
38 | */
39 | def writingToChannel(): Unit = {
40 | val channel = CHANNEL_FROM_SOMEWHERE
41 | // Get the channel reference from somewhere
42 | //创建持有要写数据的 ByteBuf
43 | val buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8)
44 | val cf = channel.writeAndFlush(buf)
45 | //添加 ChannelFutureListener 以便在写操作完成后接收通知
46 | cf.addListener(new ChannelFutureListener() {
47 | override def operationComplete(future: ChannelFuture): Unit = { //写操作完成,并且没有错误发生
48 | if (future.isSuccess)
49 | println("Write successful")
50 | else { //记录错误
51 | System.err.println("Write error")
52 | future.cause.printStackTrace()
53 | }
54 | }
55 | })
56 | }
57 |
58 | /**
59 | * 代码清单 4-6 从多个线程使用同一个 Channel
60 | */
61 | def writingToChannelFromManyThreads(): Unit = {
62 | val channel = CHANNEL_FROM_SOMEWHERE
63 | //创建持有要写数据的ByteBuf
64 | val buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8)
65 | //创建将数据写到Channel 的 Runnable
66 | val writer: Runnable = () ⇒ channel.write(buf.duplicate())
67 | //获取到线程池Executor 的引用
68 | val executor = Executors.newCachedThreadPool
69 | //递交写任务给线程池以便在某个线程中执行
70 | // write in one thread
71 | executor.execute(writer)
72 | //递交另一个写任务以便在另一个线程中执行
73 | // write in another thread
74 | executor.execute(writer)
75 | //...
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter4/NettyNioServer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter4
18 |
19 | import io.netty.bootstrap.ServerBootstrap
20 | import io.netty.buffer.Unpooled
21 | import io.netty.channel._
22 | import io.netty.channel.nio.NioEventLoopGroup
23 | import io.netty.channel.socket.SocketChannel
24 | import io.netty.channel.socket.nio.NioServerSocketChannel
25 | import java.net.InetSocketAddress
26 | import java.nio.charset.Charset
27 |
28 | /**
29 | * 代码清单 4-4 使用 Netty 的异步网络处理
30 | *
31 | * @author Norman Maurer
32 | */
33 | class NettyNioServer {
34 | @throws[Exception]
35 | def server(port: Int): Unit = {
36 | val buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8")))
37 | //为非阻塞模式使用NioEventLoopGroup
38 | val group: EventLoopGroup = new NioEventLoopGroup
39 | try { //创建ServerBootstrap
40 | val b = new ServerBootstrap
41 | b.group(group)
42 | .channel(classOf[NioServerSocketChannel])
43 | .localAddress(new InetSocketAddress(port))
44 | //指定 ChannelInitializer,对于每个已接受的连接都调用它
45 | .childHandler {
46 | new ChannelInitializer[SocketChannel]() {
47 | @throws[Exception]
48 | override def initChannel(ch: SocketChannel): Unit = {
49 | ch.pipeline.addLast(new ChannelInboundHandlerAdapter() {
50 | @throws[Exception]
51 | override def channelActive(ctx: ChannelHandlerContext): Unit = {
52 | //将消息写到客户端,并添加ChannelFutureListener,
53 | //以便消息一被写完就关闭连接
54 | ctx.writeAndFlush(buf.duplicate)
55 | .addListener(ChannelFutureListener.CLOSE)
56 | }
57 | })
58 | }
59 | }
60 | }
61 | //绑定服务器以接受连接
62 | val f = b.bind.sync()
63 | f.channel.closeFuture.sync()
64 | } finally {
65 | //释放所有的资源
66 | group.shutdownGracefully.sync()
67 | }
68 | }
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter4/NettyOioServer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter4
18 |
19 | import io.netty.bootstrap.ServerBootstrap
20 | import io.netty.buffer.Unpooled
21 | import io.netty.channel._
22 | import io.netty.channel.oio.OioEventLoopGroup
23 | import io.netty.channel.socket.SocketChannel
24 | import io.netty.channel.socket.oio.OioServerSocketChannel
25 | import java.net.InetSocketAddress
26 | import java.nio.charset.Charset
27 |
28 | /**
29 | * Listing 4.3 Blocking networking with Netty
30 | *
31 | * @author Norman Maurer
32 | */
33 | class NettyOioServer {
34 | @throws[Exception]
35 | def server(port: Int): Unit = {
36 | val buf = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hi!\r\n", Charset.forName("UTF-8")))
37 | val group: EventLoopGroup = new OioEventLoopGroup
38 | try {
39 | //创建 ServerBootstrap
40 | val b = new ServerBootstrap
41 | b.group(group)
42 | //使用 OioEventLoopGroup以允许阻塞模式(旧的I/O)
43 | .channel(classOf[OioServerSocketChannel])
44 | .localAddress(new InetSocketAddress(port))
45 | //指定 ChannelInitializer,对于每个已接受的连接都调用它
46 | .childHandler {
47 | new ChannelInitializer[SocketChannel]() {
48 | @throws[Exception]
49 | override def initChannel(ch: SocketChannel): Unit = {
50 | ch.pipeline.addLast(new ChannelInboundHandlerAdapter() {
51 | @throws[Exception]
52 | override def channelActive(ctx: ChannelHandlerContext): Unit = {
53 | ctx.writeAndFlush(buf.duplicate).addListener( //将消息写到客户端,并添加 ChannelFutureListener,
54 | //以便消息一被写完就关闭连接
55 | ChannelFutureListener.CLOSE)
56 | }
57 | })
58 | }
59 | }
60 | }
61 | //绑定服务器以接受连接
62 | val f = b.bind.sync()
63 | f.channel.closeFuture.sync()
64 | } finally {
65 | //释放所有的资源
66 | group.shutdownGracefully.sync()
67 | }
68 | }
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter4/PlainNioServer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter4
18 |
19 | import java.io.IOException
20 | import java.net.InetSocketAddress
21 | import java.nio.ByteBuffer
22 | import java.nio.channels.SelectionKey
23 | import java.nio.channels.Selector
24 | import java.nio.channels.ServerSocketChannel
25 | import java.nio.channels.SocketChannel
26 | import scala.util.control.Breaks._
27 |
28 | /**
29 | * 代码清单 4-2 未使用 Netty 的异步网络编程
30 | *
31 | * @author Norman Maurer
32 | */
33 | class PlainNioServer {
34 | @throws[IOException]
35 | def serve(port: Int): Unit = {
36 | val serverChannel = ServerSocketChannel.open()
37 | serverChannel.configureBlocking(false)
38 | val ss = serverChannel.socket
39 | val address = new InetSocketAddress(port)
40 | //将服务器绑定到选定的端口
41 | ss.bind(address)
42 | //打开Selector来处理 Channel
43 | val selector = Selector.open()
44 | //将ServerSocket注册到Selector以接受连接
45 | serverChannel.register(selector, SelectionKey.OP_ACCEPT)
46 | val msg = ByteBuffer.wrap("Hi!\r\n".getBytes)
47 |
48 | breakable {
49 | while (true) {
50 | try {
51 | //等待需要处理的新事件;阻塞将一直持续到下一个传入事件
52 | selector.select
53 | } catch {
54 | case ex: IOException ⇒
55 | ex.printStackTrace()
56 | //handle exception
57 | break
58 | }
59 | //获取所有接收事件的SelectionKey实例
60 | val readyKeys = selector.selectedKeys
61 | val iterator = readyKeys.iterator
62 | while (iterator.hasNext) {
63 | val key = iterator.next
64 | iterator.remove()
65 | try { //检查事件是否是一个新的已经就绪可以被接受的连接
66 | if (key.isAcceptable) {
67 | val server = key.channel.asInstanceOf[ServerSocketChannel]
68 | val client = server.accept
69 | client.configureBlocking(false)
70 | //接受客户端,并将它注册到选择器
71 | client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, msg.duplicate)
72 | println("Accepted connection from " + client)
73 | }
74 | //检查套接字是否已经准备好写数据
75 | if (key.isWritable) {
76 | val client = key.channel.asInstanceOf[SocketChannel]
77 | val buffer = key.attachment.asInstanceOf[ByteBuffer]
78 | breakable {
79 | while (buffer.hasRemaining) {
80 | //将数据写到已连接的客户端
81 | if (client.write(buffer) == 0) break
82 | }
83 | }
84 | //关闭连接
85 | client.close()
86 | }
87 | } catch {
88 | case ex: IOException ⇒
89 | key.cancel()
90 | try
91 | key.channel.close()
92 | catch {
93 | case cex: IOException ⇒
94 | // ignore on close
95 | }
96 | }
97 | }
98 | }
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter4/PlainOioServer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter4
18 |
19 | import java.io.IOException
20 | import java.net.ServerSocket
21 | import java.nio.charset.Charset
22 |
23 | /**
24 | * 代码清单 4-1 未使用 Netty 的阻塞网络编程
25 | *
26 | * @author Norman Maurer
27 | */
28 | class PlainOioServer {
29 | @throws[IOException]
30 | def serve(port: Int): Unit = { //将服务器绑定到指定端口
31 | val socket = new ServerSocket(port)
32 | try {
33 | while (true) {
34 | val clientSocket = socket.accept
35 | System.out.println("Accepted connection from " + clientSocket)
36 |
37 | //创建一个新的线程来处理该连接
38 | new Thread(() ⇒ {
39 | try {
40 | //将消息写给已连接的客户端
41 | val out = clientSocket.getOutputStream
42 | out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8")))
43 | out.flush()
44 | //关闭连接
45 | clientSocket.close()
46 | } catch {
47 | case e: IOException ⇒
48 | e.printStackTrace()
49 | } finally {
50 | try {
51 | clientSocket.close()
52 | } catch {
53 | case ex: IOException ⇒
54 | // ignore on close
55 | }
56 | }
57 | }).start() //启动线程
58 | }
59 | } catch {
60 | case e: IOException ⇒
61 | e.printStackTrace()
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter5/ByteBufExamples.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter5
18 |
19 | import io.netty.buffer._
20 | import io.netty.channel.socket.nio.NioSocketChannel
21 | import io.netty.util.ByteProcessor
22 | import java.nio.ByteBuffer
23 | import java.nio.charset.Charset
24 | import java.util.Random
25 | import collection.JavaConverters.iterableAsScalaIterableConverter
26 |
27 | import io.netty.channel.DummyChannelHandlerContext.DUMMY_INSTANCE
28 |
29 | /**
30 | * Created by kerr.
31 | *
32 | * 代码清单 5-1 支撑数组
33 | *
34 | * 代码清单 5-2 访问直接缓冲区的数据
35 | *
36 | * 代码清单 5-3 使用 ByteBuffer 的复合缓冲区模式
37 | *
38 | * 代码清单 5-4 使用 CompositeByteBuf 的复合缓冲区模式
39 | *
40 | * 代码清单 5-5 访问 CompositeByteBuf 中的数据
41 | *
42 | * 代码清单 5-6 访问数据
43 | *
44 | * 代码清单 5-7 读取所有数据
45 | *
46 | * 代码清单 5-8 写数据
47 | *
48 | * 代码清单 5-9 使用 ByteBufProcessor 来寻找\r
49 | *
50 | * 代码清单 5-10 对 ByteBuf 进行切片
51 | *
52 | * 代码清单 5-11 复制一个 ByteBuf
53 | *
54 | * 代码清单 5-12 get()和 set()方法的用法
55 | *
56 | * 代码清单 5-13 ByteBuf 上的 read()和 write()操作
57 | *
58 | * 代码清单 5-14 获取一个到 ByteBufAllocator 的引用
59 | *
60 | * 代码清单 5-15 引用计数
61 | *
62 | * 代码清单 5-16 释放引用计数的对象
63 | */
64 | object ByteBufExamples {
65 | private val random = new Random
66 | private val BYTE_BUF_FROM_SOMEWHERE = Unpooled.buffer(1024)
67 | private val CHANNEL_FROM_SOMEWHERE = new NioSocketChannel
68 | private val CHANNEL_HANDLER_CONTEXT_FROM_SOMEWHERE = DUMMY_INSTANCE
69 |
70 | private def handleArray(array: Array[Byte], offset: Int, len: Int): Unit = {
71 | println(offset)
72 | println(len)
73 | println(array(offset))
74 | }
75 |
76 | /**
77 | * 代码清单 5-1 支撑数组
78 | */
79 | def heapBuffer(): Unit = {
80 | val heapBuf = BYTE_BUF_FROM_SOMEWHERE //get reference form somewhere
81 | //检查 ByteBuf 是否有一个支撑数组
82 | if (heapBuf.hasArray) { //如果有,则获取对该数组的引用
83 | val array = heapBuf.array
84 | //计算第一个字节的偏移量
85 | val offset = heapBuf.arrayOffset + heapBuf.readerIndex
86 | //获得可读字节数
87 | val length = heapBuf.readableBytes
88 | //使用数组、偏移量和长度作为参数调用你的方法
89 | handleArray(array, offset, length)
90 | }
91 | }
92 |
93 | /**
94 | * 代码清单 5-2 访问直接缓冲区的数据
95 | */
96 | def directBuffer(): Unit = {
97 | val directBuf = BYTE_BUF_FROM_SOMEWHERE
98 | //检查 ByteBuf 是否由数组支撑。如果不是,则这是一个直接缓冲区
99 | if (!directBuf.hasArray) { //获取可读字节数
100 | val length = directBuf.readableBytes
101 | //分配一个新的数组来保存具有该长度的字节数据
102 | val array = new Array[Byte](length)
103 | //将字节复制到该数组
104 | directBuf.getBytes(directBuf.readerIndex, array)
105 | handleArray(array, 0, length)
106 | }
107 | }
108 |
109 | /**
110 | * 代码清单 5-3 使用 ByteBuffer 的复合缓冲区模式
111 | */
112 | def byteBufferComposite(header: ByteBuffer, body: ByteBuffer): Unit = { // Use an array to hold the message parts
113 | val message = Array[ByteBuffer](header, body)
114 | // Create a new ByteBuffer and use copy to merge the header and body
115 | val message2 = ByteBuffer.allocate(header.remaining + body.remaining)
116 | message2.put(header)
117 | message2.put(body)
118 | message2.flip()
119 | }
120 |
121 | /**
122 | * 代码清单 5-4 使用 CompositeByteBuf 的复合缓冲区模式
123 | */
124 | def byteBufComposite(): Unit = {
125 | val messageBuf: CompositeByteBuf = Unpooled.compositeBuffer
126 | val headerBuf = BYTE_BUF_FROM_SOMEWHERE
127 | // can be backing or direct
128 | val bodyBuf = BYTE_BUF_FROM_SOMEWHERE
129 | //将 ByteBuf 实例追加到 CompositeByteBuf
130 | messageBuf.addComponents(headerBuf, bodyBuf)
131 | //...
132 | //删除位于索引位置为 0(第一个组件)的 ByteBuf
133 | messageBuf.removeComponent(0) // remove the header
134 |
135 | //循环遍历所有的 ByteBuf 实例
136 | for (buf: ByteBuf ← messageBuf.asScala) {
137 | println(buf.toString())
138 | }
139 | }
140 |
141 | /**
142 | * 代码清单 5-5 访问 CompositeByteBuf 中的数据
143 | */
144 | def byteBufCompositeArray(): Unit = {
145 | val compBuf: CompositeByteBuf = Unpooled.compositeBuffer
146 | val length = compBuf.readableBytes
147 | //分配一个具有可读字节数长度的新数组
148 | val array = new Array[Byte](length)
149 | //将字节读到该数组中
150 | compBuf.getBytes(compBuf.readerIndex(), array)
151 | //使用偏移量和长度作为参数使用该数组
152 | handleArray(array, 0, array.length)
153 | }
154 |
155 | /**
156 | * 代码清单 5-6 访问数据
157 | */
158 | def byteBufRelativeAccess(): Unit = {
159 | val buffer = BYTE_BUF_FROM_SOMEWHERE
160 | var i = 0
161 | while (i < buffer.capacity) {
162 | val b = buffer.getByte(i)
163 | println(b.toChar)
164 | i += 1
165 | }
166 | }
167 |
168 | /**
169 | * 代码清单 5-7 读取所有数据
170 | */
171 | def readAllData(): Unit = {
172 | val buffer = BYTE_BUF_FROM_SOMEWHERE
173 | while (buffer.isReadable)
174 | println(buffer.readByte)
175 | }
176 |
177 | /**
178 | * 代码清单 5-8 写数据
179 | */
180 | def write(): Unit = { // Fills the writable bytes of a buffer with random integers.
181 | val buffer = BYTE_BUF_FROM_SOMEWHERE
182 | while (buffer.writableBytes >= 4)
183 | buffer.writeInt(random.nextInt)
184 | }
185 |
186 | /**
187 | * 代码清单 5-9 使用 ByteBufProcessor 来寻找\r
188 | *
189 | * use {@link io.netty.buffer.ByteBufProcessor in Netty 4.0.x}
190 | */
191 | def byteProcessor(): Unit = {
192 | val buffer = BYTE_BUF_FROM_SOMEWHERE
193 | val index = buffer.forEachByte(ByteProcessor.FIND_CR)
194 | }
195 |
196 | /**
197 | * 代码清单 5-9 使用 ByteBufProcessor 来寻找\r
198 | *
199 | * use {@link io.netty.util.ByteProcessor in Netty 4.1.x}
200 | */
201 | def byteBufProcessor(): Unit = {
202 | val buffer = BYTE_BUF_FROM_SOMEWHERE
203 | val index = buffer.forEachByte(ByteBufProcessor.FIND_CR)
204 | }
205 |
206 | /**
207 | * 代码清单 5-10 对 ByteBuf 进行切片
208 | */
209 | def byteBufSlice(): Unit = {
210 | val utf8 = Charset.forName("UTF-8")
211 | //创建一个用于保存给定字符串的字节的 ByteBuf
212 | val buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8)
213 | //创建该 ByteBuf 从索引 0 开始到索引 15 结束的一个新切片
214 | val sliced = buf.slice(0, 15)
215 | //将打印“Netty in Action”
216 | System.out.println(sliced.toString(utf8))
217 | //更新索引 0 处的字节
218 | buf.setByte(0, 'J'.toByte)
219 | //将会成功,因为数据是共享的,对其中一个所做的更改对另外一个也是可见的
220 | assert(buf.getByte(0) == sliced.getByte(0))
221 | }
222 |
223 | /**
224 | * 代码清单 5-11 复制一个 ByteBuf
225 | */
226 | def byteBufCopy(): Unit = {
227 | val utf8 = Charset.forName("UTF-8")
228 | //创建 ByteBuf 以保存所提供的字符串的字节
229 | val buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8)
230 | //创建该 ByteBuf 从索引 0 开始到索引 15 结束的分段的副本
231 | val copy = buf.copy(0, 15)
232 | System.out.println(copy.toString(utf8))
233 | buf.setByte(0, 'J'.toByte)
234 | //将会成功,因为数据不是共享的
235 | assert(buf.getByte(0) != copy.getByte(0))
236 | }
237 |
238 | /**
239 | * 代码清单 5-12 get()和 set()方法的用法
240 | */
241 | def byteBufSetGet(): Unit = {
242 | val utf8 = Charset.forName("UTF-8")
243 | //创建一个新的 ByteBuf以保存给定字符串的字节
244 | val buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8)
245 | //打印第一个字符'N'
246 | System.out.println(buf.getByte(0).toChar)
247 | //存储当前的 readerIndex 和 writerIndex
248 | val readerIndex = buf.readerIndex
249 | val writerIndex = buf.writerIndex
250 | //将索引 0 处的字 节更新为字符'B'
251 | buf.setByte(0, 'B'.toByte)
252 | //打印第一个字符,现在是'B'
253 | System.out.println(buf.getByte(0).toChar)
254 | //将会成功,因为这些操作并不会修改相应的索引
255 | assert(readerIndex == buf.readerIndex)
256 | assert(writerIndex == buf.writerIndex)
257 | }
258 |
259 | /**
260 | * 代码清单 5-13 ByteBuf 上的 read()和 write()操作
261 | */
262 | def byteBufWriteRead(): Unit = {
263 | val utf8 = Charset.forName("UTF-8")
264 | //创建一个新的 ByteBuf 以保存给定字符串的字节
265 | val buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8)
266 | System.out.println(buf.readByte.toChar)
267 | //存储当前的readerIndex
268 | val readerIndex = buf.readerIndex
269 | //存储当前的writerIndex
270 | val writerIndex = buf.writerIndex
271 | //将字符 '?'追加到缓冲区
272 | buf.writeByte('?'.toByte)
273 | assert(readerIndex == buf.readerIndex)
274 | //将会成功,因为 writeByte()方法移动了 writerIndex
275 | assert(writerIndex != buf.writerIndex)
276 | }
277 |
278 | /**
279 | * 代码清单 5-14 获取一个到 ByteBufAllocator 的引用
280 | */
281 | def obtainingByteBufAllocatorReference(): Unit = {
282 | val channel = CHANNEL_FROM_SOMEWHERE
283 | //从 Channel 获取一个到ByteBufAllocator 的引用
284 | val allocator = channel.alloc()
285 | val ctx = CHANNEL_HANDLER_CONTEXT_FROM_SOMEWHERE
286 | //从 ChannelHandlerContext 获取一个到 ByteBufAllocator 的引用
287 | val allocator2 = ctx.alloc()
288 | }
289 |
290 | /**
291 | * 代码清单 5-15 引用计数
292 | */
293 | def referenceCounting(): Unit = {
294 | val channel = CHANNEL_FROM_SOMEWHERE
295 | //从 Channel 获取ByteBufAllocator
296 | val allocator = channel.alloc()
297 | //从 ByteBufAllocator分配一个 ByteBuf
298 | val buffer = allocator.directBuffer
299 | //检查引用计数是否为预期的 1
300 | assert(buffer.refCnt == 1)
301 | }
302 |
303 | /**
304 | * 代码清单 5-16 释放引用计数的对象
305 | */
306 | def releaseReferenceCountedObject(): Unit = {
307 | val buffer = BYTE_BUF_FROM_SOMEWHERE
308 | //减少到该对象的活动引用。当减少到 0 时,该对象被释放,并且该方法返回 true
309 | val released = buffer.release()
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/ChannelFutures.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.buffer.Unpooled
20 | import io.netty.channel.ChannelFuture
21 | import io.netty.channel.ChannelFutureListener
22 | import io.netty.channel.socket.nio.NioSocketChannel
23 |
24 | /**
25 | * Created by kerr.
26 | *
27 | * 代码清单 6-13 添加 ChannelFutureListener 到 ChannelFuture
28 | */
29 | object ChannelFutures {
30 | private val CHANNEL_FROM_SOMEWHERE = new NioSocketChannel
31 | private val SOME_MSG_FROM_SOMEWHERE = Unpooled.buffer(1024)
32 |
33 | /**
34 | * 代码清单 6-13 添加 ChannelFutureListener 到 ChannelFuture
35 | */
36 | def addingChannelFutureListener(): Unit = {
37 | val channel = CHANNEL_FROM_SOMEWHERE
38 | // get reference to pipeline;
39 | val someMessage = SOME_MSG_FROM_SOMEWHERE
40 | //...
41 | val future = channel.write(someMessage)
42 | future.addListener(new ChannelFutureListener() {
43 | override def operationComplete(f: ChannelFuture): Unit = {
44 | if (!f.isSuccess) {
45 | f.cause.printStackTrace()
46 | f.channel.close
47 | }
48 | }
49 | })
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/DiscardHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandler.Sharable
20 | import io.netty.channel.ChannelHandlerContext
21 | import io.netty.channel.ChannelInboundHandlerAdapter
22 | import io.netty.util.ReferenceCountUtil
23 |
24 | /**
25 | * 代码清单 6-1 释放消息资源
26 | *
27 | * @author Norman Maurer
28 | */
29 | @Sharable //扩展了 ChannelInboundHandlerAdapter
30 | class DiscardHandler extends ChannelInboundHandlerAdapter {
31 | override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
32 | //丢弃已接收的消息
33 | ReferenceCountUtil.release(msg)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/DiscardInboundHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandler.Sharable
20 | import io.netty.channel.ChannelHandlerContext
21 | import io.netty.channel.ChannelInboundHandlerAdapter
22 | import io.netty.util.ReferenceCountUtil
23 |
24 | /**
25 | * 代码清单 6-3 消费并释放入站消息
26 | *
27 | * @author Norman Maurer
28 | */
29 | @Sharable //扩展了ChannelInboundandlerAdapter
30 | class DiscardInboundHandler extends ChannelInboundHandlerAdapter {
31 | override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
32 | //通过调用 ReferenceCountUtil.release()方法释放资源
33 | ReferenceCountUtil.release(msg)
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/DiscardOutboundHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandler.Sharable
20 | import io.netty.channel.ChannelHandlerContext
21 | import io.netty.channel.ChannelOutboundHandlerAdapter
22 | import io.netty.channel.ChannelPromise
23 | import io.netty.util.ReferenceCountUtil
24 |
25 | /**
26 | * 代码清单 6-4 丢弃并释放出站消息
27 | *
28 | * @author Norman Maurer
29 | */
30 | @Sharable //扩展了ChannelOutboundHandlerAdapter
31 | class DiscardOutboundHandler extends ChannelOutboundHandlerAdapter {
32 | override def write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise): Unit = {
33 | //通过使用 ReferenceCountUtil.realse(...)方法释放资源
34 | ReferenceCountUtil.release(msg)
35 | //通知 ChannelPromise数据已经被处理了
36 | promise.setSuccess
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/InboundExceptionHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandlerContext
20 | import io.netty.channel.ChannelInboundHandlerAdapter
21 |
22 | /**
23 | * 代码清单 6-12 基本的入站异常处理
24 | *
25 | * @author Norman Maurer
26 | */
27 | class InboundExceptionHandler extends ChannelInboundHandlerAdapter {
28 | override def exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable): Unit = {
29 | cause.printStackTrace()
30 | ctx.close()
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/ModifyChannelPipeline.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandlerAdapter
20 | import io.netty.channel.DummyChannelPipeline.DUMMY_INSTANCE
21 |
22 | /**
23 | * 代码清单 6-5 修改 ChannelPipeline
24 | *
25 | * @author Norman Maurer
26 | */
27 | object ModifyChannelPipeline {
28 | private val CHANNEL_PIPELINE_FROM_SOMEWHERE = DUMMY_INSTANCE
29 |
30 | /**
31 | * 代码清单 6-5 修改 ChannelPipeline
32 | */
33 | def modifyPipeline(): Unit = {
34 | val pipeline = CHANNEL_PIPELINE_FROM_SOMEWHERE
35 | // get reference to pipeline;
36 | //创建一个 FirstHandler 的实例
37 | val firstHandler = new FirstHandler
38 | //将该实例作为"handler1"添加到ChannelPipeline 中
39 | pipeline.addLast("handler1", firstHandler)
40 | //将一个 SecondHandler的实例作为"handler2"添加到 ChannelPipeline的第一个槽中。这意味着它将被放置在已有的"handler1"之前
41 | pipeline.addFirst("handler2", new SecondHandler)
42 | //将一个 ThirdHandler 的实例作为"handler3"添加到 ChannelPipeline 的最后一个槽中
43 | pipeline.addLast("handler3", new ThirdHandler)
44 | //...
45 | //通过名称移除"handler3"
46 | pipeline.remove("handler3")
47 | //通过引用移除FirstHandler(它是唯一的,所以不需要它的名称)
48 | pipeline.remove(firstHandler)
49 | //将 SecondHandler("handler2")替换为 FourthHandler:"handler4"
50 | pipeline.replace("handler2", "handler4", new FourthHandler)
51 | }
52 |
53 | final private class FirstHandler extends ChannelHandlerAdapter {}
54 |
55 | final private class SecondHandler extends ChannelHandlerAdapter {}
56 |
57 | final private class ThirdHandler extends ChannelHandlerAdapter {}
58 |
59 | final private class FourthHandler extends ChannelHandlerAdapter {}
60 |
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/OutboundExceptionHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel._
20 |
21 | /**
22 | * 代码清单 6-13 添加 ChannelFutureListener 到 ChannelPromise
23 | *
24 | * @author Norman Maurer
25 | */
26 | class OutboundExceptionHandler extends ChannelOutboundHandlerAdapter {
27 | override def write(ctx: ChannelHandlerContext, msg: Any, promise: ChannelPromise): Unit = {
28 | promise.addListener(new ChannelFutureListener() {
29 | override def operationComplete(f: ChannelFuture): Unit = {
30 | if (!f.isSuccess) {
31 | f.cause.printStackTrace()
32 | f.channel.close()
33 | }
34 | }
35 | })
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/SharableHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandler.Sharable
20 | import io.netty.channel.ChannelHandlerContext
21 | import io.netty.channel.ChannelInboundHandlerAdapter
22 |
23 | /**
24 | * 代码清单 6-10 可共享的 ChannelHandler
25 | *
26 | * @author Norman Maurer
27 | */
28 | //使用注解@Sharable标注
29 | @Sharable
30 | class SharableHandler extends ChannelInboundHandlerAdapter {
31 | override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
32 | println("channel read message " + msg)
33 | //记录方法调用,并转发给下一个 ChannelHandler
34 | ctx.fireChannelRead(msg)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/SimpleDiscardHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandler.Sharable
20 | import io.netty.channel.ChannelHandlerContext
21 | import io.netty.channel.SimpleChannelInboundHandler
22 |
23 | /**
24 | * 代码清单 6-2 使用 SimpleChannelInboundHandler
25 | *
26 | * @author Norman Maurer
27 | */
28 | @Sharable //扩展了SimpleChannelInboundHandler
29 | class SimpleDiscardHandler extends SimpleChannelInboundHandler[Any] {
30 | override def channelRead0(ctx: ChannelHandlerContext, msg: Any): Unit = {
31 | // 不需要任何显式的资源释放
32 | // No need to do anything special
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/UnsharableHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandler.Sharable
20 | import io.netty.channel.ChannelHandlerContext
21 | import io.netty.channel.ChannelInboundHandlerAdapter
22 |
23 | /**
24 | * 代码清单 6-11 @Sharable 的错误用法
25 | *
26 | * @author Norman Maurer
27 | */
28 | //使用注解@Sharable标注
29 | @Sharable
30 | class UnsharableHandler extends ChannelInboundHandlerAdapter {
31 | private var count = 0
32 |
33 | override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
34 | //将 count 字段的值加 1
35 | count += 1
36 | //记录方法调用,并转发给下一个ChannelHandler
37 | System.out.println("inboundBufferUpdated(...) called the " + count + " time")
38 | ctx.fireChannelRead(msg)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/WriteHandler.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.channel.ChannelHandlerAdapter
20 | import io.netty.channel.ChannelHandlerContext
21 |
22 | /**
23 | * 代码清单 6-9 缓存到 ChannelHandlerContext 的引用
24 | *
25 | * @author Norman Maurer
26 | */
27 | class WriteHandler extends ChannelHandlerAdapter {
28 | private var ctx: ChannelHandlerContext = _
29 |
30 | override def handlerAdded(ctx: ChannelHandlerContext): Unit = {
31 | //存储到 ChannelHandlerContext的引用以供稍后使用
32 | this.ctx = ctx
33 | }
34 |
35 | def send(msg: String): Unit = { //使用之前存储的到 ChannelHandlerContext的引用来发送消息
36 | ctx.writeAndFlush(msg)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter6/WriteHandlers.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter6
18 |
19 | import io.netty.buffer.Unpooled
20 | import io.netty.channel.DummyChannelPipeline
21 | import io.netty.util.CharsetUtil
22 | import io.netty.channel.DummyChannelHandlerContext.DUMMY_INSTANCE
23 |
24 | /**
25 | * Created by kerr.
26 | *
27 | * 代码清单 6-6 从 ChannelHandlerContext 访问 Channel
28 | *
29 | * 代码清单 6-7 通过 ChannelHandlerContext 访问 ChannelPipeline
30 | *
31 | * Listing 6.8 Calling ChannelHandlerContext write()
32 | */
33 | object WriteHandlers {
34 | private val CHANNEL_HANDLER_CONTEXT_FROM_SOMEWHERE = DUMMY_INSTANCE
35 | private val CHANNEL_PIPELINE_FROM_SOMEWHERE = DummyChannelPipeline.DUMMY_INSTANCE
36 |
37 | /**
38 | * 代码清单 6-6 从 ChannelHandlerContext 访问 Channel
39 | */
40 | def writeViaChannel(): Unit = {
41 | val ctx = CHANNEL_HANDLER_CONTEXT_FROM_SOMEWHERE
42 | //get reference form somewhere
43 | //获取到与 ChannelHandlerContext相关联的 Channel 的引用
44 | val channel = ctx.channel
45 | //通过 Channel 写入缓冲区
46 | channel.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8))
47 | }
48 |
49 | /**
50 | * 代码清单 6-7 通过 ChannelHandlerContext 访问 ChannelPipeline
51 | */
52 | def writeViaChannelPipeline(): Unit = {
53 | val ctx = CHANNEL_HANDLER_CONTEXT_FROM_SOMEWHERE
54 | //获取到与 ChannelHandlerContext相关联的 ChannelPipeline 的引用
55 | val pipeline = ctx.pipeline
56 | //通过 ChannelPipeline写入缓冲区
57 | pipeline.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8))
58 | }
59 |
60 | /**
61 | * 代码清单 6-8 调用 ChannelHandlerContext 的 write()方法
62 | */
63 | def writeViaChannelHandlerContext(): Unit = { //获取到 ChannelHandlerContext 的引用
64 | val ctx = CHANNEL_HANDLER_CONTEXT_FROM_SOMEWHERE //get reference form somewhere;
65 | //write()方法将把缓冲区数据发送到下一个 ChannelHandler
66 | ctx.write(Unpooled.copiedBuffer("Netty in Action", CharsetUtil.UTF_8))
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter7/EventLoopExamples.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter7
18 |
19 | import java.util.Collections
20 | import collection.JavaConverters.asScalaBufferConverter
21 |
22 | /**
23 | * 代码清单 7-1 在事件循环中执行任务
24 | *
25 | * @author Norman Maurer
26 | */
27 | object EventLoopExamples {
28 | /**
29 | * 代码清单 7-1 在事件循环中执行任务
30 | */
31 | def executeTaskInEventLoop(): Unit = {
32 | val terminated = true
33 | //...
34 | while (!terminated) { //阻塞,直到有事件已经就绪可被运行
35 | val readyEvents = blockUntilEventsReady
36 | for (ev: Runnable ← readyEvents.asScala) { //循环遍历,并处理所有的事件
37 | ev.run()
38 | }
39 | }
40 | }
41 |
42 | private def blockUntilEventsReady = Collections.singletonList[Runnable](new Runnable() {
43 | override def run(): Unit = {
44 | try {
45 | Thread.sleep(1000)
46 | } catch {
47 | case e: InterruptedException ⇒
48 | e.printStackTrace()
49 | }
50 | }
51 | })
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter7/ScheduleExamples.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter7
18 |
19 | import io.netty.channel.socket.nio.NioSocketChannel
20 | import java.util.concurrent.Executors
21 | import java.util.concurrent.TimeUnit
22 |
23 | /**
24 | * 代码清单 7-2 使用 ScheduledExecutorService 调度任务
25 | *
26 | * 代码清单 7-3 使用 EventLoop 调度任务
27 | *
28 | * 代码清单 7-4 使用 EventLoop 调度周期性的任务
29 | *
30 | * 代码清单 7-5 使用 ScheduledFuture 取消任务
31 | *
32 | * @author Norman Maurer
33 | */
34 | object ScheduleExamples {
35 | private val CHANNEL_FROM_SOMEWHERE = new NioSocketChannel
36 |
37 | /**
38 | * 代码清单 7-2 使用 ScheduledExecutorService 调度任务
39 | */
40 | def schedule(): Unit = { //创建一个其线程池具有 10 个线程的ScheduledExecutorService
41 | val executor = Executors.newScheduledThreadPool(10)
42 | val future = executor.schedule(
43 | new Runnable() {
44 | override def run(): Unit = {
45 | //该任务要打印的消息
46 | println("Now it is 60 seconds later")
47 | } //调度任务在从现在开始的 60 秒之后执行
48 | }, //创建一个 Runnable,以供调度稍后执行
49 | 60, TimeUnit.SECONDS)
50 | //...
51 | //一旦调度任务执行完成,就关闭 ScheduledExecutorService 以释放资源
52 | executor.shutdown()
53 | }
54 |
55 | /**
56 | * 代码清单 7-3 使用 EventLoop 调度任务
57 | */
58 | def scheduleViaEventLoop(): Unit = {
59 | val ch = CHANNEL_FROM_SOMEWHERE
60 | // get reference from somewhere
61 | val future = ch.eventLoop.schedule(
62 | new Runnable() {
63 | override def run(): Unit = { //要执行的代码
64 | System.out.println("60 seconds later")
65 | }
66 | }, //创建一个 Runnable以供调度稍后执行
67 | 60, TimeUnit.SECONDS)
68 | }
69 |
70 | /**
71 | * 代码清单 7-4 使用 EventLoop 调度周期性的任务
72 | */
73 | def scheduleFixedViaEventLoop(): Unit = {
74 | val ch = CHANNEL_FROM_SOMEWHERE
75 | val future = ch.eventLoop.scheduleAtFixedRate(new Runnable() {
76 | override def run(): Unit = { //这将一直运行,直到 ScheduledFuture 被取消
77 | System.out.println("Run every 60 seconds")
78 | } //调度在 60 秒之后,并且以后每间隔 60 秒运行
79 | }, 60, 60, TimeUnit.SECONDS)
80 | }
81 |
82 | /**
83 | * 代码清单 7-5 使用 ScheduledFuture 取消任务
84 | */
85 | def cancelingTaskUsingScheduledFuture(): Unit = {
86 | val ch = CHANNEL_FROM_SOMEWHERE
87 | //调度任务,并获得所返回的ScheduledFuture
88 | val future = ch.eventLoop.scheduleAtFixedRate(new Runnable() {
89 | override def run(): Unit = {
90 | System.out.println("Run every 60 seconds")
91 | }
92 | }, 60, 60, TimeUnit.SECONDS)
93 | // Some other code that runs...
94 | val mayInterruptIfRunning = false
95 | //取消该任务,防止它再次运行
96 | future.cancel(mayInterruptIfRunning)
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter8/BootstrapClient.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter8
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.buffer.ByteBuf
21 | import io.netty.channel._
22 | import io.netty.channel.nio.NioEventLoopGroup
23 | import io.netty.channel.socket.nio.NioSocketChannel
24 | import java.net.InetSocketAddress
25 |
26 | /**
27 | * 代码清单 8-1 引导一个客户端
28 | *
29 | * @author Norman Maurer
30 | * @author Marvin Wolfthal
31 | */
32 | object BootstrapClient {
33 | def main(args: Array[String]): Unit = {
34 | val client = new BootstrapClient
35 | client.bootstrap()
36 | }
37 | }
38 |
39 | class BootstrapClient {
40 | /**
41 | * 代码清单 8-1 引导一个客户端
42 | */
43 | def bootstrap(): Unit = {
44 | //设置 EventLoopGroup,提供用于处理 Channel 事件的 EventLoop
45 | val group: EventLoopGroup = new NioEventLoopGroup
46 | //创建一个Bootstrap类的实例以创建和连接新的客户端Channel
47 | val bootstrap = new Bootstrap
48 | bootstrap.group(group)
49 | //指定要使用的Channel 实现
50 | .channel(classOf[NioSocketChannel])
51 | //设置用于 Channel 事件和数据的ChannelInboundHandler
52 | .handler {
53 | new SimpleChannelInboundHandler[ByteBuf]() {
54 | @throws[Exception]
55 | override protected def channelRead0(
56 | channelHandlerContext: ChannelHandlerContext,
57 | byteBuf: ByteBuf): Unit = {
58 | println("Received data")
59 | }
60 | }
61 | }
62 | //连接到远程主机
63 | val future = bootstrap.connect(new InetSocketAddress("www.manning.com", 80))
64 | future.addListener(new ChannelFutureListener() {
65 | @throws[Exception]
66 | override def operationComplete(channelFuture: ChannelFuture): Unit = {
67 | if (channelFuture.isSuccess)
68 | println("Connection established")
69 | else {
70 | System.err.println("Connection attempt failed")
71 | channelFuture.cause.printStackTrace()
72 | }
73 | }
74 | })
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter8/BootstrapClientWithOptionsAndAttrs.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter8
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.buffer.ByteBuf
21 | import io.netty.channel.ChannelHandlerContext
22 | import io.netty.channel.ChannelOption
23 | import io.netty.channel.SimpleChannelInboundHandler
24 | import io.netty.channel.nio.NioEventLoopGroup
25 | import io.netty.channel.socket.nio.NioSocketChannel
26 | import io.netty.util.AttributeKey
27 | import java.net.InetSocketAddress
28 | import java.lang.{ Boolean ⇒ JBoolean }
29 |
30 | /**
31 | * 代码清单 8-7 使用属性值
32 | *
33 | * @author Norman Maurer
34 | */
35 | class BootstrapClientWithOptionsAndAttrs {
36 | /**
37 | * 代码清单 8-7 使用属性值
38 | */
39 | def bootstrap(): Unit = { //创建一个 AttributeKey 以标识该属性
40 | val id: AttributeKey[Integer] = AttributeKey.newInstance("ID")
41 | //创建一个 Bootstrap 类的实例以创建客户端 Channel 并连接它们
42 | val bootstrap = new Bootstrap
43 | //设置 EventLoopGroup,其提供了用以处理 Channel 事件的 EventLoop
44 | bootstrap.group(new NioEventLoopGroup)
45 | .channel(classOf[NioSocketChannel])
46 | .handler(new SimpleChannelInboundHandler[ByteBuf]() {
47 | @throws[Exception]
48 | override def channelRegistered(ctx: ChannelHandlerContext): Unit = { //使用 AttributeKey 检索属性以及它的值
49 | val idValue = ctx.channel.attr(id).get
50 | // do something with the idValue
51 | }
52 |
53 | @throws[Exception]
54 | override protected def channelRead0(channelHandlerContext: ChannelHandlerContext, byteBuf: ByteBuf): Unit = {
55 | System.out.println("Received data")
56 | }
57 | })
58 |
59 | //设置 ChannelOption,其将在 connect()或者bind()方法被调用时被设置到已经创建的 Channel 上
60 | bootstrap
61 | .option[JBoolean](ChannelOption.SO_KEEPALIVE, true)
62 | .option[Integer](ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
63 |
64 | //存储该 id 属性
65 | bootstrap.attr[Integer](id, 123456)
66 |
67 | //使用配置好的 Bootstrap 实例连接到远程主机
68 | val future = bootstrap.connect(new InetSocketAddress("www.manning.com", 80))
69 | future.syncUninterruptibly
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter8/BootstrapDatagramChannel.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter8
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.channel.ChannelFuture
21 | import io.netty.channel.ChannelFutureListener
22 | import io.netty.channel.ChannelHandlerContext
23 | import io.netty.channel.SimpleChannelInboundHandler
24 | import io.netty.channel.oio.OioEventLoopGroup
25 | import io.netty.channel.socket.DatagramPacket
26 | import io.netty.channel.socket.oio.OioDatagramChannel
27 | import java.net.InetSocketAddress
28 |
29 | /**
30 | * 代码清单 8-8 使用 Bootstrap 和 DatagramChannel
31 | *
32 | * @author Norman Maurer
33 | * @author Marvin Wolfthal
34 | */
35 | class BootstrapDatagramChannel {
36 | /**
37 | * 代码清单 8-8 使用 Bootstrap 和 DatagramChannel
38 | */
39 | def bootstrap(): Unit = {
40 | //创建一个 Bootstrap 的实例以创建和绑定新的数据报 Channel
41 | val bootstrap = new Bootstrap
42 | //设置 EventLoopGroup,其提供了用以处理 Channel 事件的 EventLoop
43 | bootstrap.group(new OioEventLoopGroup)
44 | //指定 Channel 的实现
45 | .channel(classOf[OioDatagramChannel])
46 | .handler(new SimpleChannelInboundHandler[DatagramPacket]() {
47 | @throws[Exception]
48 | override def channelRead0(ctx: ChannelHandlerContext, msg: DatagramPacket): Unit = {
49 | // Do something with the packet
50 | }
51 | })
52 |
53 | //调用 bind() 方法,因为该协议是无连接的
54 | val future = bootstrap.bind(new InetSocketAddress(0))
55 | future.addListener(new ChannelFutureListener() {
56 | @throws[Exception]
57 | override def operationComplete(channelFuture: ChannelFuture): Unit = {
58 | if (channelFuture.isSuccess)
59 | println("Channel bound")
60 | else {
61 | System.err.println("Bind attempt failed")
62 | channelFuture.cause.printStackTrace()
63 | }
64 | }
65 | })
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter8/BootstrapServer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter8
18 |
19 | import io.netty.bootstrap.ServerBootstrap
20 | import io.netty.buffer.ByteBuf
21 | import io.netty.channel.ChannelFuture
22 | import io.netty.channel.ChannelFutureListener
23 | import io.netty.channel.ChannelHandlerContext
24 | import io.netty.channel.SimpleChannelInboundHandler
25 | import io.netty.channel.nio.NioEventLoopGroup
26 | import io.netty.channel.socket.nio.NioServerSocketChannel
27 | import java.net.InetSocketAddress
28 |
29 | /**
30 | * 代码清单 8-4 引导服务器
31 | *
32 | * @author Norman Maurer
33 | * @author Marvin Wolfthal
34 | */
35 | class BootstrapServer {
36 | /**
37 | * 代码清单 8-4 引导服务器
38 | */
39 | def bootstrap(): Unit = {
40 | val group = new NioEventLoopGroup
41 | //创建 Server Bootstrap
42 | val bootstrap = new ServerBootstrap
43 | //设置 EventLoopGroup,其提供了用于处理 Channel 事件的EventLoop
44 | bootstrap.group(group)
45 | //指定要使用的 Channel 实现
46 | .channel(classOf[NioServerSocketChannel])
47 | //设置用于处理已被接受的子 Channel 的I/O及数据的 ChannelInboundHandler
48 | .childHandler {
49 | new SimpleChannelInboundHandler[ByteBuf]() {
50 | @throws[Exception]
51 | override protected def channelRead0(channelHandlerContext: ChannelHandlerContext, byteBuf: ByteBuf): Unit = {
52 | System.out.println("Received data")
53 | }
54 | }
55 | }
56 |
57 | //通过配置好的 ServerBootstrap 的实例绑定该 Channel
58 | val future = bootstrap.bind(new InetSocketAddress(8080))
59 | future.addListener(new ChannelFutureListener() {
60 | @throws[Exception]
61 | override def operationComplete(channelFuture: ChannelFuture): Unit = {
62 | if (channelFuture.isSuccess) System.out.println("Server bound")
63 | else {
64 | System.err.println("Bind attempt failed")
65 | channelFuture.cause.printStackTrace()
66 | }
67 | }
68 | })
69 | }
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter8/BootstrapSharingEventLoopGroup.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter8
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.bootstrap.ServerBootstrap
21 | import io.netty.buffer.ByteBuf
22 | import io.netty.channel.ChannelFuture
23 | import io.netty.channel.ChannelFutureListener
24 | import io.netty.channel.ChannelHandlerContext
25 | import io.netty.channel.SimpleChannelInboundHandler
26 | import io.netty.channel.nio.NioEventLoopGroup
27 | import io.netty.channel.socket.nio.NioServerSocketChannel
28 | import io.netty.channel.socket.nio.NioSocketChannel
29 | import java.net.InetSocketAddress
30 |
31 | /**
32 | * 代码清单 8-5 引导服务器
33 | *
34 | * @author Norman Maurer
35 | * @author Marvin Wolfthal
36 | */
37 | class BootstrapSharingEventLoopGroup {
38 | /**
39 | * 代码清单 8-5 引导服务器
40 | */
41 | def bootstrap(): Unit = { //创建 ServerBootstrap 以创建 ServerSocketChannel,并绑定它
42 | val bootstrap = new ServerBootstrap
43 | //设置 EventLoopGroup,其将提供用以处理 Channel 事件的 EventLoop
44 | bootstrap.group(new NioEventLoopGroup, new NioEventLoopGroup)
45 | //指定要使用的 Channel 实现
46 | .channel(classOf[NioServerSocketChannel])
47 | //设置用于处理已被接受的子 Channel 的 I/O 和数据的 ChannelInboundHandler
48 | .childHandler {
49 | new SimpleChannelInboundHandler[ByteBuf]() {
50 | private[chapter8] var connectFuture: ChannelFuture = _
51 |
52 | @throws[Exception]
53 | override def channelActive(ctx: ChannelHandlerContext): Unit = {
54 | //创建一个 Bootstrap 类的实例以连接到远程主机
55 | val bootstrap = new Bootstrap
56 | //指定 Channel 的实现
57 | bootstrap.channel(classOf[NioSocketChannel])
58 | .handler(new SimpleChannelInboundHandler[ByteBuf]() {
59 | @throws[Exception]
60 | override protected def channelRead0(
61 | ctx: ChannelHandlerContext,
62 | in: ByteBuf): Unit = {
63 | println("Received data")
64 | }
65 | })
66 | //使用与分配给已被接受的子Channel相同的EventLoop
67 | bootstrap.group(ctx.channel.eventLoop)
68 | //连接到远程节点
69 | connectFuture = bootstrap.connect(new InetSocketAddress("www.manning.com", 80))
70 | }
71 |
72 | @throws[Exception]
73 | override protected def channelRead0(
74 | channelHandlerContext: ChannelHandlerContext,
75 | byteBuf: ByteBuf): Unit = {
76 | if (connectFuture.isDone) {
77 | //当连接完成时,执行一些数据操作(如代理)
78 | // do something with the data
79 | }
80 | }
81 | }
82 | }
83 |
84 | //通过配置好的 ServerBootstrap 绑定该 ServerSocketChannel
85 | val future = bootstrap.bind(new InetSocketAddress(8080))
86 | future.addListener(new ChannelFutureListener() {
87 | @throws[Exception]
88 | override def operationComplete(channelFuture: ChannelFuture): Unit = {
89 | if (channelFuture.isSuccess) System.out.println("Server bound")
90 | else {
91 | System.err.println("Bind attempt failed")
92 | channelFuture.cause.printStackTrace()
93 | }
94 | }
95 | })
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter8/BootstrapWithInitializer.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter8
18 |
19 | import io.netty.bootstrap.ServerBootstrap
20 | import io.netty.channel._
21 | import io.netty.channel.nio.NioEventLoopGroup
22 | import io.netty.channel.socket.nio.NioServerSocketChannel
23 | import io.netty.handler.codec.http.HttpClientCodec
24 | import io.netty.handler.codec.http.HttpObjectAggregator
25 | import java.net.InetSocketAddress
26 |
27 | /**
28 | * 代码清单 8-6 引导和使用 ChannelInitializer
29 | *
30 | * @author Norman Maurer
31 | */
32 | class BootstrapWithInitializer {
33 | /**
34 | * 代码清单 8-6 引导和使用 ChannelInitializer
35 | */
36 | @throws[InterruptedException]
37 | def bootstrap(): Unit = {
38 | //创建 ServerBootstrap 以创建和绑定新的 Channel
39 | val bootstrap = new ServerBootstrap
40 |
41 | //设置 EventLoopGroup,其将提供用以处理 Channel 事件的 EventLoop
42 | bootstrap.group(new NioEventLoopGroup, new NioEventLoopGroup)
43 | .channel(classOf[NioServerSocketChannel]) //指定 Channel 的实现
44 | //注册一个 ChannelInitializerImpl 的实例来设置 ChannelPipeline
45 | .childHandler(new ChannelInitializerImpl)
46 |
47 | //绑定到地址
48 | val future = bootstrap.bind(new InetSocketAddress(8080))
49 | future.sync
50 | }
51 |
52 | //用以设置 ChannelPipeline 的自定义 ChannelInitializerImpl 实现
53 | final private[chapter8] class ChannelInitializerImpl extends ChannelInitializer[Channel] {
54 | //将所需的 ChannelHandler 添加到 ChannelPipeline
55 | @throws[Exception]
56 | override protected def initChannel(ch: Channel): Unit = {
57 | val pipeline = ch.pipeline
58 | pipeline.addLast(new HttpClientCodec)
59 | pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE))
60 | }
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter8/GracefulShutdown.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter8
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.buffer.ByteBuf
21 | import io.netty.channel._
22 | import io.netty.channel.nio.NioEventLoopGroup
23 | import io.netty.channel.socket.nio.NioSocketChannel
24 | import io.netty.util.concurrent.Future
25 | import java.net.InetSocketAddress
26 |
27 | /**
28 | * 代码清单 8-9 优雅关闭
29 | *
30 | * @author Norman Maurer
31 | * @author Marvin Wolfthal
32 | */
33 | object GracefulShutdown {
34 | def main(args: Array[String]): Unit = {
35 | val client = new GracefulShutdown
36 | client.bootstrap()
37 | }
38 | }
39 |
40 | class GracefulShutdown {
41 | /**
42 | * 代码清单 8-9 优雅关闭
43 | */
44 | def bootstrap(): Unit = {
45 | //创建处理 I/O 的EventLoopGroup
46 | val group = new NioEventLoopGroup
47 |
48 | //创建一个 Bootstrap 类的实例并配置它
49 | val bootstrap = new Bootstrap
50 | bootstrap.group(group)
51 | .channel(classOf[NioSocketChannel])
52 | .handler(
53 | new SimpleChannelInboundHandler[ByteBuf]() {
54 | @throws[Exception]
55 | override protected def channelRead0(channelHandlerContext: ChannelHandlerContext, byteBuf: ByteBuf): Unit = {
56 | System.out.println("Received data")
57 | }
58 | }
59 | )
60 | bootstrap.connect(new InetSocketAddress("www.manning.com", 80)).syncUninterruptibly()
61 |
62 | //shutdownGracefully()方法将释放所有的资源,并且关闭所有的当前正在使用中的 Channel
63 | val future = group.shutdownGracefully()
64 | // block until the group has shutdown
65 | future.syncUninterruptibly()
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/scala/nia/chapter8/InvalidBootstrapClient.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 netty.reactiveplatform.xyz
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package nia.chapter8
18 |
19 | import io.netty.bootstrap.Bootstrap
20 | import io.netty.buffer.ByteBuf
21 | import io.netty.channel.ChannelHandlerContext
22 | import io.netty.channel.SimpleChannelInboundHandler
23 | import io.netty.channel.nio.NioEventLoopGroup
24 | import io.netty.channel.socket.oio.OioSocketChannel
25 | import java.net.InetSocketAddress
26 |
27 | /**
28 | * 代码清单 8-3 不兼容的 Channel 和 EventLoopGroup
29 | *
30 | * @author Norman Maurer
31 | */
32 | object InvalidBootstrapClient {
33 | def main(args: Array[String]): Unit = {
34 | val client = new InvalidBootstrapClient
35 | client.bootstrap()
36 | }
37 | }
38 |
39 | class InvalidBootstrapClient {
40 | /**
41 | * 代码清单 8-3 不兼容的 Channel 和 EventLoopGroup
42 | */
43 | def bootstrap(): Unit = {
44 | val group = new NioEventLoopGroup
45 | //创建一个新的 Bootstrap 类的实例,以创建新的客户端Channel
46 | val bootstrap = new Bootstrap
47 | //指定一个适用于 NIO 的 EventLoopGroup 实现
48 | bootstrap.group(group)
49 | //指定一个适用于 OIO 的 Channel 实现类
50 | .channel(classOf[OioSocketChannel])
51 | //设置一个用于处理 Channel的 I/O 事件和数据的 ChannelInboundHandler
52 | .handler {
53 | new SimpleChannelInboundHandler[ByteBuf]() {
54 | @throws[Exception]
55 | override protected def channelRead0(channelHandlerContext: ChannelHandlerContext, byteBuf: ByteBuf): Unit = {
56 | println("Received data")
57 | }
58 | }
59 | }
60 | //尝试连接到远程节点
61 | val future = bootstrap.connect(new InetSocketAddress("www.manning.com", 80))
62 | future.syncUninterruptibly
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/paradox/chapter-01/index.md:
--------------------------------------------------------------------------------
1 | # 第1章——Netty——异步和事件驱动
2 |
3 | ## 代码清单 1-1 阻塞 I/O 示例
4 |
5 | Java
6 | : @@snip[代码清单1-1](../../main/java/nia/chapter1/javadsl/BlockingIoExample.java){ #snip }
7 |
8 | Scala
9 | : @@snip[代码清单1-1](../../main/scala/nia/chapter1/scaladsl/BlockingIoExample.scala){ #snip }
10 |
11 |
--------------------------------------------------------------------------------
/src/paradox/contents.md:
--------------------------------------------------------------------------------
1 |
2 | # 目录
3 |
4 | @@@ index
5 |
6 | * [勘误](errata/index.md)
7 | * [第一章——Netty——异步和事件驱动](chapter-01/index.md)
8 |
9 | @@@
--------------------------------------------------------------------------------
/src/paradox/errata/index.md:
--------------------------------------------------------------------------------
1 | ## 《Netty实战》勘误
2 |
3 | #### 1 页码:ii • 行数:11 • 印次 1 • 修订印次: 3
4 |
5 | `COBAL` 应该是 `COBOL`.
6 |
7 | ---
8 |
9 | #### 2 页码:前言 • 行数:2 • 印次 1 • 修订印次: 2
10 |
11 | “当我从2001年年末” 中的“2001年”应该是“2011年”
12 |
13 | ---
14 |
15 | #### 3 页码:5 • 行数:1 • 印次 1 • 修订印次: 2
16 |
17 | “44 代码清单1-1 ”中的“44”是排版问题,要去掉
18 |
19 | ---
20 |
21 | #### 4 页码:12 • 行数:10 • 印次 1
22 |
23 | `如图 1-3 所示的那些`: 原文是包含 those的,这里改为 `如图 1-3 所示`,即省译
24 |
25 | ---
26 |
27 | #### 5 页码:13 • 行数:8 • 印次 1 • 修订印次: 2
28 |
29 | “你可能有的在你的 ChannelHandler 中需要进行同步的任何顾虑”一句
30 |
31 | 这里是按照原文翻译的,将“将你的 ChannelHandler”改为了“ChannelHandler 实现”更有利于读者理解。
32 |
33 | ---
34 |
35 | #### 6 页码:18 • 行数:最上面代码第6-7行 • 印次 1 • 修订印次: 3
36 |
37 | 将未决消息(添加译者注)冲刷到远程节点,并关闭该`Channel`。
38 | //译者注
39 | 未决消息(pending message)是指目前暂存于`ChannelOutboundBuffer`中的消息,在下一次调用`flush()`或者`writeAndFlush()`方法时将会尝试写出到套接字。
40 |
41 | ---
42 |
43 | #### 7 页码:19 • 行数:20 • 印次 1 • 修订印次: 2
44 |
45 | ```java
46 | if (args.length != 1) {
47 | System.err.println("Usage: " + EchoServer.class.getSimpleName() + " ");
48 | }
49 | ```
50 | 应该是:
51 | ```java
52 | if (args.length != 1) {
53 | System.err.println("Usage: " + EchoServer.class.getSimpleName() + " ");
54 | return;
55 | }
56 | ```
57 |
58 | 即需要按照下面的代码清单处理,本书英文版也有这个问题,感谢你哈。
59 | 其中 `return;` 属于 `if`语句,并且需正确对齐
60 |
61 | ---
62 |
63 | #### 8 页码:33 • 行数:2 • 印次 1 • 修订印次: 3
64 |
65 | ChannelPipeline提供了 ChannelHandler链的容器
66 |
67 | 改为
68 | `ChannelPipeline` 为 `ChannelHandler` 链提供了容器
69 |
70 | 调整句式,更加方便读者阅读习惯。
71 |
72 | ---
73 |
74 | #### 9 页码:37 • 行数:6 • 印次 1
75 |
76 | 拆句+补译:
77 |
78 | 与 ServerChannel 相关联的 EventLoopGroup 将分配一个负责为传入连接请求创建Channel 的 EventLoop。
79 |
80 | 改为:
81 |
82 | 与 ServerChannel 相关联的 EventLoopGroup 将分配一个 EventLoop,该EventLoop将负责为传入连接请求创建Channel。
83 |
84 | ---
85 |
86 | #### 10 页码:50 • 行数:1 • 印次 1 • 修订印次: 3
87 |
88 | 在首次出现`X`和`--`时,应加译者注说明X和--所代表的意思
89 |
90 | 表中X表示支持,--表示不支持。——译者注
91 |
92 | ---
93 |
94 | #### 11 页码:53 • 行数:19 • 印次 1 • 修订印次: 2
95 |
96 | 此处不能算勘误,算是改进。
97 |
98 | “试图移动 `写索引` 超过这个值将会触发一个异常”中的`写索引` 改为 `写索引(即writerIndex)` 更加有利于读者理解。
99 |
100 | #### 12 页码:70 • 行数:倒数第2行 • 印次 1
101 |
102 | 这里系原书中就存在的一个技术错误,或者说表意不明。
103 | The Channel was created, but isn’t registered to an EventLoop
104 | 修改为:
105 |
106 | Channel 已经从EventLoop中注销了。
107 |
108 | 添加译者注:只要该Channel没有关闭,我们就可以再次将该Channel注册到EventLoop。
109 |
110 | ---
111 |
112 | #### 13 页码:78 • 行数:表6-6 第一行 • 印次 1 • 修订印次: 3
113 |
114 | 表6-6第1行左边应该是:
115 | addFirst
116 |
117 | addBefore
118 |
119 | addAfter
120 |
121 | addLast
122 |
123 | 目前全部挤在一起了,且第一个AddFirst应该是addFirst
124 |
125 | ---
126 |
127 | #### 14 页码:78 • 行数:4 • 印次 1 • 修订印次: 3
128 |
129 | `ChannelHandler` 可以通过添加、删除……的布局。
130 |
131 | 有歧义,修改为:
132 |
133 | 通过调用 `ChannelPipeline`上的相关方法,`ChannelHandler`可以添加、
134 | 删除或者替换其他的`ChannelHandler`,从而实时地修改 `ChannelPipeline`的布局。
135 |
136 | ---
137 |
138 | #### 15 页码:78 • 行数:7 • 印次 1 • 修订印次: 3
139 |
140 | 页码:78 • 行数:7 • 印次 1 • 修订印次: 3
141 |
142 | 表 6-6 `ChannelHandler`的用于修改`ChannelPipeline`的方法
143 |
144 | 系原文有误:
145 |
146 | Table 6.6 ChannelHandler methods for modifying a ChannelPipeline
147 |
148 | 应该修改为:
149 |
150 | `ChannelPipeline`上的相关方法,由`ChannelHandler`用来修改`ChannelPipeline` 的布局
151 |
152 | ---
153 |
154 | #### 16 页码:82 • 行数:7 • 印次 1 • 修订印次: 3
155 |
156 | ChannelHandler Context多了一个空格,应该是ChannelHandlerContext
157 |
158 | ---
159 |
160 | #### 17 页码:85 • 行数:2 • 印次 1 • 修订印次: 2
161 |
162 | 这里不算勘误,算改进,以帮助读者理解:
163 |
164 | “用于这种用法的 ChannelHandler 必须要使用 @Sharable 注解标注。”
165 |
166 | 改进为:
167 |
168 | “对于这种用法(指在多个`ChannelPipeline`中共享同一个`ChannelHandler`),对应的`ChannelHandler`必须要使用 `@Sharable` 注解标注。”
169 |
170 | ---
171 |
172 | #### 18 页码:93 • 行数:27 • 印次 1 • 修订印次: 2
173 |
174 | 类型和变量之间缺少空格
175 |
176 | 错误:ThreadFactorythreadFactory
177 |
178 | 应该是ThreadFactory threadFactory
179 |
180 | ---
181 |
182 | #### 19 页码:105 • 行数:25 • 印次 1 • 修订印次: 2
183 |
184 | 这里更多的还是考虑到了对仗,但是从技术性描述来说的确不是特别准确。
185 |
186 | 首先出现
187 | `Accepted Channel` 的时候,是在英文原版39页,描述引导的时候,这里,我们可以看到前面一会儿使用了`connection has been accepted`一会儿又是`Channel`,所以从很早很早开始,这两个词几乎就在混用了和相互指代了,即在描述性的文字中,二者是一个意思。
188 |
189 | 同样在原书49页也有
190 | `A new Channel was accepted and is ready`,而并不是 `A new Connection was accepted and is ready`.
191 |
192 | 在原书50页:
193 | `OP_ACCEPT Requests notification when a new connection is accepted, and a Channel is created.`
194 |
195 | 这里有您的论调的来源:)
196 |
197 | 然后我们将目光转到113页,这里文中前面也有出现过` accepted Channel`,后面出现了`accepting`(原书为斜体) ServerChannel,即我们不要逐一输入,而要替换为上下文对象。
198 |
199 | 其他出现`accepting`的地方的几处是` accepting connections `以及` accepting new connections `。
200 |
201 | 所以这里我们更多第可以看做是对仗。
202 |
203 |
204 | 当然求翻译准确性,我们的确可以修改,我建议改为下面的形式。
205 |
206 | `接受(斜体)(子 Channel )的 ServerChannel。
207 | //添加译者注
208 | //实际上是指接受来自客户端的连接,在连被接接受之后,该 `ServerChannel`将会创建一个对应的子 `Channel`。
209 |
210 | ---
211 |
212 | #### 20 页码:110 • 行数:24 • 印次 1 • 修订印次: 3
213 |
214 | 代码清单8-7中的:
215 |
216 | new AttributeKey("ID")
217 |
218 | 应该是:
219 |
220 | AttributeKey.newInstance("ID")(添加译者注)
221 |
222 | // 需要注意的是,`AttributeKey`上同时存在`newInstance(String)`和`valueOf(String)`方法,它们都可以用来获取具有指定名称的`AttributeKey`实例,不同的是,前者可能会在多线程环境下使用时抛出异常(实际上调用了`createOrThrow(String)`方法)——通常适用于初始化静态变量的时候;而后者(实际上调用了`getOrCreate(String)`方法)则更加通用(线程安全)。——译者注
223 |
224 | (系原书代码清单错误,默认的构造函数是private的,在本书中文版源代码下载链接https://github.com/ReactivePlatform/netty-in-action-cn中是正确的。)
225 |
226 | ---
227 |
228 | #### 21 页码:115 • 行数:9-1 • 印次 1 • 修订印次: 3
229 |
230 | 图9-2,最后应该是 ABC - DEF - GHI
231 |
232 | ---
233 |
234 | #### 22 页码:131 • 行数:20 • 印次 1 • 修订印次: 2
235 |
236 | `原子类型` 改为 `原始类型`
237 |
238 | ---
239 |
240 | #### 23 页码:132 • 行数:图10-4 • 印次 1 • 修订印次: 3
241 |
242 | 图中有误:
243 |
244 | 1. ChannelInboundHandler 需要改为
245 | ChannelOutboundHandler
246 |
247 | 2.需要补充一条箭头线。
248 |
249 | [img](http://file.epubit.com.cn/ScreenShow/170547ba3b449b3359e4)
250 |
251 | ---
252 |
253 | #### 24 页码:147 • 行数:表11-3 • 印次 1
254 |
255 | 不算勘误,译法改进:
256 |
257 | “数据帧:属于上一个 BinaryWebSocketFrame 或者 TextWebSocketFrame的文本的或者二进制数据”
258 |
259 |
260 |
261 | 这里我们改进为,按照:https://github.com/ReactivePlatform/netty-in-action-cn/issues/6
262 |
263 |
264 |
265 | “数据帧:属于上一个 BinaryWebSocketFrame 或者 TextWebSocketFrame的二进制或者文本数据”
266 |
267 | ---
268 |
269 | #### 25 页码:166 • 行数:37 • 印次 1 • 修订印次: 3
270 |
271 | text/plain 中的 plain
272 |
273 | 应该是
274 |
275 | html
276 |
277 | 这样显示才正确,代码清单中英文版都已经更新
278 |
279 | ---
280 |
281 | #### 26 页码:173 • 行数:24 • 印次 1 • 修订印次: 3
282 |
283 | 补充说明:
284 |
285 | 第二个客户端则是通过……连接的(添加译者注)。
286 |
287 | // 也可以通过在一个新的浏览器中访问 http://localhost:9999 来达到同样的目的,从而代替 Chrome 浏览器的开发者工具。——译者注
288 |
289 | ---
290 |
291 | #### 27 页码:174 • 行数:倒数第2行 • 印次 5
292 |
293 | SSLEng.ine 应该是 SSLEngine
294 |
295 | ---
296 |
297 | #### 28 页码:239 • 行数:24 • 印次 1
298 |
299 | 不算勘误,译法改进
300 |
301 | “不被引用了这个项目所产生的构件的其他项目,视为传递依赖。”
302 |
303 | 修改为
304 |
305 | “不会被其他项目视为传递依赖,这些项目引用了由这个项目所生成的构件。”
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
--------------------------------------------------------------------------------
/src/paradox/index.md:
--------------------------------------------------------------------------------
1 | @@toc { depth=2 }
2 |
3 | 《Netty实战》
4 |
5 | Netty实战是一本写给Java和Scala开发者的Netty书。
6 |
7 | @@@ index
8 |
9 | * [目录](contents.md)
10 |
11 | @@@
--------------------------------------------------------------------------------