├── .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 | @@@ --------------------------------------------------------------------------------