Initial commit
This commit is contained in:
33
learn-netty4/.gitignore
vendored
Normal file
33
learn-netty4/.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
HELP.md
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**
|
||||
!**/src/test/**
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
.DS_Store
|
||||
202
learn-netty4/LICENSE.txt
Normal file
202
learn-netty4/LICENSE.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
34
learn-netty4/NOTICE.txt
Normal file
34
learn-netty4/NOTICE.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
Learn Netty4 Project
|
||||
=================
|
||||
|
||||
Please visit the flydean web site for more information:
|
||||
|
||||
* http://www.flydean.com/
|
||||
|
||||
Copyright 2022 learn-netty4 Project
|
||||
|
||||
The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
version 2.0 (the "License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at:
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Also, please refer to each LICENSE.<component>.txt file, which is located in
|
||||
the 'license' directory of the distribution file, for the license terms of the
|
||||
components that this product depends on.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
This project contains a modified portion of 'Netty Project',an asynchronous
|
||||
event-driven network application framework,which can be obtained at:
|
||||
|
||||
* LICENSE:
|
||||
* license/LICENSE.netty.txt (Apache License 2.0)
|
||||
* HOMEPAGE:
|
||||
* https://github.com/netty/netty
|
||||
76
learn-netty4/README.md
Normal file
76
learn-netty4/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# learn-netty4
|
||||
跟我一起学netty4.X
|
||||
|
||||
## LICENSE
|
||||
|
||||
Copyright 2022 learn-netty4 Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
This project contains a modified portion of 'Netty Project',an asynchronous
|
||||
event-driven network application framework,which can be obtained at:
|
||||
|
||||
* LICENSE:
|
||||
* license/LICENSE.netty.txt (Apache License 2.0)
|
||||
* HOMEPAGE:
|
||||
* https://github.com/netty/netty
|
||||
|
||||
## 本项目的内容
|
||||
|
||||
毕生的收藏都在这了,看得上的小伙伴点个star吧 
|
||||
|
||||
系列文章请参考:[netty系列教程](http://www.flydean.com/category/%e5%93%8d%e5%ba%94%e5%bc%8f%e7%b3%bb%e7%bb%9f/netty/)
|
||||
|
||||
|
||||
文章列表如下:
|
||||
|
||||
* [01 Netty Startup](http://www.flydean.com/01-netty-startup)
|
||||
* [02 Netty Bytebuf](http://www.flydean.com/02-netty-bytebuf)
|
||||
* [03 Netty Architecture](http://www.flydean.com/03-netty-architecture)
|
||||
* [04 Netty Channel](http://www.flydean.com/04-netty-Channel)
|
||||
* [05 Netty Channel Event](http://www.flydean.com/05-netty-ChannelEvent)
|
||||
* [06 Netty Cheerup China](http://www.flydean.com/06-netty-cheerup-china)
|
||||
* [07 Netty Stream Based Transport](http://www.flydean.com/07-netty-stream-based-transport)
|
||||
* [08 Netty Pojo Buf](http://www.flydean.com/08-netty-pojo-buf)
|
||||
* [09 Netty Reconnect](http://www.flydean.com/09-netty-reconnect)
|
||||
* [10 Netty Chat](http://www.flydean.com/10-netty-chat)
|
||||
* [11 Netty Udp](http://www.flydean.com/11-netty-udp)
|
||||
* [12 Netty Securechat](http://www.flydean.com/12-netty-securechat)
|
||||
* [13 Netty Customprotocol](http://www.flydean.com/13-netty-customprotocol)
|
||||
* [14 Netty Cust Codec](http://www.flydean.com/14-netty-cust-codec)
|
||||
* [15 Netty Buildin Frame Detection](http://www.flydean.com/15-netty-buildin-frame-detection)
|
||||
* [16 Netty Buildin Codec Common](http://www.flydean.com/16-netty-buildin-codec-common)
|
||||
* [17 Netty Protobuf](http://www.flydean.com/17-netty-protobuf)
|
||||
* [18 Netty Http Request](http://www.flydean.com/18-netty-http-request)
|
||||
* [19 Netty Http Client Request](http://www.flydean.com/19-netty-http-client-request)
|
||||
* [20 Netty Fileserver](http://www.flydean.com/20-netty-fileserver)
|
||||
* [21 Netty Http Fileupload](http://www.flydean.com/21-netty-http-fileupload)
|
||||
* [22 Netty Cors](http://www.flydean.com/22-netty-cors)
|
||||
* [23 Netty Websocket Server](http://www.flydean.com/23-netty-websocket-server)
|
||||
* [24 Netty Websocket Server 2](http://www.flydean.com/24-netty-websocket-server2)
|
||||
* [25 Netty Websocket Client](http://www.flydean.com/25-netty-websocket-client)
|
||||
* [26 Netty Secure Http 2](http://www.flydean.com/26-netty-secure-http2)
|
||||
* [27 Netty Http 2](http://www.flydean.com/27-netty-http2)
|
||||
* [28 Netty Wrap Http 2](http://www.flydean.com/28-netty-wrap-http2)
|
||||
* [29 Netty Flowcontrol](http://www.flydean.com/29-netty-flowcontrol)
|
||||
* [30 Netty Http 2 Client](http://www.flydean.com/30-netty-http2client)
|
||||
* [31 Netty Framecodec Http 2](http://www.flydean.com/31-netty-framecodec-http2)
|
||||
* [32 Netty Http 2 Client Framecodec](http://www.flydean.com/32-netty-http2client-framecodec)
|
||||
* [33 Netty Multiplex Http 2 Server](http://www.flydean.com/33-netty-multiplex-http2server)
|
||||
* [34 Netty Multiple Server](http://www.flydean.com/34-netty-multiple-server)
|
||||
* [35 Netty Simple Proxy](http://www.flydean.com/35-netty-simple-proxy)
|
||||
* [36 Netty Socks Support](http://www.flydean.com/36-netty-socks-support)
|
||||
* [37 Netty Cust Socks Server](http://www.flydean.com/37-netty-cust-socks-server)
|
||||
* [38 Netty Cust Port Unification](http://www.flydean.com/38-netty-cust-port-unification)
|
||||
|
||||
1440
learn-netty4/file.txt
Normal file
1440
learn-netty4/file.txt
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
20
learn-netty4/license/LICENSE.netty.txt
Normal file
20
learn-netty4/license/LICENSE.netty.txt
Normal file
@@ -0,0 +1,20 @@
|
||||
The Netty Project
|
||||
=================
|
||||
|
||||
Please visit the Netty web site for more information:
|
||||
|
||||
* https://netty.io/
|
||||
|
||||
Copyright 2014 The Netty Project
|
||||
|
||||
The Netty Project licenses this file to you under the Apache License,
|
||||
version 2.0 (the "License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at:
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
131
learn-netty4/pom.xml
Normal file
131
learn-netty4/pom.xml
Normal file
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>learn-netty</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<logback.version>1.2.11</logback.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.77.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.24</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-access</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- google protobuf -->
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java</artifactId>
|
||||
<version>3.20.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.protobuf</groupId>
|
||||
<artifactId>protobuf-java-util</artifactId>
|
||||
<version>3.20.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jboss marshalling-->
|
||||
<dependency>
|
||||
<groupId>org.jboss.marshalling</groupId>
|
||||
<artifactId>jboss-marshalling</artifactId>
|
||||
<version>2.0.12.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.marshalling</groupId>
|
||||
<artifactId>jboss-marshalling-serial</artifactId>
|
||||
<version>2.0.12.Final</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.marshalling</groupId>
|
||||
<artifactId>jboss-marshalling-river</artifactId>
|
||||
<version>2.0.12.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.conscrypt</groupId>
|
||||
<artifactId>conscrypt-openjdk-uber</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/io.netty/netty-tcnative -->
|
||||
<!-- This artifact is dynamically linked to OpenSSL and Apache APR.-->
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative</artifactId>
|
||||
<version>2.0.52.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative-boringssl-static</artifactId>
|
||||
<version>2.0.52.Final</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.barchart.udt</groupId>
|
||||
<artifactId>barchart-udt-core</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.barchart.udt</groupId>
|
||||
<artifactId>barchart-udt-bundle</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.openhft</groupId>
|
||||
<artifactId>affinity</artifactId>
|
||||
<version>3.20.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.netty</groupId>-->
|
||||
<!-- <artifactId>netty-transport-native-kqueue</artifactId>-->
|
||||
<!-- <version>4.1.70.Final</version>-->
|
||||
<!-- <classifier>osx-x86_64</classifier>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- OCSP 依赖-->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<version>1.70</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>1.70</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean01.first;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
|
||||
/**
|
||||
* 第一个netty的客户端
|
||||
*/
|
||||
public final class FirstClient {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(new FirstClientHandler());
|
||||
}
|
||||
});
|
||||
|
||||
// 连接服务器
|
||||
ChannelFuture f = b.connect(HOST, PORT).sync();
|
||||
|
||||
// 等待关闭
|
||||
f.channel().closeFuture().sync();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean01.first;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Handles a client-side channel.
|
||||
*/
|
||||
@Slf4j
|
||||
public class FirstClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private ByteBuf content;
|
||||
private ChannelHandlerContext ctx;
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
this.ctx = ctx;
|
||||
content = ctx.alloc().directBuffer(256).writeBytes("Hello flydean.com".getBytes(StandardCharsets.UTF_8));
|
||||
// 发送消息
|
||||
sayHello();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private void sayHello() {
|
||||
// 向服务器输出消息
|
||||
ctx.writeAndFlush(content.retain());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean01.first;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version FirstServer, 2021/8/1
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class FirstServer {
|
||||
|
||||
private final int port;
|
||||
|
||||
public void start() throws InterruptedException {
|
||||
//建立两个EventloopGroup用来处理连接和消息
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup();
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast(new FirstServerHandler());
|
||||
}
|
||||
})
|
||||
.option(ChannelOption.SO_BACKLOG, 128)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true);
|
||||
|
||||
// 绑定端口并开始接收连接
|
||||
ChannelFuture f = b.bind(port).sync();
|
||||
// 等待server socket关闭
|
||||
f.channel().closeFuture().sync();
|
||||
} finally {
|
||||
//关闭group
|
||||
workerGroup.shutdownGracefully();
|
||||
bossGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
int port=8000;
|
||||
new FirstServer(port).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean01.first;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version FirstServer, 2021/8/1
|
||||
*/
|
||||
@Slf4j
|
||||
public class FirstServerHandler extends ChannelInboundHandlerAdapter {
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
// 对消息进行处理
|
||||
ByteBuf in = (ByteBuf) msg;
|
||||
try {
|
||||
log.info("收到消息:{}",in.toString(io.netty.util.CharsetUtil.US_ASCII));
|
||||
}finally {
|
||||
ReferenceCountUtil.release(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean02.bytebuf;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static io.netty.buffer.Unpooled.*;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version ByteBufUsage, 2021/8/2
|
||||
*/
|
||||
public class ByteBufUsage {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//创建ByteBuf
|
||||
ByteBuf heapBuffer = buffer(128);
|
||||
ByteBuf directBuffer = directBuffer(256);
|
||||
ByteBuf wrappedBuffer = wrappedBuffer(new byte[128], new byte[256]);
|
||||
|
||||
//随机访问
|
||||
ByteBuf buffer = heapBuffer;
|
||||
for (int i = 0; i < buffer.capacity(); i ++) {
|
||||
byte b = buffer.getByte(i);
|
||||
System.out.println((char) b);
|
||||
}
|
||||
|
||||
//遍历readable bytes
|
||||
while (directBuffer.isReadable()) {
|
||||
System.out.println(directBuffer.readByte());
|
||||
}
|
||||
|
||||
//写入writable bytes
|
||||
while (wrappedBuffer.maxWritableBytes() >= 4) {
|
||||
wrappedBuffer.writeInt(new Random().nextInt());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean03.handler;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Handles a server-side channel.
|
||||
*/
|
||||
@Slf4j
|
||||
public class MyHandler extends SimpleChannelInboundHandler<Object> {
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
// 对消息进行处理
|
||||
ByteBuf in = (ByteBuf) msg;
|
||||
try {
|
||||
log.info("收到消息:{}",in.toString(io.netty.util.CharsetUtil.US_ASCII));
|
||||
}finally {
|
||||
ReferenceCountUtil.release(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
//异常处理
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean06.cheerup;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
/**
|
||||
* 收到Client的消息之后会输出"加油"
|
||||
*/
|
||||
public final class CheerUpServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
|
||||
static final int SIZE = Integer.parseInt(System.getProperty("size", "20"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// Server配置
|
||||
//boss loop
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
//worker loop
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
final CheerUpServerHandler serverHandler = new CheerUpServerHandler();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
// tcp/ip协议listen函数中的backlog参数,等待连接池的大小
|
||||
.option(ChannelOption.SO_BACKLOG, 100)
|
||||
//日志处理器
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
//初始化channel,添加handler
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
//日志处理器
|
||||
p.addLast(new LoggingHandler(LogLevel.INFO));
|
||||
p.addLast(serverHandler);
|
||||
}
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
ChannelFuture f = b.bind(PORT).sync();
|
||||
|
||||
// 等待channel关闭
|
||||
f.channel().closeFuture().sync();
|
||||
} finally {
|
||||
// 关闭所有的event loop
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean06.cheerup;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 加油服务器的处理器
|
||||
*/
|
||||
@Slf4j
|
||||
@Sharable
|
||||
public class CheerUpServerHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private ByteBuf message;
|
||||
|
||||
public CheerUpServerHandler(){
|
||||
message = Unpooled.buffer(CheerUpServer.SIZE);
|
||||
message.writeBytes("加油!".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
log.info("收到消息:{}",msg);
|
||||
log.info("服务器端收到消息:{}",((ByteBuf)msg).toString(StandardCharsets.UTF_8));
|
||||
log.info("可读字节:{},readerIndex:{}",message.readableBytes(),message.readerIndex());
|
||||
log.info("可写字节:{},writerIndex:{}",message.writableBytes(),message.writerIndex());
|
||||
|
||||
// message = Unpooled.buffer(CheerUpServer.SIZE);
|
||||
// message.writeBytes("加油!".getBytes(StandardCharsets.UTF_8));
|
||||
message.retain();
|
||||
ctx.writeAndFlush(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean06.cheerup;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
/**
|
||||
* 客户端
|
||||
*/
|
||||
public final class ChinaClient {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
|
||||
static final int SIZE = Integer.parseInt(System.getProperty("size", "20"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// 客户端的eventLoop
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
//添加日志处理器
|
||||
p.addLast(new LoggingHandler(LogLevel.INFO));
|
||||
p.addLast(new ChinaClientHandler());
|
||||
}
|
||||
});
|
||||
|
||||
// 启动客户端
|
||||
ChannelFuture f = b.connect(HOST, PORT).sync();
|
||||
|
||||
// 等待channel关闭
|
||||
f.channel().closeFuture().sync();
|
||||
} finally {
|
||||
// 关闭所有的event loop
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean06.cheerup;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 加油服务的客户端,
|
||||
*/
|
||||
@Slf4j
|
||||
public class ChinaClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private ByteBuf message;
|
||||
|
||||
/**
|
||||
* 客户端处理器
|
||||
*/
|
||||
public ChinaClientHandler() {
|
||||
message = Unpooled.buffer(ChinaClient.SIZE);
|
||||
message.writeBytes("中国".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
log.info("可读字节:{},readerIndex:{}",message.readableBytes(),message.readerIndex());
|
||||
log.info("可写字节:{},writerIndex:{}",message.writableBytes(),message.writerIndex());
|
||||
log.info("capacity:{},refCnt{}",message.capacity(),message.refCnt());
|
||||
message.retain();
|
||||
ctx.writeAndFlush(message);
|
||||
// ctx.writeAndFlush("中国");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
log.info("客户端收到消息:{}",((ByteBuf)msg).toString(StandardCharsets.UTF_8));
|
||||
log.info("可读字节:{},readerIndex:{}",message.readableBytes(),message.readerIndex());
|
||||
log.info("可写字节:{},writerIndex:{}",message.writableBytes(),message.writerIndex());
|
||||
log.info("capacity:{},refCnt{}",message.capacity(),message.refCnt());
|
||||
|
||||
log.info("可读字节:{},readerIndex:{}",message.readableBytes(),message.readerIndex());
|
||||
log.info("可写字节:{},writerIndex:{}",message.writableBytes(),message.writerIndex());
|
||||
// message = Unpooled.buffer(ChinaClient.SIZE);
|
||||
// message.writeBytes("中国".getBytes(StandardCharsets.UTF_8));
|
||||
message.retain();
|
||||
ctx.writeAndFlush(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean08.pojo;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.serialization.ClassResolvers;
|
||||
import io.netty.handler.codec.serialization.ObjectDecoder;
|
||||
import io.netty.handler.codec.serialization.ObjectEncoder;
|
||||
|
||||
/**
|
||||
* Pojo对象的client
|
||||
*/
|
||||
public final class PojoClient {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
|
||||
static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(
|
||||
// 添加encoder和decoder
|
||||
new ObjectEncoder(),
|
||||
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
|
||||
new PojoClientHandler());
|
||||
}
|
||||
});
|
||||
|
||||
// 建立连接
|
||||
b.connect(HOST, PORT).sync().channel().closeFuture().sync();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean08.pojo;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.channel.ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE;
|
||||
|
||||
/**
|
||||
* client端处理器,
|
||||
*/
|
||||
@Slf4j
|
||||
public class PojoClientHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final List<Integer> firstMessage;
|
||||
|
||||
/**
|
||||
* 初始化handler
|
||||
*/
|
||||
public PojoClientHandler() {
|
||||
firstMessage = new ArrayList<>(PojoClient.SIZE);
|
||||
for (int i = 0; i < PojoClient.SIZE; i ++) {
|
||||
firstMessage.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
// 在channel active的时候发送消息
|
||||
ChannelFuture future = ctx.writeAndFlush("中国");
|
||||
// 将ChannelFuture中的Throwable转发到ChannelPipeline中。
|
||||
future.addListener(FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
// 将消息写回channel
|
||||
log.info("客户端收到对象:{}",msg);
|
||||
ctx.write(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean08.pojo;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.serialization.ClassResolvers;
|
||||
import io.netty.handler.codec.serialization.ObjectDecoder;
|
||||
import io.netty.handler.codec.serialization.ObjectEncoder;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
/**
|
||||
* 可以发送PoJo的Sever
|
||||
*/
|
||||
public final class PojoServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
//定义bossGroup和workerGroup
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(
|
||||
// 添加encoder和decoder
|
||||
new ObjectEncoder(),
|
||||
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
|
||||
new PojoServerHandler());
|
||||
}
|
||||
});
|
||||
|
||||
// 绑定端口,并准备接受连接
|
||||
b.bind(PORT).sync().channel().closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean08.pojo;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 处理客户端发来的消息
|
||||
*/
|
||||
@Slf4j
|
||||
public class PojoServerHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
log.info("server收到对象:{}",msg);
|
||||
ctx.write("加油!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean09.reconnect;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
|
||||
/**
|
||||
* netty客户端
|
||||
*/
|
||||
public final class ReconnectClient {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
|
||||
// 在reconnect之前 Sleep 5 秒钟
|
||||
static final int RECONNECT_DELAY = Integer.parseInt(System.getProperty("reconnectDelay", "5"));
|
||||
//如果在10秒中之内没有任何相应则重连
|
||||
private static final int READ_TIMEOUT = Integer.parseInt(System.getProperty("readTimeout", "10"));
|
||||
|
||||
private static final ReconnectClientHandler handler = new ReconnectClientHandler();
|
||||
private static final Bootstrap bs = new Bootstrap();
|
||||
|
||||
public static void main(String[] args) {
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
bs.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(HOST, PORT)
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast(new IdleStateHandler(READ_TIMEOUT, 0, 0), handler);
|
||||
}
|
||||
});
|
||||
bs.connect();
|
||||
}
|
||||
|
||||
static void connect() {
|
||||
bs.connect().addListener(future -> {
|
||||
if (future.cause() != null) {
|
||||
handler.startTime = -1;
|
||||
handler.println("建立连接失败: " + future.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean09.reconnect;
|
||||
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 重连handler
|
||||
*/
|
||||
@Sharable
|
||||
@Slf4j
|
||||
public class ReconnectClientHandler extends SimpleChannelInboundHandler<Object> {
|
||||
|
||||
long startTime = -1;
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
if (startTime < 0) {
|
||||
startTime = System.currentTimeMillis();
|
||||
}
|
||||
println("连接到: " + ctx.channel().remoteAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
// 读取消息
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||
if (!(evt instanceof IdleStateEvent)) {
|
||||
return;
|
||||
}
|
||||
IdleStateEvent e = (IdleStateEvent) evt;
|
||||
if (e.state() == IdleState.READER_IDLE) {
|
||||
// 在Idle状态
|
||||
println("Idle状态,关闭连接");
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(final ChannelHandlerContext ctx) {
|
||||
println("连接断开:" + ctx.channel().remoteAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnregistered(final ChannelHandlerContext ctx) throws Exception {
|
||||
println("sleep:" + ReconnectClient.RECONNECT_DELAY + 's');
|
||||
|
||||
ctx.channel().eventLoop().schedule(() -> {
|
||||
println("重连接: " + ReconnectClient.HOST + ':' + ReconnectClient.PORT);
|
||||
ReconnectClient.connect();
|
||||
}, ReconnectClient.RECONNECT_DELAY, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
void println(String msg) {
|
||||
if (startTime < 0) {
|
||||
log.error("服务下线:{}",msg);
|
||||
} else {
|
||||
log.error("服务运行时间:{},{}",(System.currentTimeMillis() - startTime) / 1000, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean09.reconnect;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
/**
|
||||
* netty 服务器
|
||||
*/
|
||||
public final class ReconnectServer {
|
||||
private static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
|
||||
private static final ReconnectServerHandler handler = new ReconnectServerHandler();
|
||||
|
||||
private ReconnectServer() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ch.pipeline().addLast(handler);
|
||||
}
|
||||
});
|
||||
|
||||
// 绑定端口并启动
|
||||
ChannelFuture f = b.bind(PORT).sync();
|
||||
|
||||
// 等待所有的socket都关闭
|
||||
f.channel().closeFuture().sync();
|
||||
} finally {
|
||||
workerGroup.shutdownGracefully();
|
||||
bossGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean09.reconnect;
|
||||
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Sharable
|
||||
@Slf4j
|
||||
public class ReconnectServerHandler extends SimpleChannelInboundHandler<Object> {
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
// 读取消息
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean10.chat;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* 聊天室客户端
|
||||
*/
|
||||
@Slf4j
|
||||
public final class ChatClient {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new ChatClientInitializer());
|
||||
|
||||
// 建立连接
|
||||
Channel ch = b.connect(HOST, PORT).sync().channel();
|
||||
log.info("client channel: {}", ch);
|
||||
|
||||
// 从命令行输入
|
||||
ChannelFuture lastWriteFuture = null;
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
|
||||
for (;;) {
|
||||
String line = in.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
// 将从命令行输入的一行字符写到channel中
|
||||
lastWriteFuture = ch.writeAndFlush(line + "\r\n");
|
||||
// 如果输入'再见',则等待server端关闭channel
|
||||
if ("再见".equalsIgnoreCase(line)) {
|
||||
ch.closeFuture().sync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 等待所有的消息都写入channel中
|
||||
if (lastWriteFuture != null) {
|
||||
lastWriteFuture.sync();
|
||||
}
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean10.chat;
|
||||
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 客户端处理器
|
||||
*/
|
||||
@Sharable
|
||||
@Slf4j
|
||||
public class ChatClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
|
||||
log.info("client accepted channel: {}", ctx.channel());
|
||||
log.info("接收到消息:{}",msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean10.chat;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
|
||||
import io.netty.handler.codec.Delimiters;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
|
||||
/**
|
||||
* 客户端ChannelPipeline的配置
|
||||
*/
|
||||
public class ChatClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private static final StringDecoder DECODER = new StringDecoder();
|
||||
private static final StringEncoder ENCODER = new StringEncoder();
|
||||
|
||||
private static final ChatClientHandler CLIENT_HANDLER = new ChatClientHandler();
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
// 添加行分割器
|
||||
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
|
||||
pipeline.addLast(DECODER);
|
||||
pipeline.addLast(ENCODER);
|
||||
|
||||
// 添加客户端处理器
|
||||
pipeline.addLast(CLIENT_HANDLER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean10.chat;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* chat 服务器
|
||||
*/
|
||||
@Slf4j
|
||||
public final class ChatServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new ChatServerInitializer());
|
||||
|
||||
Channel channel = b.bind(PORT).sync().channel();
|
||||
log.info("server channel:{}", channel);
|
||||
channel.closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean10.chat;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* server端的处理器
|
||||
*/
|
||||
@Sharable
|
||||
@Slf4j
|
||||
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
log.info("accepted channel: {}", ctx.channel());
|
||||
log.info("accepted channel parent: {}", ctx.channel().parent());
|
||||
// channel活跃
|
||||
ctx.write("Channel Active状态!\r\n");
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, String request) throws Exception {
|
||||
// 如果读取到"再见"就关闭channel
|
||||
String response;
|
||||
// 判断是否关闭
|
||||
boolean close = false;
|
||||
if (request.isEmpty()) {
|
||||
response = "你说啥?\r\n";
|
||||
} else if ("再见".equalsIgnoreCase(request)) {
|
||||
response = "再见,我的朋友!\r\n";
|
||||
close = true;
|
||||
} else {
|
||||
response = "你是不是说: '" + request + "'?\r\n";
|
||||
}
|
||||
|
||||
// 写入消息
|
||||
ChannelFuture future = ctx.write(response);
|
||||
// 添加CLOSE listener,用来关闭channel
|
||||
if (close) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean10.chat;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
|
||||
import io.netty.handler.codec.Delimiters;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
|
||||
/**
|
||||
* 初始化一个ChannelPipeline
|
||||
*/
|
||||
public class ChatServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private static final StringDecoder DECODER = new StringDecoder();
|
||||
private static final StringEncoder ENCODER = new StringEncoder();
|
||||
|
||||
private static final ChatServerHandler SERVER_HANDLER = new ChatServerHandler();
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
// 添加行分割器
|
||||
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
|
||||
// 添加String Decoder和String Encoder,用来进行字符串的转换
|
||||
pipeline.addLast(DECODER);
|
||||
pipeline.addLast(ENCODER);
|
||||
// 最后添加真正的处理器
|
||||
pipeline.addLast(SERVER_HANDLER);
|
||||
}
|
||||
}
|
||||
63
learn-netty4/src/main/java/com/flydean11/udp/UDPClient.java
Normal file
63
learn-netty4/src/main/java/com/flydean11/udp/UDPClient.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean11.udp;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.SocketUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* UDP client
|
||||
*/
|
||||
@Slf4j
|
||||
public final class UDPClient {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioDatagramChannel.class)
|
||||
.option(ChannelOption.SO_BROADCAST, true)
|
||||
.handler(new UDPClientHandler());
|
||||
|
||||
Channel ch = b.bind(0).sync().channel();
|
||||
|
||||
// 将消息广播给UDP服务器
|
||||
ch.writeAndFlush(new DatagramPacket(
|
||||
Unpooled.copiedBuffer("开始广播", CharsetUtil.UTF_8),
|
||||
SocketUtils.socketAddress("255.255.255.255", PORT))).sync();
|
||||
|
||||
// 等待channel关闭,如果Channel没在5秒钟之内关闭,则打印异常
|
||||
if (!ch.closeFuture().await(5000)) {
|
||||
log.info("channel没在5秒内关闭!");
|
||||
}
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean11.udp;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class UDPClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
|
||||
String response = msg.content().toString(CharsetUtil.UTF_8);
|
||||
if (response.startsWith("广播: ")) {
|
||||
log.info("收到广播: {}", response.substring(4));
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
45
learn-netty4/src/main/java/com/flydean11/udp/UDPServer.java
Normal file
45
learn-netty4/src/main/java/com/flydean11/udp/UDPServer.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean11.udp;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
|
||||
/**
|
||||
* UDP 服务器
|
||||
*/
|
||||
public final class UDPServer {
|
||||
|
||||
private static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioDatagramChannel.class)
|
||||
.option(ChannelOption.SO_BROADCAST, true)
|
||||
.handler(new UDPServerHandler());
|
||||
|
||||
b.bind(PORT).sync().channel().closeFuture().await();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean11.udp;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Slf4j
|
||||
public class UDPServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
|
||||
|
||||
private static final Random random = new Random();
|
||||
|
||||
// 广播消息
|
||||
private static final String[] quotes = {
|
||||
"鹅鹅鹅",
|
||||
"曲项向天歌",
|
||||
"白毛浮绿水",
|
||||
"红掌拨清波",
|
||||
};
|
||||
|
||||
private static String nextQuote() {
|
||||
int quoteId;
|
||||
synchronized (random) {
|
||||
quoteId = random.nextInt(quotes.length);
|
||||
}
|
||||
return quotes[quoteId];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
|
||||
log.info("服务器收到消息:{}",packet);
|
||||
if ("开始广播".equals(packet.content().toString(CharsetUtil.UTF_8))) {
|
||||
ctx.write(new DatagramPacket(
|
||||
Unpooled.copiedBuffer("广播: " + nextQuote(), CharsetUtil.UTF_8), packet.sender()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean12.securechat;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* SSL 聊天室客户端
|
||||
*/
|
||||
public final class SecureChatClient {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 配置 SSL.
|
||||
final SslContext sslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new SecureChatClientInitializer(sslCtx));
|
||||
|
||||
// 建立连接
|
||||
Channel ch = b.connect(HOST, PORT).sync().channel();
|
||||
|
||||
// 从命令行输入
|
||||
ChannelFuture lastWriteFuture = null;
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
|
||||
for (;;) {
|
||||
String line = in.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
// 将从命令行输入的一行字符写到channel中
|
||||
lastWriteFuture = ch.writeAndFlush(line + "\r\n");
|
||||
// 如果输入'再见',则等待server端关闭channel
|
||||
if ("再见".equalsIgnoreCase(line)) {
|
||||
ch.closeFuture().sync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 等待所有的消息都写入channel中
|
||||
if (lastWriteFuture != null) {
|
||||
lastWriteFuture.sync();
|
||||
}
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean12.securechat;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* SSL 聊天室客户端
|
||||
*/
|
||||
public final class SecureChatClient2 {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// 配置 SSL.
|
||||
final SslContext sslCtx = SslContextBuilder.forClient()
|
||||
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new SecureChatClientInitializer(sslCtx));
|
||||
|
||||
// 建立连接
|
||||
Channel ch = b.connect(HOST, PORT).sync().channel();
|
||||
|
||||
// 从命令行输入
|
||||
ChannelFuture lastWriteFuture = null;
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
|
||||
for (;;) {
|
||||
String line = in.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
// 将从命令行输入的一行字符写到channel中
|
||||
lastWriteFuture = ch.writeAndFlush(line + "\r\n");
|
||||
// 如果输入'再见',则等待server端关闭channel
|
||||
if ("再见".equalsIgnoreCase(line)) {
|
||||
ch.closeFuture().sync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 等待所有的消息都写入channel中
|
||||
if (lastWriteFuture != null) {
|
||||
lastWriteFuture.sync();
|
||||
}
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean12.securechat;
|
||||
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 客户端处理器
|
||||
*/
|
||||
@Sharable
|
||||
@Slf4j
|
||||
public class SecureChatClientHandler extends SimpleChannelInboundHandler<String> {
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
|
||||
log.info("接收到消息:{}",msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean12.securechat;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
|
||||
import io.netty.handler.codec.Delimiters;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
|
||||
/**
|
||||
* 客户端ChannelPipeline的配置
|
||||
*/
|
||||
public class SecureChatClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private static final StringDecoder DECODER = new StringDecoder();
|
||||
private static final StringEncoder ENCODER = new StringEncoder();
|
||||
|
||||
private static final SecureChatClientHandler CLIENT_HANDLER = new SecureChatClientHandler();
|
||||
|
||||
private final SslContext sslCtx;
|
||||
|
||||
public SecureChatClientInitializer(SslContext sslCtx){
|
||||
this.sslCtx = sslCtx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
// 添加SSL处理机制
|
||||
pipeline.addLast(sslCtx.newHandler(ch.alloc(), SecureChatClient.HOST, SecureChatClient.PORT));
|
||||
|
||||
// 添加行分割器
|
||||
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
|
||||
pipeline.addLast(DECODER);
|
||||
pipeline.addLast(ENCODER);
|
||||
|
||||
// 添加客户端处理器
|
||||
pipeline.addLast(CLIENT_HANDLER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean12.securechat;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
|
||||
/**
|
||||
* telnet 服务器
|
||||
*/
|
||||
public final class SecureChatServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
|
||||
.build();
|
||||
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new SecureChatServerInitializer(sslCtx));
|
||||
|
||||
b.bind(PORT).sync().channel().closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean12.securechat;
|
||||
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
import io.netty.channel.group.DefaultChannelGroup;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* server端的处理器
|
||||
*/
|
||||
@Sharable
|
||||
@Slf4j
|
||||
public class SecureChatServerHandler extends SimpleChannelInboundHandler<String> {
|
||||
//一个全局共享的channel group
|
||||
static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
// channel活跃
|
||||
ctx.write("Channel Active状态!\r\n");
|
||||
|
||||
ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
|
||||
(GenericFutureListener<Future<Channel>>) future -> {
|
||||
ctx.writeAndFlush(
|
||||
"欢迎你: " + InetAddress.getLocalHost().getHostName() + " !\n");
|
||||
ctx.writeAndFlush(
|
||||
"从现在起,你的会话将使用: " +
|
||||
ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() +
|
||||
" 进行加密保护.\n");
|
||||
// 将新创建的channel添加到全局的channel group中,用于后续的消息广播
|
||||
channels.add(ctx.channel());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, String message) throws Exception {
|
||||
// 将一个客户端的消息广播到所有的channel中
|
||||
for (Channel c: channels) {
|
||||
if (c != ctx.channel()) {
|
||||
c.writeAndFlush( ctx.channel().remoteAddress() + "说: " + message + '\n');
|
||||
} else {
|
||||
c.writeAndFlush("你是不是说: " + message + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
// 如果读取到"再见"就关闭channel
|
||||
String response;
|
||||
// 判断是否关闭
|
||||
boolean close = false;
|
||||
if (message.isEmpty()) {
|
||||
response = "你说啥?\r\n";
|
||||
} else if ("再见".equalsIgnoreCase(message)) {
|
||||
response = "再见,我的朋友!\r\n";
|
||||
close = true;
|
||||
} else {
|
||||
response = "你是不是说: '" + message + "'?\r\n";
|
||||
}
|
||||
|
||||
// 写入消息
|
||||
ChannelFuture future = ctx.write(response);
|
||||
// 添加CLOSE listener,用来关闭channel
|
||||
if (close) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean12.securechat;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
|
||||
import io.netty.handler.codec.Delimiters;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
|
||||
/**
|
||||
* 初始化一个ChannelPipeline
|
||||
*/
|
||||
public class SecureChatServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
private static final StringDecoder DECODER = new StringDecoder();
|
||||
private static final StringEncoder ENCODER = new StringEncoder();
|
||||
|
||||
private static final SecureChatServerHandler SERVER_HANDLER = new SecureChatServerHandler();
|
||||
|
||||
private final SslContext sslCtx;
|
||||
|
||||
public SecureChatServerInitializer(SslContext sslCtx){
|
||||
this.sslCtx = sslCtx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
//添加SSL处理器
|
||||
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
|
||||
// 添加行分割器
|
||||
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
|
||||
// 添加String Decoder和String Encoder,用来进行字符串的转换
|
||||
pipeline.addLast(DECODER);
|
||||
pipeline.addLast(ENCODER);
|
||||
// 最后添加真正的处理器
|
||||
pipeline.addLast(SERVER_HANDLER);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean13.customprotocol;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 自定义协议客户端
|
||||
*/
|
||||
@Slf4j
|
||||
public final class CustomProtocolClient {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
static final int COUNT = Integer.parseInt(System.getProperty("count", "100"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new CustomProtocolClientInitializer());
|
||||
|
||||
// 建立连接
|
||||
ChannelFuture f = b.connect(HOST, PORT).sync();
|
||||
|
||||
// 获取自定义handler
|
||||
CustomProtocolClientHandler handler =
|
||||
(CustomProtocolClientHandler) f.channel().pipeline().last();
|
||||
|
||||
// 打印结果
|
||||
log.info("2的{}次方是:{}",COUNT, handler.getResult());
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean13.customprotocol;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* 客户端的handler
|
||||
*/
|
||||
@Slf4j
|
||||
public class CustomProtocolClientHandler extends SimpleChannelInboundHandler<BigInteger> {
|
||||
|
||||
private ChannelHandlerContext ctx;
|
||||
private int receivedMessages;
|
||||
private int next = 1;
|
||||
final BlockingQueue<BigInteger> answer = new LinkedBlockingQueue<>();
|
||||
|
||||
public BigInteger getResult() {
|
||||
boolean interrupted = false;
|
||||
try {
|
||||
for (;;) {
|
||||
try {
|
||||
return answer.take();
|
||||
} catch (InterruptedException ignore) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) {
|
||||
this.ctx = ctx;
|
||||
sendNumbers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, final BigInteger msg) {
|
||||
receivedMessages ++;
|
||||
if (receivedMessages == CustomProtocolClient.COUNT) {
|
||||
// 计算完毕,将结果放入answer中
|
||||
ctx.channel().close().addListener(future -> {
|
||||
boolean offered = answer.offer(msg);
|
||||
assert offered;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private void sendNumbers() {
|
||||
// 最大计算2的1000次方
|
||||
ChannelFuture future = null;
|
||||
for (int i = 0; i < 1000 && next <= CustomProtocolClient.COUNT; i++) {
|
||||
future = ctx.write(2);
|
||||
next++;
|
||||
}
|
||||
if (next <= CustomProtocolClient.COUNT) {
|
||||
assert future != null;
|
||||
future.addListener(numberSender);
|
||||
}
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
private final ChannelFutureListener numberSender = future -> {
|
||||
if (future.isSuccess()) {
|
||||
sendNumbers();
|
||||
} else {
|
||||
future.cause().printStackTrace();
|
||||
future.channel().close();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean13.customprotocol;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
|
||||
/**
|
||||
* 客户端pipeline初始化器
|
||||
*/
|
||||
public class CustomProtocolClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
// stream压缩
|
||||
pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));
|
||||
pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
|
||||
|
||||
// 添加Number编码器
|
||||
pipeline.addLast(new NumberDecoder());
|
||||
pipeline.addLast(new NumberEncoder());
|
||||
|
||||
// 最后添加业务逻辑
|
||||
pipeline.addLast(new CustomProtocolClientHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean13.customprotocol;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
/**
|
||||
* 自定义协议服务器
|
||||
*/
|
||||
public final class CustomProtocolServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new CustomProtocolServerInitializer());
|
||||
|
||||
b.bind(PORT).sync().channel().closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean13.customprotocol;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 自定义协议处理器
|
||||
*/
|
||||
@Slf4j
|
||||
public class CustomProtocolServerHandler extends SimpleChannelInboundHandler<BigInteger> {
|
||||
|
||||
private int count = 0;
|
||||
private BigInteger result=BigInteger.valueOf(1);
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, BigInteger msg) throws Exception {
|
||||
// 将接收到的msg乘以2,然后返回给客户端
|
||||
count++;
|
||||
result = result.multiply(msg);
|
||||
ctx.writeAndFlush(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
log.info("2的{}次方是{}",count,result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean13.customprotocol;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
|
||||
/**
|
||||
* 服务器端pipeline初始化
|
||||
*/
|
||||
public class CustomProtocolServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
// 对流进行压缩
|
||||
pipeline.addLast(ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));
|
||||
pipeline.addLast(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
|
||||
|
||||
// 添加number编码解码器
|
||||
pipeline.addLast(new NumberDecoder());
|
||||
pipeline.addLast(new NumberEncoder());
|
||||
|
||||
// 添加业务处理逻辑
|
||||
pipeline.addLast(new CustomProtocolServerHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean13.customprotocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 将编码过后的byte[] 转换成BigInteger对象
|
||||
*/
|
||||
public class NumberDecoder extends ByteToMessageDecoder {
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
|
||||
// 保证魔法词和数组长度有效
|
||||
if (in.readableBytes() < 5) {
|
||||
return;
|
||||
}
|
||||
in.markReaderIndex();
|
||||
// 检查魔法词
|
||||
int magicNumber = in.readUnsignedByte();
|
||||
if (magicNumber != 'N') {
|
||||
in.resetReaderIndex();
|
||||
throw new CorruptedFrameException("无效的魔法词: " + magicNumber);
|
||||
}
|
||||
// 读取所有的数据
|
||||
int dataLength = in.readInt();
|
||||
if (in.readableBytes() < dataLength) {
|
||||
in.resetReaderIndex();
|
||||
return;
|
||||
}
|
||||
// 将剩下的数据转换成为BigInteger
|
||||
byte[] decoded = new byte[dataLength];
|
||||
in.readBytes(decoded);
|
||||
out.add(new BigInteger(decoded));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean13.customprotocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 将Number编码成byte[]格式,第一个byte表示魔法词"N",
|
||||
* 接下来的4个byte代表数组的长度
|
||||
* 最后的才是真正的数字
|
||||
*/
|
||||
public class NumberEncoder extends MessageToByteEncoder<Number> {
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Number msg, ByteBuf out) {
|
||||
// 将number编码成为ByteBuf
|
||||
BigInteger v;
|
||||
if (msg instanceof BigInteger) {
|
||||
v = (BigInteger) msg;
|
||||
} else {
|
||||
v = new BigInteger(String.valueOf(msg));
|
||||
}
|
||||
|
||||
// 将BigInteger转换成为byte[]数组
|
||||
byte[] data = v.toByteArray();
|
||||
int dataLength = data.length;
|
||||
|
||||
// 将Number进行编码
|
||||
out.writeByte((byte) 'N'); // 魔法词
|
||||
out.writeInt(dataLength); // 数组长度
|
||||
out.writeBytes(data); // 最终的数据
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean14.custcodec;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version IntegerEncoder, 2021/8/13
|
||||
*/
|
||||
public class IntegerEncoder extends MessageToByteEncoder<Integer> {
|
||||
@Override
|
||||
public void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out)
|
||||
throws Exception {
|
||||
out.writeInt(msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean14.custcodec;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version IntegerHeaderFrameDecoder, 2021/8/13
|
||||
*/
|
||||
public class IntegerHeaderFrameDecoder extends ByteToMessageDecoder {
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx,
|
||||
ByteBuf buf, List<Object> out) throws Exception {
|
||||
|
||||
if (buf.readableBytes() < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
buf.markReaderIndex();
|
||||
int length = buf.readInt();
|
||||
|
||||
if (buf.readableBytes() < length) {
|
||||
buf.resetReaderIndex();
|
||||
return;
|
||||
}
|
||||
|
||||
out.add(buf.readBytes(length));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean14.custcodec;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version IntegerHeaderFrameDecoderReplay, 2021/8/13
|
||||
*/
|
||||
public class IntegerHeaderFrameDecoderReplay extends ReplayingDecoder<Void> {
|
||||
|
||||
protected void decode(ChannelHandlerContext ctx,
|
||||
ByteBuf buf, List<Object> out) throws Exception {
|
||||
|
||||
out.add(buf.readBytes(buf.readInt()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean14.custcodec;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version IntegerHeaderFrameDecoderSteps, 2021/8/13
|
||||
*/
|
||||
public class IntegerHeaderFrameDecoderSteps extends ReplayingDecoder<MyDecoderState> {
|
||||
|
||||
private int length;
|
||||
|
||||
public IntegerHeaderFrameDecoderSteps() {
|
||||
// Set the initial state.
|
||||
super(MyDecoderState.READ_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx,
|
||||
ByteBuf buf, List<Object> out) throws Exception {
|
||||
switch (state()) {
|
||||
case READ_LENGTH:
|
||||
length = buf.readInt();
|
||||
checkpoint(MyDecoderState.READ_CONTENT);
|
||||
case READ_CONTENT:
|
||||
ByteBuf frame = buf.readBytes(length);
|
||||
checkpoint(MyDecoderState.READ_LENGTH);
|
||||
out.add(frame);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Shouldn't reach here.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean14.custcodec;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version IntegerToStringEncoder, 2021/8/13
|
||||
*/
|
||||
public class IntegerToStringEncoder extends
|
||||
MessageToMessageEncoder<Integer> {
|
||||
|
||||
@Override
|
||||
public void encode(ChannelHandlerContext ctx, Integer message, List<Object> out)
|
||||
throws Exception {
|
||||
out.add(message.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean14.custcodec;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version MyDecoderState, 2021/8/13
|
||||
*/
|
||||
public enum MyDecoderState {
|
||||
READ_LENGTH,
|
||||
READ_CONTENT;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean14.custcodec;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version SquareDecoder, 2021/8/13
|
||||
*/
|
||||
public class SquareDecoder extends ByteToMessageDecoder {
|
||||
@Override
|
||||
public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
|
||||
throws Exception {
|
||||
out.add(in.readBytes(in.readableBytes()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean14.custcodec;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wayne
|
||||
* @version StringToIntegerDecoder, 2021/8/13
|
||||
*/
|
||||
public class StringToIntegerDecoder extends
|
||||
MessageToMessageDecoder<String> {
|
||||
|
||||
@Override
|
||||
public void decode(ChannelHandlerContext ctx, String message,
|
||||
List<Object> out) throws Exception {
|
||||
out.add(message.length());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.marshalling;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jboss.marshalling.MarshallerFactory;
|
||||
import org.jboss.marshalling.Marshalling;
|
||||
import org.jboss.marshalling.MarshallingConfiguration;
|
||||
import org.jboss.marshalling.Unmarshaller;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
public class MarshallingReader {
|
||||
|
||||
public void marshallingRead(String fileName) throws IOException, ClassNotFoundException {
|
||||
// 使用river协议创建MarshallerFactory
|
||||
MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("river");
|
||||
// 创建配置文件
|
||||
MarshallingConfiguration configuration = new MarshallingConfiguration();
|
||||
// 使用版本号4
|
||||
configuration.setVersion(4);
|
||||
final Unmarshaller unmarshaller = marshallerFactory.createUnmarshaller(configuration);
|
||||
try(FileInputStream is = new FileInputStream(fileName)){
|
||||
unmarshaller.start(Marshalling.createByteInput(is));
|
||||
Student student=(Student)unmarshaller.readObject();
|
||||
log.info("student:{}",student);
|
||||
unmarshaller.finish();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, ClassNotFoundException {
|
||||
MarshallingReader reader= new MarshallingReader();
|
||||
reader.marshallingRead("/tmp/marshall.txt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.marshalling;
|
||||
|
||||
import org.jboss.marshalling.*;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
|
||||
public class MarshallingWriter {
|
||||
|
||||
public void marshallingWrite(String fileName, Object obj) throws IOException {
|
||||
// 使用river作为marshalling的方式
|
||||
MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("river");
|
||||
// 创建marshalling的配置
|
||||
MarshallingConfiguration configuration = new MarshallingConfiguration();
|
||||
// 使用版本号4
|
||||
configuration.setVersion(4);
|
||||
// configuration.setClassCount(10);
|
||||
// configuration.setBufferSize(8096);
|
||||
// configuration.setInstanceCount(100);
|
||||
// configuration.setExceptionListener(new MarshallingException());
|
||||
// configuration.setClassResolver(new SimpleClassResolver(getClass().getClassLoader()));
|
||||
// configuration.setObjectPreResolver(new ChainingObjectResolver(Collections.singletonList(new HibernateDetachResolver())));
|
||||
final Marshaller marshaller = marshallerFactory.createMarshaller(configuration);
|
||||
try(FileOutputStream os = new FileOutputStream(fileName)){
|
||||
marshaller.start(Marshalling.createByteOutput(os));
|
||||
marshaller.writeObject(obj);
|
||||
marshaller.finish();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
MarshallingWriter writer = new MarshallingWriter();
|
||||
Student student= new Student("jack", 18, "first grade");
|
||||
writer.marshallingWrite("/tmp/marshall.txt",student);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.marshalling;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class Student implements Serializable {
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
private String className;
|
||||
|
||||
}
|
||||
675
learn-netty4/src/main/java/com/flydean17/protobuf/Student.java
Normal file
675
learn-netty4/src/main/java/com/flydean17/protobuf/Student.java
Normal file
@@ -0,0 +1,675 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: student.proto
|
||||
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
/**
|
||||
* Protobuf type {@code com.flydean17.protobuf.Student}
|
||||
*/
|
||||
public final class Student extends
|
||||
com.google.protobuf.GeneratedMessageV3 implements
|
||||
// @@protoc_insertion_point(message_implements:com.flydean17.protobuf.Student)
|
||||
StudentOrBuilder {
|
||||
private static final long serialVersionUID = 0L;
|
||||
// Use Student.newBuilder() to construct.
|
||||
private Student(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
|
||||
super(builder);
|
||||
}
|
||||
private Student() {
|
||||
name_ = "";
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
@SuppressWarnings({"unused"})
|
||||
protected java.lang.Object newInstance(
|
||||
UnusedPrivateParameter unused) {
|
||||
return new Student();
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public final com.google.protobuf.UnknownFieldSet
|
||||
getUnknownFields() {
|
||||
return this.unknownFields;
|
||||
}
|
||||
private Student(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
this();
|
||||
if (extensionRegistry == null) {
|
||||
throw new java.lang.NullPointerException();
|
||||
}
|
||||
int mutable_bitField0_ = 0;
|
||||
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
|
||||
com.google.protobuf.UnknownFieldSet.newBuilder();
|
||||
try {
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int tag = input.readTag();
|
||||
switch (tag) {
|
||||
case 0:
|
||||
done = true;
|
||||
break;
|
||||
case 8: {
|
||||
bitField0_ |= 0x00000001;
|
||||
age_ = input.readInt32();
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
java.lang.String s = input.readStringRequireUtf8();
|
||||
bitField0_ |= 0x00000002;
|
||||
name_ = s;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!parseUnknownField(
|
||||
input, unknownFields, extensionRegistry, tag)) {
|
||||
done = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(this);
|
||||
} catch (java.io.IOException e) {
|
||||
throw new com.google.protobuf.InvalidProtocolBufferException(
|
||||
e).setUnfinishedMessage(this);
|
||||
} finally {
|
||||
this.unknownFields = unknownFields.build();
|
||||
makeExtensionsImmutable();
|
||||
}
|
||||
}
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return com.flydean17.protobuf.StudentOuterClass.internal_static_com_flydean17_protobuf_Student_descriptor;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return com.flydean17.protobuf.StudentOuterClass.internal_static_com_flydean17_protobuf_Student_fieldAccessorTable
|
||||
.ensureFieldAccessorsInitialized(
|
||||
com.flydean17.protobuf.Student.class, com.flydean17.protobuf.Student.Builder.class);
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
public static final int AGE_FIELD_NUMBER = 1;
|
||||
private int age_;
|
||||
/**
|
||||
* <code>int32 age = 1;</code>
|
||||
* @return Whether the age field is set.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public boolean hasAge() {
|
||||
return ((bitField0_ & 0x00000001) != 0);
|
||||
}
|
||||
/**
|
||||
* <code>int32 age = 1;</code>
|
||||
* @return The age.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public int getAge() {
|
||||
return age_;
|
||||
}
|
||||
|
||||
public static final int NAME_FIELD_NUMBER = 2;
|
||||
private volatile java.lang.Object name_;
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return Whether the name field is set.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public boolean hasName() {
|
||||
return ((bitField0_ & 0x00000002) != 0);
|
||||
}
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return The name.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public java.lang.String getName() {
|
||||
java.lang.Object ref = name_;
|
||||
if (ref instanceof java.lang.String) {
|
||||
return (java.lang.String) ref;
|
||||
} else {
|
||||
com.google.protobuf.ByteString bs =
|
||||
(com.google.protobuf.ByteString) ref;
|
||||
java.lang.String s = bs.toStringUtf8();
|
||||
name_ = s;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return The bytes for name.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public com.google.protobuf.ByteString
|
||||
getNameBytes() {
|
||||
java.lang.Object ref = name_;
|
||||
if (ref instanceof java.lang.String) {
|
||||
com.google.protobuf.ByteString b =
|
||||
com.google.protobuf.ByteString.copyFromUtf8(
|
||||
(java.lang.String) ref);
|
||||
name_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
|
||||
private byte memoizedIsInitialized = -1;
|
||||
@java.lang.Override
|
||||
public final boolean isInitialized() {
|
||||
byte isInitialized = memoizedIsInitialized;
|
||||
if (isInitialized == 1) return true;
|
||||
if (isInitialized == 0) return false;
|
||||
|
||||
memoizedIsInitialized = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public void writeTo(com.google.protobuf.CodedOutputStream output)
|
||||
throws java.io.IOException {
|
||||
if (((bitField0_ & 0x00000001) != 0)) {
|
||||
output.writeInt32(1, age_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000002) != 0)) {
|
||||
com.google.protobuf.GeneratedMessageV3.writeString(output, 2, name_);
|
||||
}
|
||||
unknownFields.writeTo(output);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
size = 0;
|
||||
if (((bitField0_ & 0x00000001) != 0)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeInt32Size(1, age_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000002) != 0)) {
|
||||
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, name_);
|
||||
}
|
||||
size += unknownFields.getSerializedSize();
|
||||
memoizedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public boolean equals(final java.lang.Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof com.flydean17.protobuf.Student)) {
|
||||
return super.equals(obj);
|
||||
}
|
||||
com.flydean17.protobuf.Student other = (com.flydean17.protobuf.Student) obj;
|
||||
|
||||
if (hasAge() != other.hasAge()) return false;
|
||||
if (hasAge()) {
|
||||
if (getAge()
|
||||
!= other.getAge()) return false;
|
||||
}
|
||||
if (hasName() != other.hasName()) return false;
|
||||
if (hasName()) {
|
||||
if (!getName()
|
||||
.equals(other.getName())) return false;
|
||||
}
|
||||
if (!unknownFields.equals(other.unknownFields)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public int hashCode() {
|
||||
if (memoizedHashCode != 0) {
|
||||
return memoizedHashCode;
|
||||
}
|
||||
int hash = 41;
|
||||
hash = (19 * hash) + getDescriptor().hashCode();
|
||||
if (hasAge()) {
|
||||
hash = (37 * hash) + AGE_FIELD_NUMBER;
|
||||
hash = (53 * hash) + getAge();
|
||||
}
|
||||
if (hasName()) {
|
||||
hash = (37 * hash) + NAME_FIELD_NUMBER;
|
||||
hash = (53 * hash) + getName().hashCode();
|
||||
}
|
||||
hash = (29 * hash) + unknownFields.hashCode();
|
||||
memoizedHashCode = hash;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static com.flydean17.protobuf.Student parseFrom(
|
||||
java.nio.ByteBuffer data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(
|
||||
java.nio.ByteBuffer data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(
|
||||
com.google.protobuf.ByteString data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(
|
||||
com.google.protobuf.ByteString data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(byte[] data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(
|
||||
byte[] data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return PARSER.parseFrom(data, extensionRegistry);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return com.google.protobuf.GeneratedMessageV3
|
||||
.parseWithIOException(PARSER, input);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return com.google.protobuf.GeneratedMessageV3
|
||||
.parseWithIOException(PARSER, input, extensionRegistry);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseDelimitedFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return com.google.protobuf.GeneratedMessageV3
|
||||
.parseDelimitedWithIOException(PARSER, input);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseDelimitedFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return com.google.protobuf.GeneratedMessageV3
|
||||
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(
|
||||
com.google.protobuf.CodedInputStream input)
|
||||
throws java.io.IOException {
|
||||
return com.google.protobuf.GeneratedMessageV3
|
||||
.parseWithIOException(PARSER, input);
|
||||
}
|
||||
public static com.flydean17.protobuf.Student parseFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return com.google.protobuf.GeneratedMessageV3
|
||||
.parseWithIOException(PARSER, input, extensionRegistry);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public Builder newBuilderForType() { return newBuilder(); }
|
||||
public static Builder newBuilder() {
|
||||
return DEFAULT_INSTANCE.toBuilder();
|
||||
}
|
||||
public static Builder newBuilder(com.flydean17.protobuf.Student prototype) {
|
||||
return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
|
||||
}
|
||||
@java.lang.Override
|
||||
public Builder toBuilder() {
|
||||
return this == DEFAULT_INSTANCE
|
||||
? new Builder() : new Builder().mergeFrom(this);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
protected Builder newBuilderForType(
|
||||
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
|
||||
Builder builder = new Builder(parent);
|
||||
return builder;
|
||||
}
|
||||
/**
|
||||
* Protobuf type {@code com.flydean17.protobuf.Student}
|
||||
*/
|
||||
public static final class Builder extends
|
||||
com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
|
||||
// @@protoc_insertion_point(builder_implements:com.flydean17.protobuf.Student)
|
||||
com.flydean17.protobuf.StudentOrBuilder {
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return com.flydean17.protobuf.StudentOuterClass.internal_static_com_flydean17_protobuf_Student_descriptor;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return com.flydean17.protobuf.StudentOuterClass.internal_static_com_flydean17_protobuf_Student_fieldAccessorTable
|
||||
.ensureFieldAccessorsInitialized(
|
||||
com.flydean17.protobuf.Student.class, com.flydean17.protobuf.Student.Builder.class);
|
||||
}
|
||||
|
||||
// Construct using com.flydean17.protobuf.Student.newBuilder()
|
||||
private Builder() {
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
|
||||
private Builder(
|
||||
com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
|
||||
super(parent);
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
private void maybeForceBuilderInitialization() {
|
||||
if (com.google.protobuf.GeneratedMessageV3
|
||||
.alwaysUseFieldBuilders) {
|
||||
}
|
||||
}
|
||||
@java.lang.Override
|
||||
public Builder clear() {
|
||||
super.clear();
|
||||
age_ = 0;
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
name_ = "";
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
return this;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptorForType() {
|
||||
return com.flydean17.protobuf.StudentOuterClass.internal_static_com_flydean17_protobuf_Student_descriptor;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public com.flydean17.protobuf.Student getDefaultInstanceForType() {
|
||||
return com.flydean17.protobuf.Student.getDefaultInstance();
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public com.flydean17.protobuf.Student build() {
|
||||
com.flydean17.protobuf.Student result = buildPartial();
|
||||
if (!result.isInitialized()) {
|
||||
throw newUninitializedMessageException(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public com.flydean17.protobuf.Student buildPartial() {
|
||||
com.flydean17.protobuf.Student result = new com.flydean17.protobuf.Student(this);
|
||||
int from_bitField0_ = bitField0_;
|
||||
int to_bitField0_ = 0;
|
||||
if (((from_bitField0_ & 0x00000001) != 0)) {
|
||||
result.age_ = age_;
|
||||
to_bitField0_ |= 0x00000001;
|
||||
}
|
||||
if (((from_bitField0_ & 0x00000002) != 0)) {
|
||||
to_bitField0_ |= 0x00000002;
|
||||
}
|
||||
result.name_ = name_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public Builder clone() {
|
||||
return super.clone();
|
||||
}
|
||||
@java.lang.Override
|
||||
public Builder setField(
|
||||
com.google.protobuf.Descriptors.FieldDescriptor field,
|
||||
java.lang.Object value) {
|
||||
return super.setField(field, value);
|
||||
}
|
||||
@java.lang.Override
|
||||
public Builder clearField(
|
||||
com.google.protobuf.Descriptors.FieldDescriptor field) {
|
||||
return super.clearField(field);
|
||||
}
|
||||
@java.lang.Override
|
||||
public Builder clearOneof(
|
||||
com.google.protobuf.Descriptors.OneofDescriptor oneof) {
|
||||
return super.clearOneof(oneof);
|
||||
}
|
||||
@java.lang.Override
|
||||
public Builder setRepeatedField(
|
||||
com.google.protobuf.Descriptors.FieldDescriptor field,
|
||||
int index, java.lang.Object value) {
|
||||
return super.setRepeatedField(field, index, value);
|
||||
}
|
||||
@java.lang.Override
|
||||
public Builder addRepeatedField(
|
||||
com.google.protobuf.Descriptors.FieldDescriptor field,
|
||||
java.lang.Object value) {
|
||||
return super.addRepeatedField(field, value);
|
||||
}
|
||||
@java.lang.Override
|
||||
public Builder mergeFrom(com.google.protobuf.Message other) {
|
||||
if (other instanceof com.flydean17.protobuf.Student) {
|
||||
return mergeFrom((com.flydean17.protobuf.Student)other);
|
||||
} else {
|
||||
super.mergeFrom(other);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Builder mergeFrom(com.flydean17.protobuf.Student other) {
|
||||
if (other == com.flydean17.protobuf.Student.getDefaultInstance()) return this;
|
||||
if (other.hasAge()) {
|
||||
setAge(other.getAge());
|
||||
}
|
||||
if (other.hasName()) {
|
||||
bitField0_ |= 0x00000002;
|
||||
name_ = other.name_;
|
||||
onChanged();
|
||||
}
|
||||
this.mergeUnknownFields(other.unknownFields);
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public final boolean isInitialized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public Builder mergeFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
com.flydean17.protobuf.Student parsedMessage = null;
|
||||
try {
|
||||
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
parsedMessage = (com.flydean17.protobuf.Student) e.getUnfinishedMessage();
|
||||
throw e.unwrapIOException();
|
||||
} finally {
|
||||
if (parsedMessage != null) {
|
||||
mergeFrom(parsedMessage);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
private int bitField0_;
|
||||
|
||||
private int age_ ;
|
||||
/**
|
||||
* <code>int32 age = 1;</code>
|
||||
* @return Whether the age field is set.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public boolean hasAge() {
|
||||
return ((bitField0_ & 0x00000001) != 0);
|
||||
}
|
||||
/**
|
||||
* <code>int32 age = 1;</code>
|
||||
* @return The age.
|
||||
*/
|
||||
@java.lang.Override
|
||||
public int getAge() {
|
||||
return age_;
|
||||
}
|
||||
/**
|
||||
* <code>int32 age = 1;</code>
|
||||
* @param value The age to set.
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
public Builder setAge(int value) {
|
||||
bitField0_ |= 0x00000001;
|
||||
age_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>int32 age = 1;</code>
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
public Builder clearAge() {
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
age_ = 0;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
private java.lang.Object name_ = "";
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return Whether the name field is set.
|
||||
*/
|
||||
public boolean hasName() {
|
||||
return ((bitField0_ & 0x00000002) != 0);
|
||||
}
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return The name.
|
||||
*/
|
||||
public java.lang.String getName() {
|
||||
java.lang.Object ref = name_;
|
||||
if (!(ref instanceof java.lang.String)) {
|
||||
com.google.protobuf.ByteString bs =
|
||||
(com.google.protobuf.ByteString) ref;
|
||||
java.lang.String s = bs.toStringUtf8();
|
||||
name_ = s;
|
||||
return s;
|
||||
} else {
|
||||
return (java.lang.String) ref;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return The bytes for name.
|
||||
*/
|
||||
public com.google.protobuf.ByteString
|
||||
getNameBytes() {
|
||||
java.lang.Object ref = name_;
|
||||
if (ref instanceof String) {
|
||||
com.google.protobuf.ByteString b =
|
||||
com.google.protobuf.ByteString.copyFromUtf8(
|
||||
(java.lang.String) ref);
|
||||
name_ = b;
|
||||
return b;
|
||||
} else {
|
||||
return (com.google.protobuf.ByteString) ref;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @param value The name to set.
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
public Builder setName(
|
||||
java.lang.String value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
bitField0_ |= 0x00000002;
|
||||
name_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
public Builder clearName() {
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
name_ = getDefaultInstance().getName();
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @param value The bytes for name to set.
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
public Builder setNameBytes(
|
||||
com.google.protobuf.ByteString value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
checkByteStringIsUtf8(value);
|
||||
bitField0_ |= 0x00000002;
|
||||
name_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
@java.lang.Override
|
||||
public final Builder setUnknownFields(
|
||||
final com.google.protobuf.UnknownFieldSet unknownFields) {
|
||||
return super.setUnknownFields(unknownFields);
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public final Builder mergeUnknownFields(
|
||||
final com.google.protobuf.UnknownFieldSet unknownFields) {
|
||||
return super.mergeUnknownFields(unknownFields);
|
||||
}
|
||||
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:com.flydean17.protobuf.Student)
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(class_scope:com.flydean17.protobuf.Student)
|
||||
private static final com.flydean17.protobuf.Student DEFAULT_INSTANCE;
|
||||
static {
|
||||
DEFAULT_INSTANCE = new com.flydean17.protobuf.Student();
|
||||
}
|
||||
|
||||
public static com.flydean17.protobuf.Student getDefaultInstance() {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
private static final com.google.protobuf.Parser<Student>
|
||||
PARSER = new com.google.protobuf.AbstractParser<Student>() {
|
||||
@java.lang.Override
|
||||
public Student parsePartialFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return new Student(input, extensionRegistry);
|
||||
}
|
||||
};
|
||||
|
||||
public static com.google.protobuf.Parser<Student> parser() {
|
||||
return PARSER;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public com.google.protobuf.Parser<Student> getParserForType() {
|
||||
return PARSER;
|
||||
}
|
||||
|
||||
@java.lang.Override
|
||||
public com.flydean17.protobuf.Student getDefaultInstanceForType() {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 发送一个Student的对象到server端
|
||||
*/
|
||||
@Slf4j
|
||||
public final class StudentClient {
|
||||
|
||||
static final String HOST = System.getProperty("host", "127.0.0.1");
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new StudentClientInitializer());
|
||||
// 建立连接
|
||||
Channel ch = b.connect(HOST, PORT).sync().channel();
|
||||
// 等待关闭
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class StudentClientHandler extends SimpleChannelInboundHandler<Student> {
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
// channel活跃
|
||||
//构建一个Student,并将其写入到channel中
|
||||
Student student= Student.newBuilder().setAge(22).setName("flydean").build();
|
||||
log.info("client发送消息{}",student);
|
||||
ctx.write(student);
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, Student student) throws Exception {
|
||||
log.info("client收到消息{}",student);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.protobuf.ProtobufDecoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufEncoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
|
||||
|
||||
public class StudentClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
|
||||
p.addLast(new ProtobufVarint32FrameDecoder());
|
||||
p.addLast(new ProtobufDecoder(Student.getDefaultInstance()));
|
||||
|
||||
p.addLast(new ProtobufVarint32LengthFieldPrepender());
|
||||
p.addLast(new ProtobufEncoder());
|
||||
|
||||
p.addLast(new StudentClientHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
public interface StudentOrBuilder extends
|
||||
// @@protoc_insertion_point(interface_extends:com.flydean17.protobuf.Student)
|
||||
com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
/**
|
||||
* <code>int32 age = 1;</code>
|
||||
* @return Whether the age field is set.
|
||||
*/
|
||||
boolean hasAge();
|
||||
/**
|
||||
* <code>int32 age = 1;</code>
|
||||
* @return The age.
|
||||
*/
|
||||
int getAge();
|
||||
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return Whether the name field is set.
|
||||
*/
|
||||
boolean hasName();
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return The name.
|
||||
*/
|
||||
java.lang.String getName();
|
||||
/**
|
||||
* <code>string name = 2;</code>
|
||||
* @return The bytes for name.
|
||||
*/
|
||||
com.google.protobuf.ByteString
|
||||
getNameBytes();
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
public final class StudentOuterClass {
|
||||
private StudentOuterClass() {}
|
||||
public static void registerAllExtensions(
|
||||
com.google.protobuf.ExtensionRegistryLite registry) {
|
||||
}
|
||||
|
||||
public static void registerAllExtensions(
|
||||
com.google.protobuf.ExtensionRegistry registry) {
|
||||
registerAllExtensions(
|
||||
(com.google.protobuf.ExtensionRegistryLite) registry);
|
||||
}
|
||||
static final com.google.protobuf.Descriptors.Descriptor
|
||||
internal_static_com_flydean17_protobuf_Student_descriptor;
|
||||
static final
|
||||
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
|
||||
internal_static_com_flydean17_protobuf_Student_fieldAccessorTable;
|
||||
|
||||
public static com.google.protobuf.Descriptors.FileDescriptor
|
||||
getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
private static com.google.protobuf.Descriptors.FileDescriptor
|
||||
descriptor;
|
||||
static {
|
||||
java.lang.String[] descriptorData = {
|
||||
"\n\rstudent.proto\022\026com.flydean17.protobuf\"" +
|
||||
"?\n\007Student\022\020\n\003age\030\001 \001(\005H\000\210\001\001\022\021\n\004name\030\002 \001" +
|
||||
"(\tH\001\210\001\001B\006\n\004_ageB\007\n\005_nameB\032\n\026com.flydean1" +
|
||||
"7.protobufP\001b\006proto3"
|
||||
};
|
||||
descriptor = com.google.protobuf.Descriptors.FileDescriptor
|
||||
.internalBuildGeneratedFileFrom(descriptorData,
|
||||
new com.google.protobuf.Descriptors.FileDescriptor[] {
|
||||
});
|
||||
internal_static_com_flydean17_protobuf_Student_descriptor =
|
||||
getDescriptor().getMessageTypes().get(0);
|
||||
internal_static_com_flydean17_protobuf_Student_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
|
||||
internal_static_com_flydean17_protobuf_Student_descriptor,
|
||||
new java.lang.String[] { "Age", "Name", "Age", "Name", });
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(outer_class_scope)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
/**
|
||||
* student server用来接收student消息
|
||||
*/
|
||||
public final class StudentServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new StudentServerInitializer());
|
||||
|
||||
b.bind(PORT).sync().channel().closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class StudentServerHandler extends SimpleChannelInboundHandler<Student> {
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, Student student) throws Exception {
|
||||
log.info("server收到消息{}",student);
|
||||
// 写入消息
|
||||
ChannelFuture future = ctx.write(student);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.protobuf.ProtobufDecoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufEncoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
|
||||
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
|
||||
|
||||
public class StudentServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
|
||||
p.addLast(new ProtobufVarint32FrameDecoder());
|
||||
p.addLast(new ProtobufDecoder(Student.getDefaultInstance()));
|
||||
|
||||
p.addLast(new ProtobufVarint32LengthFieldPrepender());
|
||||
p.addLast(new ProtobufEncoder());
|
||||
|
||||
p.addLast(new StudentServerHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package com.flydean17.protobuf;
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "com.flydean17.protobuf";
|
||||
//option java_outer_classname = "StudentWrapper";
|
||||
|
||||
message Student {
|
||||
optional int32 age = 1;
|
||||
optional string name =2;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean18.httprequest;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 一个简单的http server,用来从web端接收http请求
|
||||
*/
|
||||
@Slf4j
|
||||
public final class HttpRequestServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// server配置
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.option(ChannelOption.SO_BACKLOG, 1024);
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new HttpRequestServerInitializer());
|
||||
|
||||
Channel ch = b.bind(PORT).sync().channel();
|
||||
log.info("请打开你的浏览器,访问 http://127.0.0.1:8000/");
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean18.httprequest;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.*;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
|
||||
import static io.netty.handler.codec.http.HttpHeaderValues.*;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||
|
||||
@Slf4j
|
||||
public class HttpRequestServerHandler extends SimpleChannelInboundHandler<HttpObject> {
|
||||
private static final byte[] CONTENT = "欢迎来到www.flydean.com!".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
|
||||
if (msg instanceof HttpRequest) {
|
||||
HttpRequest req = (HttpRequest) msg;
|
||||
|
||||
boolean keepAlive = HttpUtil.isKeepAlive(req);
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,
|
||||
Unpooled.wrappedBuffer(CONTENT));
|
||||
response.headers()
|
||||
// .set(CONTENT_TYPE, TEXT_PLAIN)
|
||||
.set(CONTENT_TYPE, "text/plain;charset=utf-8")
|
||||
.setInt(CONTENT_LENGTH, response.content().readableBytes());
|
||||
|
||||
if (keepAlive) {
|
||||
if (!req.protocolVersion().isKeepAliveDefault()) {
|
||||
//设置header connection=keep-alive
|
||||
response.headers().set(CONNECTION, KEEP_ALIVE);
|
||||
}
|
||||
} else {
|
||||
// 如果keepAlive是false,则设置header connection=close
|
||||
response.headers().set(CONNECTION, CLOSE);
|
||||
}
|
||||
ChannelFuture f = ctx.write(response);
|
||||
if (!keepAlive) {
|
||||
f.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean18.httprequest;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.codec.http.HttpServerExpectContinueHandler;
|
||||
|
||||
public class HttpRequestServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(new HttpServerCodec());
|
||||
p.addLast(new HttpServerExpectContinueHandler());
|
||||
p.addLast(new HttpRequestServerHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean19.httpclientrequest;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.codec.http.cookie.ClientCookieEncoder;
|
||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* 一个自定义的HTTP Client
|
||||
*/
|
||||
public final class ClientRequestClient {
|
||||
|
||||
static final String URL = System.getProperty("url", "http://127.0.0.1:8000/");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
URI uri = new URI(URL);
|
||||
String host = uri.getHost() == null? "127.0.0.1" : uri.getHost();
|
||||
int port = uri.getPort();
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.handler(new ClientRequestClientInitializer());
|
||||
|
||||
// 建立连接
|
||||
Channel ch = b.connect(host, port).sync().channel();
|
||||
|
||||
// HTTP请求
|
||||
HttpRequest request = new DefaultFullHttpRequest(
|
||||
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath(), Unpooled.EMPTY_BUFFER);
|
||||
request.headers().set(HttpHeaderNames.HOST, host);
|
||||
request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
|
||||
request.headers().set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP);
|
||||
|
||||
// 设置cookie
|
||||
request.headers().set(
|
||||
HttpHeaderNames.COOKIE,
|
||||
ClientCookieEncoder.STRICT.encode(
|
||||
new DefaultCookie("name", "flydean"),
|
||||
new DefaultCookie("site", "www.flydean.com")));
|
||||
|
||||
// 发送HTTP请求
|
||||
ch.writeAndFlush(request);
|
||||
// 关闭连接
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
// 关闭服务器
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean19.httpclientrequest;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ClientRequestClientHandler extends SimpleChannelInboundHandler<HttpObject> {
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
|
||||
if (msg instanceof HttpResponse) {
|
||||
HttpResponse response = (HttpResponse) msg;
|
||||
|
||||
log.info("STATUS: {}" , response.status());
|
||||
log.info("VERSION: {}" , response.protocolVersion());
|
||||
|
||||
if (!response.headers().isEmpty()) {
|
||||
for (CharSequence name: response.headers().names()) {
|
||||
for (CharSequence value: response.headers().getAll(name)) {
|
||||
log.info("HEADER: {}={}" ,name , value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HttpUtil.isTransferEncodingChunked(response)) {
|
||||
log.info("CHUNKED CONTENT {");
|
||||
} else {
|
||||
log.info("CONTENT {");
|
||||
}
|
||||
}
|
||||
if (msg instanceof HttpContent) {
|
||||
HttpContent content = (HttpContent) msg;
|
||||
|
||||
log.info(content.content().toString(CharsetUtil.UTF_8));
|
||||
|
||||
if (content instanceof LastHttpContent) {
|
||||
log.info("} END OF CONTENT");
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean19.httpclientrequest;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpContentDecompressor;
|
||||
|
||||
public class ClientRequestClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
|
||||
p.addLast(new HttpClientCodec());
|
||||
p.addLast(new HttpContentDecompressor());
|
||||
//p.addLast(new HttpObjectAggregator(1048576));
|
||||
p.addLast(new ClientRequestClientHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean19.httpclientrequest;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* HTTP 服务器
|
||||
*/
|
||||
@Slf4j
|
||||
public final class HttpRequestServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// server配置
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new HttpRequestServerInitializer());
|
||||
|
||||
Channel ch = b.bind(PORT).sync().channel();
|
||||
|
||||
log.info("请打开你的浏览器,访问 http://127.0.0.1:8000/");
|
||||
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean19.httpclientrequest;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.codec.http.cookie.Cookie;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
|
||||
@Slf4j
|
||||
public class HttpRequestServerHandler extends SimpleChannelInboundHandler<Object> {
|
||||
|
||||
private HttpRequest request;
|
||||
//存储请求内容
|
||||
private final StringBuilder buf = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
|
||||
if (msg instanceof HttpRequest) {
|
||||
HttpRequest request = this.request = (HttpRequest) msg;
|
||||
|
||||
if (HttpUtil.is100ContinueExpected(request)) {
|
||||
send100Continue(ctx);
|
||||
}
|
||||
|
||||
buf.setLength(0);
|
||||
buf.append("欢迎来到www.flydean.com\r\n");
|
||||
buf.append("===================================\r\n");
|
||||
|
||||
buf.append("VERSION: ").append(request.protocolVersion()).append("\r\n");
|
||||
buf.append("HOSTNAME: ").append(request.headers().get(HttpHeaderNames.HOST, "unknown")).append("\r\n");
|
||||
buf.append("REQUEST_URI: ").append(request.uri()).append("\r\n\r\n");
|
||||
|
||||
HttpHeaders headers = request.headers();
|
||||
if (!headers.isEmpty()) {
|
||||
for (Entry<String, String> h: headers) {
|
||||
CharSequence key = h.getKey();
|
||||
CharSequence value = h.getValue();
|
||||
buf.append("HEADER: ").append(key).append(" = ").append(value).append("\r\n");
|
||||
}
|
||||
buf.append("\r\n");
|
||||
}
|
||||
|
||||
QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request.uri());
|
||||
Map<String, List<String>> params = queryStringDecoder.parameters();
|
||||
if (!params.isEmpty()) {
|
||||
for (Entry<String, List<String>> p: params.entrySet()) {
|
||||
String key = p.getKey();
|
||||
List<String> vals = p.getValue();
|
||||
for (String val : vals) {
|
||||
buf.append("PARAM: ").append(key).append(" = ").append(val).append("\r\n");
|
||||
}
|
||||
}
|
||||
buf.append("\r\n");
|
||||
}
|
||||
|
||||
appendDecoderResult(buf, request);
|
||||
}
|
||||
|
||||
if (msg instanceof HttpContent) {
|
||||
HttpContent httpContent = (HttpContent) msg;
|
||||
|
||||
ByteBuf content = httpContent.content();
|
||||
if (content.isReadable()) {
|
||||
buf.append("CONTENT: ");
|
||||
buf.append(content.toString(CharsetUtil.UTF_8));
|
||||
buf.append("\r\n");
|
||||
appendDecoderResult(buf, request);
|
||||
}
|
||||
|
||||
//如果消息是HttpContent的最后一部分,则发送响应
|
||||
if (msg instanceof LastHttpContent) {
|
||||
log.info("LastHttpContent:{}",msg);
|
||||
buf.append("END OF CONTENT\r\n");
|
||||
|
||||
LastHttpContent trailer = (LastHttpContent) msg;
|
||||
if (!trailer.trailingHeaders().isEmpty()) {
|
||||
buf.append("\r\n");
|
||||
for (CharSequence name: trailer.trailingHeaders().names()) {
|
||||
for (CharSequence value: trailer.trailingHeaders().getAll(name)) {
|
||||
buf.append("TRAILING HEADER: ");
|
||||
buf.append(name).append(" = ").append(value).append("\r\n");
|
||||
}
|
||||
}
|
||||
buf.append("\r\n");
|
||||
}
|
||||
|
||||
if (!writeResponse(trailer, ctx)) {
|
||||
// 如果不是长连接,那么在返回所有的响应之后关闭连接
|
||||
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendDecoderResult(StringBuilder buf, HttpObject o) {
|
||||
DecoderResult result = o.decoderResult();
|
||||
if (result.isSuccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
buf.append(".. WITH DECODER FAILURE: ");
|
||||
buf.append(result.cause());
|
||||
buf.append("\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 将响应写回channel
|
||||
* @param currentObj
|
||||
* @param ctx
|
||||
* @return
|
||||
*/
|
||||
private boolean writeResponse(HttpObject currentObj, ChannelHandlerContext ctx) {
|
||||
//是否长连接
|
||||
boolean keepAlive = HttpUtil.isKeepAlive(request);
|
||||
// 构建响应对象
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HTTP_1_1, currentObj.decoderResult().isSuccess()? OK : BAD_REQUEST,
|
||||
Unpooled.copiedBuffer(buf.toString(), CharsetUtil.UTF_8));
|
||||
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
|
||||
if (keepAlive) {
|
||||
// 添加 'Content-Length' header
|
||||
response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
|
||||
// 添加 keep alive header
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||
}
|
||||
// 对cookie进行decode和encode
|
||||
String cookieString = request.headers().get(HttpHeaderNames.COOKIE);
|
||||
log.info("cookieString:{}",cookieString);
|
||||
if (cookieString != null) {
|
||||
Set<Cookie> cookies = ServerCookieDecoder.STRICT.decode(cookieString);
|
||||
if (!cookies.isEmpty()) {
|
||||
// 重置cookie
|
||||
for (Cookie cookie: cookies) {
|
||||
response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//如果本身没有cookie,则添加cookie
|
||||
log.info("没有cookie,设置cookie");
|
||||
response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode("name", "flydean"));
|
||||
response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode("site", "www.flydean.com"));
|
||||
}
|
||||
|
||||
// 将请求写回
|
||||
ctx.write(response);
|
||||
|
||||
return keepAlive;
|
||||
}
|
||||
|
||||
private static void send100Continue(ChannelHandlerContext ctx) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE, Unpooled.EMPTY_BUFFER);
|
||||
ctx.write(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean19.httpclientrequest;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpContentCompressor;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
|
||||
public class HttpRequestServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
|
||||
p.addLast(new HttpRequestDecoder());
|
||||
//p.addLast(new HttpObjectAggregator(1048576));
|
||||
p.addLast(new HttpResponseEncoder());
|
||||
// p.addLast(new HttpContentCompressor());
|
||||
p.addLast(new HttpRequestServerHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean20.httpfile;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public final class HttpFileServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new HttpFileServerInitializer());
|
||||
|
||||
Channel ch = b.bind(PORT).sync().channel();
|
||||
|
||||
log.info("请打开你的浏览器,访问 http://127.0.0.1:8000/");
|
||||
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean20.httpfile;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.stream.ChunkedFile;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpMethod.GET;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0;
|
||||
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
|
||||
@Slf4j
|
||||
public class HttpFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
|
||||
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
|
||||
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT+8:00";
|
||||
public static final int HTTP_CACHE_SECONDS = 60;
|
||||
|
||||
private FullHttpRequest request;
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
|
||||
this.request = request;
|
||||
if (!request.decoderResult().isSuccess()) {
|
||||
sendError(ctx, BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GET.equals(request.method())) {
|
||||
sendError(ctx, METHOD_NOT_ALLOWED);
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean keepAlive = HttpUtil.isKeepAlive(request);
|
||||
final String uri = request.uri();
|
||||
final String path = sanitizeUri(uri);
|
||||
if (path == null) {
|
||||
sendError(ctx, FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
File file = new File(path);
|
||||
if (file.isHidden() || !file.exists()) {
|
||||
sendError(ctx, NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.isDirectory()) {
|
||||
if (uri.endsWith("/")) {
|
||||
sendListing(ctx, file, uri);
|
||||
} else {
|
||||
sendRedirect(ctx, uri + '/');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.isFile()) {
|
||||
sendError(ctx, FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
// 文件缓存判断
|
||||
String ifModifiedSince = request.headers().get(HttpHeaderNames.IF_MODIFIED_SINCE);
|
||||
if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||
Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);
|
||||
|
||||
// 比较文件修改的时间
|
||||
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
|
||||
long fileLastModifiedSeconds = file.lastModified() / 1000;
|
||||
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
|
||||
sendNotModified(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RandomAccessFile raf;
|
||||
try {
|
||||
raf = new RandomAccessFile(file, "r");
|
||||
} catch (FileNotFoundException ignore) {
|
||||
sendError(ctx, NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
long fileLength = raf.length();
|
||||
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
HttpUtil.setContentLength(response, fileLength);
|
||||
setContentTypeHeader(response, file);
|
||||
setDateAndCacheHeaders(response, file);
|
||||
|
||||
if (!keepAlive) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
|
||||
} else if (request.protocolVersion().equals(HTTP_1_0)) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||
}
|
||||
|
||||
// 头部部分
|
||||
ctx.write(response);
|
||||
|
||||
// 内容部分
|
||||
ChannelFuture sendFileFuture;
|
||||
ChannelFuture lastContentFuture;
|
||||
// sendFileFuture =
|
||||
// ctx.write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
|
||||
// // 结束部分
|
||||
// lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
|
||||
sendFileFuture =
|
||||
ctx.writeAndFlush(new HttpChunkedInput(new ChunkedFile(raf, 0, fileLength, 8192)),
|
||||
ctx.newProgressivePromise());
|
||||
// HttpChunkedInput 会自动填写 LastHttpContent部分
|
||||
lastContentFuture = sendFileFuture;
|
||||
|
||||
sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
|
||||
@Override
|
||||
public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
|
||||
if (total < 0) {
|
||||
log.info(future.channel() + " 传输进度: " + progress);
|
||||
} else {
|
||||
log.info(future.channel() + " 传输进度: " + progress + " / " + total);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operationComplete(ChannelProgressiveFuture future) {
|
||||
log.info(future.channel() + " 传输完毕.");
|
||||
}
|
||||
});
|
||||
|
||||
// 是否关闭连接
|
||||
if (!keepAlive) {
|
||||
// 当最后一部分也写出之后,再关闭
|
||||
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
if (ctx.channel().isActive()) {
|
||||
sendError(ctx, INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
|
||||
|
||||
//对URL进行处理
|
||||
private static String sanitizeUri(String uri) {
|
||||
// Decode the path.
|
||||
try {
|
||||
uri = URLDecoder.decode(uri, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
if (uri.isEmpty() || uri.charAt(0) != '/') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 替换文件路径为服务器中的路径
|
||||
uri = uri.replace('/', File.separatorChar);
|
||||
|
||||
// 保证文件路径访问的安全性
|
||||
if (uri.contains(File.separator + '.') ||
|
||||
uri.contains('.' + File.separator) ||
|
||||
uri.charAt(0) == '.' || uri.charAt(uri.length() - 1) == '.' ||
|
||||
INSECURE_URI.matcher(uri).matches()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String filePath=SystemPropertyUtil.get("user.dir") + File.separator + uri;
|
||||
// 转换成绝对路径
|
||||
return filePath;
|
||||
}
|
||||
|
||||
//允许被列出的文件
|
||||
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
|
||||
|
||||
|
||||
private void sendListing(ChannelHandlerContext ctx, File dir, String dirPath) {
|
||||
StringBuilder buf = new StringBuilder()
|
||||
.append("<!DOCTYPE html>\r\n")
|
||||
.append("<html><head><meta charset='utf-8' /><title>")
|
||||
.append(dirPath)
|
||||
.append("中的文件列表")
|
||||
.append("</title></head><body>\r\n")
|
||||
|
||||
.append("<h3>")
|
||||
.append(dirPath)
|
||||
.append("中的文件列表")
|
||||
.append("</h3>\r\n")
|
||||
|
||||
.append("<ul>")
|
||||
.append("<li><a href=\"../\">..</a></li>\r\n");
|
||||
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (File f: files) {
|
||||
if (f.isHidden() || !f.canRead()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = f.getName();
|
||||
if (!ALLOWED_FILE_NAME.matcher(name).matches()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.append("<li><a href=\"")
|
||||
.append(name)
|
||||
.append("\">")
|
||||
.append(name)
|
||||
.append("</a></li>\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
buf.append("</ul></body></html>\r\n");
|
||||
|
||||
ByteBuf buffer = ctx.alloc().buffer(buf.length());
|
||||
buffer.writeCharSequence(buf.toString(), CharsetUtil.UTF_8);
|
||||
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, buffer);
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
|
||||
sendAndCleanupConnection(ctx, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
private void sendRedirect(ChannelHandlerContext ctx, String newUri) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND, Unpooled.EMPTY_BUFFER);
|
||||
response.headers().set(HttpHeaderNames.LOCATION, newUri);
|
||||
|
||||
sendAndCleanupConnection(ctx, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 异常
|
||||
*/
|
||||
private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HTTP_1_1, status, Unpooled.copiedBuffer("异常: " + status + "\r\n", CharsetUtil.UTF_8));
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
|
||||
sendAndCleanupConnection(ctx, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果文件没有修改,则发送 "304 Not Modified"
|
||||
*/
|
||||
private void sendNotModified(ChannelHandlerContext ctx) {
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED, Unpooled.EMPTY_BUFFER);
|
||||
setDateHeader(response);
|
||||
|
||||
sendAndCleanupConnection(ctx, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断请求是否是 Keep-Alive,根据请求的Keep-Alive判断是否关闭连接
|
||||
*/
|
||||
private void sendAndCleanupConnection(ChannelHandlerContext ctx, FullHttpResponse response) {
|
||||
final boolean keepAlive = HttpUtil.isKeepAlive(request);
|
||||
HttpUtil.setContentLength(response, response.content().readableBytes());
|
||||
if (!keepAlive) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
|
||||
} else if (request.protocolVersion().equals(HTTP_1_0)) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||
}
|
||||
|
||||
ChannelFuture flushPromise = ctx.writeAndFlush(response);
|
||||
|
||||
if (!keepAlive) {
|
||||
flushPromise.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置时间头
|
||||
*/
|
||||
private static void setDateHeader(FullHttpResponse response) {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
|
||||
|
||||
Calendar time = new GregorianCalendar();
|
||||
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置时间头和缓存头
|
||||
*/
|
||||
private static void setDateAndCacheHeaders(HttpResponse response, File fileToCache) {
|
||||
SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
|
||||
dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));
|
||||
|
||||
// 日期 header
|
||||
Calendar time = new GregorianCalendar();
|
||||
log.info(dateFormatter.format(time.getTime()));
|
||||
|
||||
response.headers().set(HttpHeaderNames.DATE, dateFormatter.format(time.getTime()));
|
||||
|
||||
// 缓存 headers
|
||||
time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
|
||||
response.headers().set(HttpHeaderNames.EXPIRES, dateFormatter.format(time.getTime()));
|
||||
response.headers().set(HttpHeaderNames.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
|
||||
response.headers().set(
|
||||
HttpHeaderNames.LAST_MODIFIED, dateFormatter.format(new Date(fileToCache.lastModified())));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件的名字设置合适的content-type
|
||||
*/
|
||||
private static void setContentTypeHeader(HttpResponse response, File file) {
|
||||
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean20.httpfile;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
|
||||
public class HttpFileServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||
pipeline.addLast(new ChunkedWriteHandler());
|
||||
pipeline.addLast(new HttpFileServerHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean21.httpupload;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.codec.http.cookie.ClientCookieEncoder;
|
||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.netty.handler.codec.http.multipart.*;
|
||||
import io.netty.util.internal.SocketUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* 文件上传客户端
|
||||
*/
|
||||
@Slf4j
|
||||
public final class HttpUploadClient {
|
||||
|
||||
static final String BASE_URL = System.getProperty("baseUrl", "http://127.0.0.1:8000/");
|
||||
static final String FILE = System.getProperty("file", "file.txt");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String postSimple = BASE_URL + "post";
|
||||
String postFile= BASE_URL + "postmultipart";
|
||||
String get= BASE_URL + "get";
|
||||
|
||||
URI uriSimple = new URI(postSimple);
|
||||
String host = uriSimple.getHost();
|
||||
int port = uriSimple.getPort();
|
||||
|
||||
URI uriFile = new URI(postFile);
|
||||
File file = new File(FILE);
|
||||
log.info(file.getCanonicalPath());
|
||||
if (!file.canRead()) {
|
||||
throw new FileNotFoundException(FILE);
|
||||
}
|
||||
|
||||
EventLoopGroup group = new NioEventLoopGroup();
|
||||
|
||||
// 创建HttpDataFactory 默认是放在内存空间,当超出MINSIZE则会存放在Disk中
|
||||
HttpDataFactory factory = new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
|
||||
|
||||
DiskFileUpload.deleteOnExitTemporaryFile = true; // 在退出的时候删除文件
|
||||
DiskFileUpload.baseDirectory = null;
|
||||
DiskAttribute.deleteOnExitTemporaryFile = true; // 在退出的时候删除文件
|
||||
DiskAttribute.baseDirectory = null;
|
||||
|
||||
try {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group).channel(NioSocketChannel.class)
|
||||
.handler(new HttpUploadClientInitializer());
|
||||
|
||||
// Simple Get form
|
||||
List<Entry<String, String>> headers = formget(b, host, port, get, uriSimple);
|
||||
if (headers == null) {
|
||||
factory.cleanAllHttpData();
|
||||
return;
|
||||
}
|
||||
|
||||
// Simple Post form
|
||||
List<InterfaceHttpData> bodylist = formpost(b, host, port, uriSimple, file, factory, headers);
|
||||
if (bodylist == null) {
|
||||
factory.cleanAllHttpData();
|
||||
return;
|
||||
}
|
||||
|
||||
// Multipart Post form
|
||||
formpostmultipart(b, host, port, uriFile, factory, headers, bodylist);
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
// 清除所有的temp文件
|
||||
factory.cleanAllHttpData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一个标准的HTTP get请求
|
||||
**/
|
||||
private static List<Entry<String, String>> formget(
|
||||
Bootstrap bootstrap, String host, int port, String get, URI uriSimple) throws Exception {
|
||||
Channel channel = bootstrap.connect(host, port).sync().channel();
|
||||
// HTTP请求
|
||||
QueryStringEncoder encoder = new QueryStringEncoder(get);
|
||||
// 添加请求参数
|
||||
encoder.addParam("method", "GET");
|
||||
encoder.addParam("name", "flydean");
|
||||
encoder.addParam("site", "www.flydean.com");
|
||||
|
||||
URI uriGet = new URI(encoder.toString());
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uriGet.toASCIIString());
|
||||
HttpHeaders headers = request.headers();
|
||||
headers.set(HttpHeaderNames.HOST, host);
|
||||
headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
|
||||
headers.set(HttpHeaderNames.ACCEPT_ENCODING, HttpHeaderValues.GZIP + "," + HttpHeaderValues.DEFLATE);
|
||||
headers.set(HttpHeaderNames.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2");
|
||||
headers.set(HttpHeaderNames.REFERER, uriSimple.toString());
|
||||
headers.set(HttpHeaderNames.USER_AGENT, "Netty Simple Http Client side");
|
||||
headers.set(HttpHeaderNames.ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
|
||||
headers.set(
|
||||
HttpHeaderNames.COOKIE, ClientCookieEncoder.STRICT.encode(
|
||||
new DefaultCookie("name", "flydean"),
|
||||
new DefaultCookie("site", "www.flydean.com"))
|
||||
);
|
||||
|
||||
channel.writeAndFlush(request);
|
||||
|
||||
channel.closeFuture().sync();
|
||||
// 返回header
|
||||
return headers.entries();
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准POST请求
|
||||
*/
|
||||
private static List<InterfaceHttpData> formpost(
|
||||
Bootstrap bootstrap,
|
||||
String host, int port, URI uriSimple, File file, HttpDataFactory factory,
|
||||
List<Entry<String, String>> headers) throws Exception {
|
||||
|
||||
ChannelFuture future = bootstrap.connect(SocketUtils.socketAddress(host, port));
|
||||
Channel channel = future.sync().channel();
|
||||
|
||||
// 构建HTTP request
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriSimple.toASCIIString());
|
||||
|
||||
// Use the PostBody encoder
|
||||
HttpPostRequestEncoder bodyRequestEncoder =
|
||||
new HttpPostRequestEncoder(factory, request, false); // false => not multipart
|
||||
|
||||
// 添加headers
|
||||
for (Entry<String, String> entry : headers) {
|
||||
request.headers().set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
// 添加form属性
|
||||
bodyRequestEncoder.addBodyAttribute("method", "POST");
|
||||
bodyRequestEncoder.addBodyAttribute("name", "flydean");
|
||||
bodyRequestEncoder.addBodyAttribute("site", "www.flydean.com");
|
||||
bodyRequestEncoder.addBodyFileUpload("myfile", file, "application/x-zip-compressed", false);
|
||||
|
||||
// finalize request, 判断是否需要chunk
|
||||
request = bodyRequestEncoder.finalizeRequest();
|
||||
|
||||
// 创建bodylist
|
||||
List<InterfaceHttpData> bodylist = bodyRequestEncoder.getBodyListAttributes();
|
||||
|
||||
// 发送请求
|
||||
channel.write(request);
|
||||
|
||||
// 判断bodyRequestEncoder是否是Chunked,发送请求内容
|
||||
if (bodyRequestEncoder.isChunked()) {
|
||||
channel.write(bodyRequestEncoder);
|
||||
}
|
||||
channel.flush();
|
||||
|
||||
//清除请求
|
||||
// bodyRequestEncoder.cleanFiles();
|
||||
|
||||
// 等待channel关闭
|
||||
channel.closeFuture().sync();
|
||||
return bodylist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multipart form
|
||||
*/
|
||||
private static void formpostmultipart(
|
||||
Bootstrap bootstrap, String host, int port, URI uriFile, HttpDataFactory factory,
|
||||
Iterable<Entry<String, String>> headers, List<InterfaceHttpData> bodylist) throws Exception {
|
||||
ChannelFuture future = bootstrap.connect(SocketUtils.socketAddress(host, port));
|
||||
Channel channel = future.sync().channel();
|
||||
|
||||
// 创建HTTP request
|
||||
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uriFile.toASCIIString());
|
||||
|
||||
// Use the PostBody encoder
|
||||
HttpPostRequestEncoder bodyRequestEncoder =
|
||||
new HttpPostRequestEncoder(factory, request, true); // true => multipart
|
||||
|
||||
// 添加headers
|
||||
for (Entry<String, String> entry : headers) {
|
||||
request.headers().set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
// 添加body http data
|
||||
bodyRequestEncoder.setBodyHttpDatas(bodylist);
|
||||
// finalize request,判断是否需要chunk
|
||||
request = bodyRequestEncoder.finalizeRequest();
|
||||
// 发送请求头
|
||||
channel.write(request);
|
||||
// 判断bodyRequestEncoder是否是Chunked,发送请求内容
|
||||
if (bodyRequestEncoder.isChunked()) {
|
||||
channel.write(bodyRequestEncoder);
|
||||
}
|
||||
channel.flush();
|
||||
// 清除文件
|
||||
bodyRequestEncoder.cleanFiles();
|
||||
channel.closeFuture().sync();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean21.httpupload;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 上传文件处理器
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpUploadClientHandler extends SimpleChannelInboundHandler<HttpObject> {
|
||||
|
||||
private boolean readingChunks;
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {
|
||||
if (msg instanceof HttpResponse) {
|
||||
HttpResponse response = (HttpResponse) msg;
|
||||
|
||||
log.info("STATUS: " + response.status());
|
||||
log.info("VERSION: " + response.protocolVersion());
|
||||
|
||||
if (!response.headers().isEmpty()) {
|
||||
for (CharSequence name : response.headers().names()) {
|
||||
for (CharSequence value : response.headers().getAll(name)) {
|
||||
log.info("HEADER: " + name + " = " + value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (response.status().code() == 200 && HttpUtil.isTransferEncodingChunked(response)) {
|
||||
readingChunks = true;
|
||||
log.info("CHUNKED CONTENT {");
|
||||
} else {
|
||||
log.info("CONTENT {");
|
||||
}
|
||||
}
|
||||
if (msg instanceof HttpContent) {
|
||||
HttpContent chunk = (HttpContent) msg;
|
||||
|
||||
if (chunk instanceof LastHttpContent) {
|
||||
log.info(chunk.content().toString(CharsetUtil.UTF_8));
|
||||
if (readingChunks) {
|
||||
log.info("} END OF CHUNKED CONTENT");
|
||||
} else {
|
||||
log.info("} END OF CONTENT");
|
||||
}
|
||||
readingChunks = false;
|
||||
} else {
|
||||
log.info(chunk.content().toString(CharsetUtil.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.channel().close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean21.httpupload;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpContentDecompressor;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
|
||||
public class HttpUploadClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
pipeline.addLast("codec", new HttpClientCodec());
|
||||
pipeline.addLast("inflater", new HttpContentDecompressor());
|
||||
pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
|
||||
pipeline.addLast("handler", new HttpUploadClientHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean21.httpupload;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* HTTP文件上传服务器
|
||||
*/
|
||||
@Slf4j
|
||||
public final class HttpUploadServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup);
|
||||
b.channel(NioServerSocketChannel.class);
|
||||
b.handler(new LoggingHandler(LogLevel.INFO));
|
||||
b.childHandler(new HttpUploadServerInitializer());
|
||||
|
||||
Channel ch = b.bind(PORT).sync().channel();
|
||||
|
||||
log.info("请打开你的浏览器,访问 http://127.0.0.1:8000/");
|
||||
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean21.httpupload;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.codec.http.cookie.Cookie;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||
import io.netty.handler.codec.http.multipart.*;
|
||||
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
|
||||
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException;
|
||||
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import static io.netty.buffer.Unpooled.copiedBuffer;
|
||||
|
||||
@Slf4j
|
||||
public class HttpUploadServerHandler extends SimpleChannelInboundHandler<HttpObject> {
|
||||
|
||||
private HttpRequest request;
|
||||
|
||||
private HttpData partialContent;
|
||||
|
||||
private final StringBuilder responseContent = new StringBuilder();
|
||||
|
||||
private static final HttpDataFactory factory =
|
||||
new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE);
|
||||
|
||||
private HttpPostRequestDecoder decoder;
|
||||
|
||||
static {
|
||||
DiskFileUpload.deleteOnExitTemporaryFile = true; // 退出的时候删除临时文件
|
||||
DiskFileUpload.baseDirectory = null;
|
||||
DiskAttribute.deleteOnExitTemporaryFile = true; // 退出的时候删除临时文件
|
||||
DiskAttribute.baseDirectory = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) {
|
||||
if (decoder != null) {
|
||||
decoder.cleanFiles();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
|
||||
//处理HttpRequest
|
||||
if (msg instanceof HttpRequest) {
|
||||
HttpRequest request = this.request = (HttpRequest) msg;
|
||||
|
||||
responseContent.setLength(0);
|
||||
responseContent.append("请求响应开始\r\n");
|
||||
responseContent.append("===================================\r\n");
|
||||
responseContent.append("VERSION: ").append(request.protocolVersion().text()).append("\r\n");
|
||||
responseContent.append("REQUEST_URI: ").append(request.uri()).append("\r\n");
|
||||
|
||||
// 添加header值
|
||||
for (Entry<String, String> entry : request.headers()) {
|
||||
responseContent.append("HEADER: ").append(entry.getKey()).append('=').append(entry.getValue()).append("\r\n");
|
||||
}
|
||||
|
||||
// 设置cookie值
|
||||
Set<Cookie> cookies;
|
||||
String value = request.headers().get(HttpHeaderNames.COOKIE);
|
||||
if (value == null) {
|
||||
cookies = Collections.emptySet();
|
||||
} else {
|
||||
cookies = ServerCookieDecoder.STRICT.decode(value);
|
||||
}
|
||||
for (Cookie cookie : cookies) {
|
||||
responseContent.append("COOKIE: ").append(cookie).append("\r\n");
|
||||
}
|
||||
|
||||
//解析URL中的参数
|
||||
QueryStringDecoder decoderQuery = new QueryStringDecoder(request.uri());
|
||||
Map<String, List<String>> uriAttributes = decoderQuery.parameters();
|
||||
for (Entry<String, List<String>> attr: uriAttributes.entrySet()) {
|
||||
for (String attrVal: attr.getValue()) {
|
||||
responseContent.append("URI: ").append(attr.getKey()).append('=').append(attrVal).append("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
//GET请求
|
||||
if (HttpMethod.GET.equals(request.method())) {
|
||||
responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
//POST请求
|
||||
decoder = new HttpPostRequestDecoder(factory, request);
|
||||
} catch (ErrorDataDecoderException e1) {
|
||||
// 异常处理
|
||||
log.error("出现异常",e1);
|
||||
responseContent.append(e1.getMessage());
|
||||
writeResponse(ctx.channel(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean readingChunks = HttpUtil.isTransferEncodingChunked(request);
|
||||
responseContent.append("Is Chunked: ").append(readingChunks).append("\r\n");
|
||||
responseContent.append("IsMultipart: ").append(decoder.isMultipart()).append("\r\n");
|
||||
if (readingChunks) {
|
||||
responseContent.append("Chunks: ");
|
||||
}
|
||||
}
|
||||
|
||||
//处理HttpContent
|
||||
// 如果POST的decoder存在
|
||||
if (decoder != null) {
|
||||
if (msg instanceof HttpContent) {
|
||||
HttpContent chunk = (HttpContent) msg;
|
||||
try {
|
||||
decoder.offer(chunk);
|
||||
} catch (ErrorDataDecoderException e1) {
|
||||
// 异常处理
|
||||
log.error("出现异常",e1);
|
||||
responseContent.append(e1.getMessage());
|
||||
writeResponse(ctx.channel(), true);
|
||||
return;
|
||||
}
|
||||
//读取httpData
|
||||
log.info("readHttpDataChunkByChunk");
|
||||
readHttpDataChunkByChunk();
|
||||
// 读最后一部分
|
||||
if (chunk instanceof LastHttpContent) {
|
||||
writeResponse(ctx.channel());
|
||||
reset();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeResponse(ctx.channel());
|
||||
}
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
request = null;
|
||||
//destory decoder
|
||||
decoder.destroy();
|
||||
decoder = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取HttpData
|
||||
*/
|
||||
private void readHttpDataChunkByChunk() {
|
||||
try {
|
||||
while (decoder.hasNext()) {
|
||||
log.info("decoder has next");
|
||||
InterfaceHttpData data = decoder.next();
|
||||
if (data != null) {
|
||||
// 检测当前的 HttpData 如果是一个 FileUpload 并且和上一个 partialContent一致,表示是最后一个HttpData
|
||||
if (partialContent == data) {
|
||||
log.info(" 100% (FinalSize: " + partialContent.length() + ")");
|
||||
partialContent = null;
|
||||
}
|
||||
// new value
|
||||
writeHttpData(data);
|
||||
}
|
||||
}
|
||||
//如果decoder并没有结束
|
||||
// 检测当前的 partial 数据
|
||||
InterfaceHttpData data = decoder.currentPartialHttpData();
|
||||
if (data != null) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (partialContent == null) {
|
||||
partialContent = (HttpData) data;
|
||||
if (partialContent instanceof FileUpload) {
|
||||
builder.append("Start FileUpload: ")
|
||||
.append(((FileUpload) partialContent).getFilename()).append(" ");
|
||||
} else {
|
||||
builder.append("Start Attribute: ")
|
||||
.append(partialContent.getName()).append(" ");
|
||||
}
|
||||
builder.append("(DefinedSize: ").append(partialContent.definedLength()).append(")");
|
||||
}
|
||||
if (partialContent.definedLength() > 0) {
|
||||
builder.append(" ").append(partialContent.length() * 100 / partialContent.definedLength())
|
||||
.append("% ");
|
||||
log.info(builder.toString());
|
||||
} else {
|
||||
builder.append(" ").append(partialContent.length()).append(" ");
|
||||
log.info(builder.toString());
|
||||
}
|
||||
}
|
||||
} catch (EndOfDataDecoderException e1) {
|
||||
// end
|
||||
responseContent.append("END OF CONTENT CHUNK BY CHUNK\r\n\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeHttpData(InterfaceHttpData data) {
|
||||
if (data.getHttpDataType() == HttpDataType.Attribute) {
|
||||
Attribute attribute = (Attribute) data;
|
||||
String value;
|
||||
try {
|
||||
value = attribute.getValue();
|
||||
} catch (IOException e1) {
|
||||
// 异常处理
|
||||
log.error("出现异常",e1);
|
||||
responseContent.append("BODY Attribute: ").append(attribute.getHttpDataType().name()).append(": ").append(attribute.getName()).append(" 读取数据出错: ").append(e1.getMessage()).append("\r\n");
|
||||
return;
|
||||
}
|
||||
if (value.length() > 100) {
|
||||
responseContent.append("BODY Attribute: ").append(attribute.getHttpDataType().name()).append(": ").append(attribute.getName()).append("数据太长了\r\n");
|
||||
} else {
|
||||
responseContent.append("BODY Attribute: ").append(attribute.getHttpDataType().name()).append(": ").append(attribute).append("\r\n");
|
||||
}
|
||||
} else {
|
||||
responseContent.append("BODY FileUpload: ").append(data.getHttpDataType().name()).append(": ").append(data).append("\r\n");
|
||||
if (data.getHttpDataType() == HttpDataType.FileUpload) {
|
||||
FileUpload fileUpload = (FileUpload) data;
|
||||
if (fileUpload.isCompleted()) {
|
||||
if (fileUpload.length() < 10000) {
|
||||
responseContent.append("文件内容如下:\r\n");
|
||||
try {
|
||||
responseContent.append(fileUpload.getString(fileUpload.getCharset()));
|
||||
} catch (IOException e1) {
|
||||
// 异常处理
|
||||
log.error("出现异常",e1);
|
||||
}
|
||||
responseContent.append("\r\n");
|
||||
} else {
|
||||
responseContent.append("文件太长了:").append(fileUpload.length()).append("\r\n");
|
||||
}
|
||||
} else {
|
||||
responseContent.append("文件接收有误!\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeResponse(Channel channel) {
|
||||
writeResponse(channel, false);
|
||||
}
|
||||
|
||||
private void writeResponse(Channel channel, boolean forceClose) {
|
||||
ByteBuf buf = copiedBuffer(responseContent.toString(), CharsetUtil.UTF_8);
|
||||
responseContent.setLength(0);
|
||||
|
||||
boolean keepAlive = HttpUtil.isKeepAlive(request) && !forceClose;
|
||||
|
||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
|
||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||
response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes());
|
||||
|
||||
if (!keepAlive) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
|
||||
} else if (request.protocolVersion().equals(HttpVersion.HTTP_1_0)) {
|
||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
||||
}
|
||||
|
||||
Set<Cookie> cookies;
|
||||
String value = request.headers().get(HttpHeaderNames.COOKIE);
|
||||
if (value == null) {
|
||||
cookies = Collections.emptySet();
|
||||
} else {
|
||||
cookies = ServerCookieDecoder.STRICT.decode(value);
|
||||
}
|
||||
if (!cookies.isEmpty()) {
|
||||
//设置cookie
|
||||
for (Cookie cookie : cookies) {
|
||||
response.headers().add(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
|
||||
}
|
||||
}
|
||||
// 写入response
|
||||
ChannelFuture future = channel.writeAndFlush(response);
|
||||
// 关闭连接
|
||||
if (!keepAlive) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.channel().close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean21.httpupload;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpContentCompressor;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
|
||||
public class HttpUploadServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
pipeline.addLast(new HttpRequestDecoder());
|
||||
pipeline.addLast(new HttpResponseEncoder());
|
||||
pipeline.addLast(new HttpContentCompressor());
|
||||
pipeline.addLast(new HttpUploadServerHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean22.cors;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
/**
|
||||
* 自定义响应handler
|
||||
*/
|
||||
public class CustResponseHandler extends SimpleChannelInboundHandler<Object> {
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, Object msg) {
|
||||
final FullHttpResponse response = new DefaultFullHttpResponse(
|
||||
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER);
|
||||
response.headers().set("site", "www.flydean.com");
|
||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean22.cors;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
public final class HttpCorsServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port","8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.handler(new LoggingHandler(LogLevel.INFO))
|
||||
.childHandler(new HttpCorsServerInitializer());
|
||||
|
||||
b.bind(PORT).sync().channel().closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean22.cors;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.cors.CorsConfig;
|
||||
import io.netty.handler.codec.http.cors.CorsConfigBuilder;
|
||||
import io.netty.handler.codec.http.cors.CorsHandler;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
|
||||
public class HttpCorsServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) {
|
||||
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
pipeline.addLast(new HttpResponseEncoder());
|
||||
pipeline.addLast(new HttpRequestDecoder());
|
||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||
pipeline.addLast(new ChunkedWriteHandler());
|
||||
|
||||
CorsConfig corsConfig = CorsConfigBuilder.forAnyOrigin().allowNullOrigin().allowCredentials().build();
|
||||
pipeline.addLast(new CorsHandler(corsConfig));
|
||||
|
||||
pipeline.addLast(new CustResponseHandler());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean23.socketserver;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* 读取html页面的数据,展示给用户
|
||||
*/
|
||||
@Slf4j
|
||||
public final class TestSocketHttpPage {
|
||||
|
||||
public static ByteBuf getContent() throws IOException {
|
||||
URL url= TestSocketHttpPage.class.getClassLoader().getResource("socket.html");
|
||||
log.info("url: {}",url);
|
||||
String filePath = url.getFile();
|
||||
File file = new File(filePath);
|
||||
log.info(file.getCanonicalPath());
|
||||
FileReader fileReader = new FileReader(file);
|
||||
BufferedReader reader = new BufferedReader(fileReader);
|
||||
StringBuilder builder= new StringBuilder();
|
||||
reader.lines().forEach(builder::append);
|
||||
reader.close();
|
||||
fileReader.close();
|
||||
return Unpooled.copiedBuffer(builder.toString(), CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
private TestSocketHttpPage() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean23.socketserver;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
@Slf4j
|
||||
public final class TestSocketServer {
|
||||
|
||||
static final int PORT = Integer.parseInt(System.getProperty("port", "8000"));
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
try {
|
||||
ServerBootstrap b = new ServerBootstrap();
|
||||
b.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.childHandler(new TestSocketServerInitializer());
|
||||
|
||||
Channel ch = b.bind(PORT).sync().channel();
|
||||
|
||||
log.info("使用你的浏览器访问:" + "http://127.0.0.1:" + PORT + '/');
|
||||
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean23.socketserver;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import io.netty.handler.codec.http.websocketx.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpMethod.GET;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
||||
|
||||
/**
|
||||
* 同时处理消息和
|
||||
*/
|
||||
@Slf4j
|
||||
public class TestSocketServerHandler extends SimpleChannelInboundHandler<Object> {
|
||||
|
||||
private static final String WEBSOCKET_PATH = "/websocket";
|
||||
|
||||
private WebSocketServerHandshaker handshaker;
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws IOException {
|
||||
//根据消息类型,处理两种不同的消息
|
||||
if (msg instanceof FullHttpRequest) {
|
||||
handleHttpRequest(ctx, (FullHttpRequest) msg);
|
||||
} else if (msg instanceof WebSocketFrame) {
|
||||
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelReadComplete(ChannelHandlerContext ctx) {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws IOException {
|
||||
// 处理异常
|
||||
if (!req.decoderResult().isSuccess()) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(req.protocolVersion(), BAD_REQUEST,
|
||||
ctx.alloc().buffer(0)));
|
||||
return;
|
||||
}
|
||||
|
||||
// 只允许get请求
|
||||
if (!GET.equals(req.method())) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(req.protocolVersion(), FORBIDDEN,
|
||||
ctx.alloc().buffer(0)));
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送测试页面
|
||||
if ("/".equals(req.uri())) {
|
||||
ByteBuf content = TestSocketHttpPage.getContent();
|
||||
FullHttpResponse res = new DefaultFullHttpResponse(req.protocolVersion(), OK, content);
|
||||
|
||||
res.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
HttpUtil.setContentLength(res, content.readableBytes());
|
||||
|
||||
sendHttpResponse(ctx, req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
// websocket握手
|
||||
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
|
||||
getWebSocketLocation(req), null, true, 5 * 1024 * 1024);
|
||||
handshaker = wsFactory.newHandshaker(req);
|
||||
if (handshaker == null) {
|
||||
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
|
||||
} else {
|
||||
handshaker.handshake(ctx.channel(), req);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
|
||||
|
||||
// 处理各种websocket的frame信息
|
||||
if (frame instanceof CloseWebSocketFrame) {
|
||||
handshaker.close(ctx, (CloseWebSocketFrame) frame.retain());
|
||||
return;
|
||||
}
|
||||
if (frame instanceof PingWebSocketFrame) {
|
||||
ctx.write(new PongWebSocketFrame(frame.content().retain()));
|
||||
return;
|
||||
}
|
||||
if (frame instanceof TextWebSocketFrame) {
|
||||
// 直接返回
|
||||
ctx.write(frame.retain());
|
||||
return;
|
||||
}
|
||||
if (frame instanceof BinaryWebSocketFrame) {
|
||||
// 直接返回
|
||||
ctx.write(frame.retain());
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
|
||||
// 生成错误页面
|
||||
HttpResponseStatus responseStatus = res.status();
|
||||
if (responseStatus.code() != 200) {
|
||||
ByteBufUtil.writeUtf8(res.content(), responseStatus.toString());
|
||||
HttpUtil.setContentLength(res, res.content().readableBytes());
|
||||
}
|
||||
// 发送response
|
||||
boolean keepAlive = HttpUtil.isKeepAlive(req) && responseStatus.code() == 200;
|
||||
HttpUtil.setKeepAlive(res, keepAlive);
|
||||
ChannelFuture future = ctx.write(res);
|
||||
if (!keepAlive) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||
String location = req.headers().get(HttpHeaderNames.HOST) + WEBSOCKET_PATH;
|
||||
return "ws://" + location;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean23.socketserver;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class TestSocketServerInitializer extends ChannelInitializer<SocketChannel> {
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception {
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
pipeline.addLast(new HttpServerCodec());
|
||||
pipeline.addLast(new HttpObjectAggregator(65536));
|
||||
pipeline.addLast(new TestSocketServerHandler());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean24.socketserver2;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 专门处理WebSocketFrame
|
||||
*/
|
||||
public class Server2FrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
|
||||
|
||||
if (frame instanceof TextWebSocketFrame) {
|
||||
// 将接收到的消息转换成为大写
|
||||
String request = ((TextWebSocketFrame) frame).text();
|
||||
ctx.channel().writeAndFlush(new TextWebSocketFrame(request.toUpperCase(Locale.CHINA)));
|
||||
} else {
|
||||
String message = "不支持的Frame类型: " + frame.getClass().getName();
|
||||
throw new UnsupportedOperationException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean24.socketserver2;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.handler.codec.http.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
|
||||
import static io.netty.handler.codec.http.HttpMethod.GET;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
||||
|
||||
/**
|
||||
* 专门处理HTTP请求
|
||||
*/
|
||||
@Slf4j
|
||||
public class Server2HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
|
||||
// 处理异常请求
|
||||
if (!req.decoderResult().isSuccess()) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(req.protocolVersion(), BAD_REQUEST,
|
||||
ctx.alloc().buffer(0)));
|
||||
return;
|
||||
}
|
||||
|
||||
// 只运行get方法
|
||||
if (!GET.equals(req.method())) {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(req.protocolVersion(), FORBIDDEN,
|
||||
ctx.alloc().buffer(0)));
|
||||
return;
|
||||
}
|
||||
|
||||
// index页面
|
||||
if ("/".equals(req.uri()) || "/index.html".equals(req.uri())) {
|
||||
ByteBuf content = Server2HttpPage.getContent();
|
||||
FullHttpResponse res = new DefaultFullHttpResponse(req.protocolVersion(), OK, content);
|
||||
|
||||
res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||
HttpUtil.setContentLength(res, content.readableBytes());
|
||||
|
||||
sendHttpResponse(ctx, req, res);
|
||||
} else {
|
||||
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(req.protocolVersion(), NOT_FOUND,
|
||||
ctx.alloc().buffer(0)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
// 异常处理
|
||||
log.error("出现异常",cause);
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
|
||||
// 生成error页面
|
||||
HttpResponseStatus responseStatus = res.status();
|
||||
if (responseStatus.code() != 200) {
|
||||
ByteBufUtil.writeUtf8(res.content(), responseStatus.toString());
|
||||
HttpUtil.setContentLength(res, res.content().readableBytes());
|
||||
}
|
||||
// 发送响应并关闭连接
|
||||
boolean keepAlive = HttpUtil.isKeepAlive(req) && responseStatus.code() == 200;
|
||||
HttpUtil.setKeepAlive(res, keepAlive);
|
||||
ChannelFuture future = ctx.writeAndFlush(res);
|
||||
if (!keepAlive) {
|
||||
future.addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022 learn-netty4 Project
|
||||
*
|
||||
* The learn-netty4 Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.flydean24.socketserver2;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
@Slf4j
|
||||
public final class Server2HttpPage {
|
||||
|
||||
public static ByteBuf getContent() throws IOException {
|
||||
URL url= Server2HttpPage.class.getClassLoader().getResource("socket2.html");
|
||||
log.info("url: {}",url);
|
||||
String filePath = url.getFile();
|
||||
File file = new File(filePath);
|
||||
log.info(file.getCanonicalPath());
|
||||
FileReader fileReader = new FileReader(file);
|
||||
BufferedReader reader = new BufferedReader(fileReader);
|
||||
StringBuilder builder= new StringBuilder();
|
||||
reader.lines().forEach(builder::append);
|
||||
reader.close();
|
||||
fileReader.close();
|
||||
return Unpooled.copiedBuffer(builder.toString(), CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
private Server2HttpPage() {
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user