Initial commit

This commit is contained in:
2023-08-27 10:27:58 +08:00
commit 260381856c
393 changed files with 36607 additions and 0 deletions

33
learn-netty4/.gitignore vendored Normal file
View 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
View 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
View 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
View 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吧 ![Github stars](https://img.shields.io/github/stars/ddean2009/learn-netty4.svg)
系列文章请参考:[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

File diff suppressed because it is too large Load Diff

View 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
View 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>

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View 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.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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View 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.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();
}
}

View File

@@ -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());
}
});
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View 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();
}
}
}

View File

@@ -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();
}
}

View 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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
};
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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));
}
}

View File

@@ -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); // 最终的数据
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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()));
}
}

View File

@@ -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.");
}
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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()));
}
}

View File

@@ -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());
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View 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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}

View File

@@ -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)
}

View File

@@ -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();
}
}
}

View 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.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();
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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());
}
}

View File

@@ -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() {
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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