├── spring-boot-netty ├── study-netty │ ├── study-client │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ └── application.properties │ │ │ │ └── java │ │ │ │ │ └── org │ │ │ │ │ └── yyx │ │ │ │ │ └── netty │ │ │ │ │ └── study │ │ │ │ │ ├── websocket │ │ │ │ │ ├── WebSocketConstant.java │ │ │ │ │ ├── WebSocketMessage.java │ │ │ │ │ ├── WebSocketHandlerClient.java │ │ │ │ │ ├── WebSocketClient.java │ │ │ │ │ ├── WebSocketUsers.java │ │ │ │ │ └── WebSocketClientHandler.java │ │ │ │ │ ├── StudyClientApplication.java │ │ │ │ │ ├── codec │ │ │ │ │ └── msgpack │ │ │ │ │ │ ├── MsgPackDecoder.java │ │ │ │ │ │ └── MsgPackEncoder.java │ │ │ │ │ ├── echo │ │ │ │ │ ├── delimiter │ │ │ │ │ │ ├── EchoClientHandler.java │ │ │ │ │ │ └── EchoClient.java │ │ │ │ │ ├── fixlength │ │ │ │ │ │ ├── EchoClientHandler.java │ │ │ │ │ │ └── EchoClient.java │ │ │ │ │ └── megpack │ │ │ │ │ │ ├── MessagePackClientHandler.java │ │ │ │ │ │ └── MessagePackClient.java │ │ │ │ │ └── time │ │ │ │ │ ├── demo1 │ │ │ │ │ ├── TimeClient.java │ │ │ │ │ └── TimeClientHandler.java │ │ │ │ │ ├── demo2 │ │ │ │ │ ├── TimeClient.java │ │ │ │ │ └── TimeClientHandler.java │ │ │ │ │ └── demo3 │ │ │ │ │ ├── TimeClient.java │ │ │ │ │ └── TimeClientHandler.java │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── yyx │ │ │ │ └── netty │ │ │ │ └── study │ │ │ │ └── StudyClientApplicationTests.java │ │ └── pom.xml │ ├── study-server │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ └── application.properties │ │ │ │ └── java │ │ │ │ │ └── org │ │ │ │ │ └── yyx │ │ │ │ │ └── netty │ │ │ │ │ └── study │ │ │ │ │ ├── websocket │ │ │ │ │ ├── WebSocketConstant.java │ │ │ │ │ ├── WebSocketMessage.java │ │ │ │ │ ├── WebSocketChildHandler.java │ │ │ │ │ ├── WebSocketServer.java │ │ │ │ │ ├── WebSocketUsers.java │ │ │ │ │ └── WebSocketServerHandler.java │ │ │ │ │ ├── StudyServerApplication.java │ │ │ │ │ ├── echo │ │ │ │ │ ├── fixlength │ │ │ │ │ │ ├── EchoServerHandler.java │ │ │ │ │ │ └── EchoServer.java │ │ │ │ │ ├── delimiter │ │ │ │ │ │ ├── EchoServerHandler.java │ │ │ │ │ │ └── EchoServer.java │ │ │ │ │ └── megpack │ │ │ │ │ │ ├── MessagePackServerHandler.java │ │ │ │ │ │ └── MessagePackServer.java │ │ │ │ │ ├── time │ │ │ │ │ ├── demo1 │ │ │ │ │ │ ├── ChildChannelHandler.java │ │ │ │ │ │ ├── TimeServer.java │ │ │ │ │ │ └── TimeServerHandler.java │ │ │ │ │ ├── demo2 │ │ │ │ │ │ ├── ChildChannelHandler.java │ │ │ │ │ │ ├── TimeServer.java │ │ │ │ │ │ └── TimeServerHandler.java │ │ │ │ │ └── demo3 │ │ │ │ │ │ ├── ChildChannelHandler.java │ │ │ │ │ │ ├── TimeServerHandler.java │ │ │ │ │ │ └── TimeServer.java │ │ │ │ │ └── codec │ │ │ │ │ └── msgpack │ │ │ │ │ ├── MsgPackEncoder.java │ │ │ │ │ └── MsgPackDecoder.java │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── yyx │ │ │ │ └── netty │ │ │ │ └── study │ │ │ │ ├── StudyServerApplicationTests.java │ │ │ │ └── CodeCTest.java │ │ └── pom.xml │ └── pom.xml ├── netty-demo │ ├── netty-server │ │ ├── README.md │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ └── application.yml │ │ │ │ └── java │ │ │ │ │ └── org │ │ │ │ │ └── yyx │ │ │ │ │ └── netty │ │ │ │ │ ├── NettyServerApplication.java │ │ │ │ │ ├── rpc │ │ │ │ │ └── service │ │ │ │ │ │ └── DemoService.java │ │ │ │ │ └── server │ │ │ │ │ ├── config │ │ │ │ │ └── NettyServerConfig.java │ │ │ │ │ ├── impl │ │ │ │ │ └── DemoServiceImpl.java │ │ │ │ │ ├── adapter │ │ │ │ │ └── ServerChannelHandlerAdapter.java │ │ │ │ │ ├── listener │ │ │ │ │ └── NettyServerListener.java │ │ │ │ │ └── dispatcher │ │ │ │ │ └── RequestDispatcher.java │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── yyx │ │ │ │ └── netty │ │ │ │ └── NettyServerApplicationTests.java │ │ └── pom.xml │ ├── netty-client │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ └── application.yml │ │ │ │ └── java │ │ │ │ │ └── org │ │ │ │ │ └── yyx │ │ │ │ │ └── netty │ │ │ │ │ ├── NettyClientApplication.java │ │ │ │ │ ├── config │ │ │ │ │ ├── NettyConfig.java │ │ │ │ │ └── NettyConfiguration.java │ │ │ │ │ ├── rpc │ │ │ │ │ ├── service │ │ │ │ │ │ └── DemoService.java │ │ │ │ │ └── util │ │ │ │ │ │ ├── WrapMethodUtils.java │ │ │ │ │ │ ├── RemoteMethodInvokeUtil.java │ │ │ │ │ │ ├── NettyBeanScanner.java │ │ │ │ │ │ ├── ChannelUtil.java │ │ │ │ │ │ └── PackageClassUtils.java │ │ │ │ │ ├── client │ │ │ │ │ ├── NettyClientHandler.java │ │ │ │ │ ├── NettyClientListener.java │ │ │ │ │ ├── NettyClient.java │ │ │ │ │ ├── NettyClientHandlerAdapter.java │ │ │ │ │ └── RPCProxyFactoryBean.java │ │ │ │ │ └── action │ │ │ │ │ └── MainAction.java │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── yyx │ │ │ │ └── netty │ │ │ │ └── client │ │ │ │ └── NettyClientApplicationTests.java │ │ ├── README.md │ │ └── pom.xml │ └── pom.xml ├── netty-commons │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── org │ │ │ └── yyx │ │ │ └── netty │ │ │ ├── exception │ │ │ ├── NoUseableChannel.java │ │ │ └── ErrorParamsException.java │ │ │ ├── entity │ │ │ ├── NullWritable.java │ │ │ ├── User.java │ │ │ └── MethodInvokeMeta.java │ │ │ └── util │ │ │ ├── ObjectCodec.java │ │ │ └── ObjectSerializerUtils.java │ └── pom.xml └── pom.xml ├── .gitignore └── README.md /spring-boot-netty/study-netty/study-client/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/README.md: -------------------------------------------------------------------------------- 1 | # netty-server 2 | >博文源码,基于SOA架构的服务端,使用Netty做RPC通信. 3 | 4 | 这是一个Netty服务端与SpringBoot集成的Demo. -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | netty: 2 | port: 12345 3 | basePackage: org.yyx.netty.rpc.service; 4 | clientName: nettyClient 5 | url: 127.0.0.1 -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # netty配置 2 | netty: 3 | # 端口号 4 | port: 12345 5 | # 最大线程数 6 | maxThreads: 1024 7 | # 数据包的最大长度 8 | max_frame_length: 65535 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | *.DS_Store 24 | *.iml 25 | .idea/ 26 | target/ 27 | 28 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/test/java/org/yyx/netty/NettyServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class NettyServerApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/test/java/org/yyx/netty/client/NettyClientApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.client; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class NettyClientApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/exception/NoUseableChannel.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.exception; 2 | 3 | /** 4 | * 没有可用的通道异常 5 | *

6 | * 7 | * @author 叶云轩 at marquis_xuan@163.com 8 | * @date 2018/11/2-16:00 9 | */ 10 | public class NoUseableChannel extends RuntimeException{ 11 | private static final long serialVersionUID = 7762465537123947683L; 12 | 13 | public NoUseableChannel() { 14 | super(); 15 | } 16 | 17 | public NoUseableChannel(String message) { 18 | super(message); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/NettyClientApplication.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 8 | * @date 2018/8/15 - 12:29 9 | */ 10 | @SpringBootApplication 11 | public class NettyClientApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(NettyClientApplication.class, args); 15 | } 16 | } -------------------------------------------------------------------------------- /spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/exception/ErrorParamsException.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.exception; 2 | 3 | /** 4 | * 参数错误异常 5 | *

6 | * 7 | * @author 叶云轩 at tdg_yyx@foxmail.com 8 | * @date 2018/11/1-14:41 9 | */ 10 | public class ErrorParamsException extends RuntimeException { 11 | private static final long serialVersionUID = -623198335011996153L; 12 | 13 | public ErrorParamsException() { 14 | super(); 15 | } 16 | 17 | public ErrorParamsException(String message) { 18 | super(message); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/config/NettyConfig.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * netty客户端配置 9 | *

10 | * 11 | * @author 叶云轩 at tdg_yyx@foxmail.com 12 | * @date 2018/11/1-17:13 13 | */ 14 | @Component 15 | @ConfigurationProperties(prefix = "netty") 16 | @Data 17 | public class NettyConfig { 18 | 19 | private String url; 20 | 21 | private int port; 22 | } 23 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-commons/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | spring-boot-netty 7 | org.yyx.netty 8 | 0.0.1-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | netty-commons 13 | 14 | 15 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketConstant.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | /** 4 | * WebSocket常量 5 | *

6 | * create by 叶云轩 at 2018/5/18-下午1:29 7 | * contact by tdg_yyx@foxmail.com 8 | */ 9 | public interface WebSocketConstant { 10 | 11 | /** 12 | * 点对点 13 | */ 14 | String SEND_TO_USER = "send_to_user"; 15 | 16 | /** 17 | * 群发 广播 18 | */ 19 | String SEND_TO_USERS = "send_to_users"; 20 | 21 | /** 22 | * 请求成功 23 | */ 24 | String REQUEST_SUCCESS = "request_success"; 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketConstant.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | /** 4 | * WebSocket常量 5 | *

6 | * create by 叶云轩 at 2018/5/18-下午1:29 7 | * contact by tdg_yyx@foxmail.com 8 | */ 9 | public interface WebSocketConstant { 10 | 11 | /** 12 | * 点对点 13 | */ 14 | String SEND_TO_USER = "send_to_user"; 15 | 16 | /** 17 | * 群发 广播 18 | */ 19 | String SEND_TO_USERS = "send_to_users"; 20 | 21 | /** 22 | * 请求成功 23 | */ 24 | String REQUEST_SUCCESS = "request_success"; 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/StudyServerApplication.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | public class StudyServerApplication implements CommandLineRunner { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(StudyServerApplication.class, args); 12 | } 13 | 14 | @Override 15 | public void run(String... args) throws Exception { 16 | } 17 | } -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/StudyClientApplication.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | public class StudyClientApplication implements CommandLineRunner { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(StudyClientApplication.class, args); 12 | } 13 | 14 | @Override 15 | public void run(String... args) throws Exception { 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketMessage.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * WebSocketMessage 9 | *

10 | * create by 叶云轩 at 2018/5/18-下午3:02 11 | * contact by tdg_yyx@foxmail.com 12 | */ 13 | @Data 14 | public class WebSocketMessage implements Serializable { 15 | private static final long serialVersionUID = -4666429837358506065L; 16 | 17 | private String accept; 18 | private String content; 19 | private Type header; 20 | 21 | enum Type { 22 | send_user, send_users, request_success; 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketMessage.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * WebSocketMessage 9 | *

10 | * create by 叶云轩 at 2018/5/18-下午3:02 11 | * contact by tdg_yyx@foxmail.com 12 | */ 13 | @Data 14 | public class WebSocketMessage implements Serializable { 15 | private static final long serialVersionUID = -4666429837358506065L; 16 | 17 | private String accept; 18 | private String content; 19 | private Type header; 20 | 21 | enum Type { 22 | send_user, send_users, request_success; 23 | } 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring boot netty 2 | 3 | ### 资源库说明 4 | 5 | 首先,不是一个项目.而是一个DEMO性质的代码分享. 6 | 7 | **当前资源库中包含了两大部分** 8 | 9 | 1. netty-demo下的可以在项目中使用的netty与SpringBoot集成的demo 10 | 2. 在study-netty下的netty相关的基础学习demo 11 | 12 | ## netty-demo 13 | 14 | #### 简述 15 | 这是我之前博客中的集成代码,当然,如今的代码已经不完全是博文中所述那样.这里做了相应的优化与升级.主要包含以下几点: 16 | 17 | 1. 性能优化,不再循环中与服务端创建连接. 18 | 2. 代码优化,客户端代码添加了更多的注释,以便理解这样的做法. 19 | 3. 功能优化,添加了保持长连接的心跳检测功能,并添加了测试类模拟测试. 20 | 21 | 原博文地址: [开源中国](https://my.oschina.net/yzwjyw/blog/1614889) [CSDN](http://blog.csdn.net/yuanzhenwei521/article/details/79194275) [博客园](http://www.cnblogs.com/tdg-yyx/p/8376842.html) 22 | 23 | #### PS 24 | 25 | 具体项目说明详见内部项目的说明文档 26 | 27 | ## study-netty 28 | 29 | #### 简述 30 | 31 | 当前目录下的项目为netty基础学习demo. 32 | 33 | #### PS 34 | 35 | 具体项目说明详见内部项目的说明文档 36 | 37 | ## End 38 | 39 | #### 作者QQ: 562638362 40 | 41 | #### 作者邮箱:marquis_xuan@163.com 42 | 43 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/README.md: -------------------------------------------------------------------------------- 1 | # Netty-client 2 | 3 | > 该项目源自博主项目中实际应用场景。主要用作RPC通信中的客户端处理。 4 | 5 | ## 阅读说明 6 | 7 | ### 阅读顺序: 8 | 9 | netty-client阅读顺序参考以下链条 10 | 11 | ```java 12 | NettyConfiguration -> NettyBeanScanner -> PackageClassUtils(工具) -> RPCProxyFactoryBean -> WrapMethodUtils(工具) -> NettyClient -> CustomChannelInitializerClient -> ClientChannelHandlerAdapter 13 | ``` 14 | 15 | 由NettyConfiguration入手,获取配置文件中的相关配置。 16 | 17 | ### 项目阅读 18 | 19 | 主要见博客: 20 | 21 | [叶云轩的知识库](http://xuan.wp.happyqing.com/2018/09/17/netty%E4%B8%8Espring-boot%E7%9A%84%E6%95%B4%E5%90%88/)、[开源中国](https://my.oschina.net/yzwjyw/blog/1614889)、[CSDN](http://blog.csdn.net/yuanzhenwei521/article/details/79194275)、[博客园](http://www.cnblogs.com/tdg-yyx/p/8376842.html) 22 | 23 | **更直接的参见源码注释。** 24 | 25 | ### 更新说明 26 | 27 | ------ 28 | 29 | #### 2018-11-01 30 | 31 | 1. 添加阅读思路,整理待优化细节 32 | 33 | ------ 34 | 35 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/NullWritable.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * 服务器可能返回空的处理 9 | *

10 | * 11 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 12 | * @date 2018/8/15 - 11:57 13 | */ 14 | @Data 15 | public class NullWritable implements Serializable { 16 | /** 17 | * 序列化标识 18 | */ 19 | private static final long serialVersionUID = 2123827169429254101L; 20 | /** 21 | * 单例 22 | */ 23 | private static NullWritable instance = new NullWritable(); 24 | 25 | /** 26 | * 私有构造器 27 | */ 28 | private NullWritable() { 29 | } 30 | 31 | /** 32 | * 返回代表Null的对象 33 | * 34 | * @return {@link NullWritable} 当方法返回值为void时或返回值为null时返回此对象 35 | */ 36 | public static NullWritable nullWritable() { 37 | return instance; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | study-client 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | study-client 12 | Demo project for Spring Boot 13 | 14 | 15 | org.yyx.netty 16 | study-netty 17 | 0.0.1-SNAPSHOT 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-maven-plugin 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/NettyServerApplication.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty; 2 | 3 | import org.springframework.boot.CommandLineRunner; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.yyx.netty.server.listener.NettyServerListener; 7 | 8 | import javax.annotation.Resource; 9 | 10 | /** 11 | * Netty服务器启动类 12 | * 13 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 14 | * @date 2018/7/9 - 上午9:33 15 | */ 16 | @SpringBootApplication 17 | public class NettyServerApplication implements CommandLineRunner { 18 | 19 | @Resource 20 | private NettyServerListener nettyServerListener; 21 | 22 | public static void main(String[] args) { 23 | SpringApplication.run(NettyServerApplication.class, args); 24 | } 25 | 26 | @Override 27 | public void run(String... args) { 28 | nettyServerListener.start(); 29 | } 30 | } -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/rpc/service/DemoService.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.rpc.service; 2 | 3 | 4 | import org.yyx.netty.entity.User; 5 | import org.yyx.netty.exception.ErrorParamsException; 6 | 7 | /** 8 | * 测试Service 9 | *

10 | * create by 叶云轩 at 2018/3/3-下午1:46 11 | * contact by tdg_yyx@foxmail.com 12 | */ 13 | public interface DemoService { 14 | 15 | /** 16 | * 除法运算 17 | * 18 | * @param numberA 第一个数 19 | * @param numberB 第二个数 20 | * @return 结果 21 | */ 22 | double division(int numberA, int numberB) throws ErrorParamsException; 23 | 24 | /** 25 | * 获取用户信息 26 | * 27 | * @return 用户信息 28 | */ 29 | User getUserInfo(); 30 | 31 | /** 32 | * 打印方法 33 | * 34 | * @return 一个字符串 35 | */ 36 | String print(); 37 | 38 | /** 39 | * 求和方法 40 | * 41 | * @param numberA 第一个数 42 | * @param numberB 第二个数 43 | * @return 两数之和 44 | */ 45 | int sum(int numberA, int numberB); 46 | } 47 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | study-server 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | study-server 11 | Demo project for Spring Boot 12 | 13 | 14 | org.yyx.netty 15 | study-netty 16 | 0.0.1-SNAPSHOT 17 | 18 | 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-maven-plugin 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/config/NettyConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.yyx.netty.rpc.util.NettyBeanScanner; 7 | 8 | /** 9 | * Netty相关的初始化入口 10 | *

11 | * 12 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 13 | * @date 2018/7/9 - 上午9:32 14 | */ 15 | @Configuration 16 | public class NettyConfiguration { 17 | 18 | /** 19 | * 初始化加载Netty相关bean的配置方法 20 | * 21 | * @param basePackage 配置的包名 22 | * @param clientName 配置的Netty实例对象名 23 | * @return NettyBeanScanner 24 | */ 25 | @Bean 26 | public static NettyBeanScanner initNettyBeanScanner(@Value("${netty.basePackage}") String basePackage, 27 | @Value("${netty.clientName}") String clientName) { 28 | // 创建对象 29 | return new NettyBeanScanner(basePackage, clientName); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/service/DemoService.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.rpc.service; 2 | 3 | import org.yyx.netty.entity.User; 4 | import org.yyx.netty.exception.ErrorParamsException; 5 | 6 | /** 7 | * 测试Service 8 | *

9 | * create by 叶云轩 at 2018/3/3-下午1:46 10 | * contact by tdg_yyx@foxmail.com 11 | * 12 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 13 | * @date 2018/8/15 - 12:29 14 | */ 15 | public interface DemoService { 16 | 17 | /** 18 | * 除法运算 19 | * 20 | * @param numberA 第一个数 21 | * @param numberB 第二个数 22 | * @return 结果 23 | */ 24 | double division(int numberA, int numberB) throws ErrorParamsException; 25 | 26 | /** 27 | * 获取用户信息 28 | * 29 | * @return 用户信息 30 | */ 31 | User getUserInfo(); 32 | 33 | /** 34 | * 打印方法 35 | * 36 | * @return 一个字符串 37 | */ 38 | String print(); 39 | 40 | /** 41 | * 求和方法 42 | * 43 | * @param numberA 第一个数 44 | * @param numberB 第二个数 45 | * @return 两数之和 46 | */ 47 | int sum(int numberA, int numberB); 48 | } 49 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/util/ObjectCodec.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.util; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.codec.MessageToMessageCodec; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *

12 | * create by 叶云轩 at 2018/3/3-下午1:42 13 | * contact by tdg_yyx@foxmail.com 14 | */ 15 | public class ObjectCodec extends MessageToMessageCodec { 16 | 17 | @Override 18 | protected void encode(ChannelHandlerContext ctx, Object msg, List out) { 19 | byte[] data = ObjectSerializerUtils.serilizer(msg); 20 | ByteBuf buf = Unpooled.buffer(); 21 | buf.writeBytes(data); 22 | out.add(buf); 23 | } 24 | 25 | @Override 26 | protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) { 27 | byte[] bytes = new byte[msg.readableBytes()]; 28 | msg.readBytes(bytes); 29 | Object deSerilizer = ObjectSerializerUtils.deSerilizer(bytes); 30 | out.add(deSerilizer); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/fixlength/EchoServerHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.fixlength; 2 | 3 | import io.netty.channel.ChannelHandlerAdapter; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | *

10 | * create by 叶云轩 at 2018/4/12-下午4:12 11 | * contact by tdg_yyx@foxmail.com 12 | */ 13 | public class EchoServerHandler extends ChannelHandlerAdapter { 14 | 15 | /** 16 | * MessagePackServerHandler 日志控制器 17 | * Create by 叶云轩 at 2018/4/12 下午4:20 18 | * Concat at tdg_yyx@foxmail.com 19 | */ 20 | private static final Logger LOGGER = LoggerFactory.getLogger(EchoServerHandler.class); 21 | 22 | @Override 23 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 24 | LOGGER.info("--- [发生异常] 释放资源"); 25 | ctx.close(); 26 | } 27 | 28 | @Override 29 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 30 | LOGGER.info("--- [接收到客户端的数据] {}", msg); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/ChildChannelHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo1; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.socket.SocketChannel; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * I/O事件处理类 10 | *

11 | * create by 叶云轩 at 2018/4/11-下午6:34 12 | * contact by tdg_yyx@foxmail.com 13 | */ 14 | public class ChildChannelHandler extends ChannelInitializer { 15 | 16 | /** 17 | * ChildChannelHandler 日志控制器 18 | * Create by 叶云轩 at 2018/4/12 上午11:29 19 | * Concat at tdg_yyx@foxmail.com 20 | */ 21 | private static final Logger LOGGER = LoggerFactory.getLogger(ChildChannelHandler.class); 22 | 23 | /** 24 | * 创建NioSocketChannel成功之后,进行初始化时, 25 | * 将ChannelHandler设置到ChannelPipeline中, 26 | * 同样,用于处理网络I/O事件 27 | * 28 | * @param ch 29 | * 30 | * @throws Exception 31 | */ 32 | @Override 33 | protected void initChannel(SocketChannel ch) throws Exception { 34 | LOGGER.info("--- [通道初始化]"); 35 | ch.pipeline().addLast(new TimeServerHandler()); 36 | } 37 | } -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/WrapMethodUtils.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.rpc.util; 2 | 3 | 4 | import org.yyx.netty.entity.MethodInvokeMeta; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * 封装接口调用的工具 10 | *

11 | * 12 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 13 | * @date 2018/8/15 - 12:27 14 | */ 15 | public class WrapMethodUtils { 16 | /** 17 | * 封装 method 的元数据信息 18 | * 19 | * @param interfaceClass 接口类 20 | * @param method 方法 21 | * @param args 参数列表 22 | * @return 封装的对象 23 | */ 24 | public static MethodInvokeMeta readMethod(Class interfaceClass, Method method, Object[] args) { 25 | MethodInvokeMeta methodInvokeMeta = new MethodInvokeMeta(); 26 | methodInvokeMeta.setInterfaceClass(interfaceClass); 27 | methodInvokeMeta.setArgs(args); 28 | methodInvokeMeta.setMethodName(method.getName()); 29 | methodInvokeMeta.setReturnType(method.getReturnType()); 30 | Class[] parameterTypes = method.getParameterTypes(); 31 | methodInvokeMeta.setParameterTypes(parameterTypes); 32 | return methodInvokeMeta; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/ChildChannelHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo2; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.socket.SocketChannel; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * I/O事件处理类 10 | *

11 | * create by 叶云轩 at 2018/4/11-下午6:34 12 | * contact by tdg_yyx@foxmail.com 13 | * 14 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 15 | * @date 2018/8/15 - 12:38 16 | */ 17 | public class ChildChannelHandler extends ChannelInitializer { 18 | 19 | /** 20 | * ChildChannelHandler 日志控制器 21 | * Create by 叶云轩 at 2018/4/12 上午11:29 22 | * Concat at tdg_yyx@foxmail.com 23 | */ 24 | private static final Logger LOGGER = LoggerFactory.getLogger(ChildChannelHandler.class); 25 | 26 | /** 27 | * 创建NioSocketChannel成功之后,进行初始化时, 28 | * 将ChannelHandler设置到ChannelPipeline中, 29 | * 同样,用于处理网络I/O事件 30 | * 31 | * @param ch 32 | * @throws Exception 33 | */ 34 | @Override 35 | protected void initChannel(SocketChannel ch) throws Exception { 36 | LOGGER.info("--- [通道初始化]"); 37 | ch.pipeline().addLast(new TimeServerHandler()); 38 | } 39 | } -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackDecoder.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.codec.msgpack; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToMessageDecoder; 6 | import org.msgpack.MessagePack; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * MsgPack解码器 14 | *

15 | * create by 叶云轩 at 2018/4/12-下午7:31 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class MsgPackDecoder extends MessageToMessageDecoder { 19 | /** 20 | * MsgPackDecoder 日志控制器 21 | * Create by 叶云轩 at 2018/5/3 下午3:19 22 | * Concat at tdg_yyx@foxmail.com 23 | */ 24 | private static final Logger LOGGER = LoggerFactory.getLogger(MsgPackDecoder.class); 25 | 26 | @Override 27 | protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { 28 | final byte[] array; 29 | final int length = msg.readableBytes(); 30 | array = new byte[length]; 31 | msg.getBytes(msg.readerIndex(), array, 0, length); 32 | MessagePack messagePack = new MessagePack(); 33 | out.add(messagePack.read(array)); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackEncoder.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.codec.msgpack; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToByteEncoder; 6 | import org.msgpack.MessagePack; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.io.IOException; 11 | 12 | /** 13 | * MsgPack编码器 14 | *

15 | * create by 叶云轩 at 2018/4/12-下午7:29 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class MsgPackEncoder extends MessageToByteEncoder { 19 | 20 | /** 21 | * MsgPackEncoder 日志控制器 22 | * Create by 叶云轩 at 2018/5/3 下午3:18 23 | * Concat at tdg_yyx@foxmail.com 24 | */ 25 | private static final Logger LOGGER = LoggerFactory.getLogger(MsgPackEncoder.class); 26 | 27 | @Override 28 | protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { 29 | MessagePack messagePack = new MessagePack(); 30 | // 序列化 31 | byte[] write = new byte[0]; 32 | try { 33 | write = messagePack.write(msg); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | } 37 | out.writeBytes(write); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/config/NettyServerConfig.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.server.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * Netty服务器配置信息 8 | *

9 | * 10 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 11 | * @date 2018/7/9 - 上午9:39 12 | */ 13 | @Component 14 | @ConfigurationProperties(prefix = "netty") 15 | public class NettyServerConfig { 16 | 17 | /** 18 | * 端口 19 | */ 20 | private int port; 21 | /** 22 | * 最大线程数 23 | */ 24 | private int maxThreads; 25 | /** 26 | * 最大数据包长度 27 | */ 28 | private int maxFrameLength; 29 | 30 | public int getPort() { 31 | return port; 32 | } 33 | 34 | public void setPort(int port) { 35 | this.port = port; 36 | } 37 | 38 | public int getMaxThreads() { 39 | return maxThreads; 40 | } 41 | 42 | public void setMaxThreads(int maxThreads) { 43 | this.maxThreads = maxThreads; 44 | } 45 | 46 | public int getMaxFrameLength() { 47 | return maxFrameLength; 48 | } 49 | 50 | public void setMaxFrameLength(int maxFrameLength) { 51 | this.maxFrameLength = maxFrameLength; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackEncoder.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.codec.msgpack; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToByteEncoder; 6 | import org.msgpack.MessagePack; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * MsgPack编码器 12 | *

13 | * create by 叶云轩 at 2018/4/12-下午7:29 14 | * contact by tdg_yyx@foxmail.com 15 | */ 16 | public class MsgPackEncoder extends MessageToByteEncoder { 17 | /** 18 | * MsgPackEncoder 日志控制器 19 | * Create by 叶云轩 at 2018/5/3 下午3:15 20 | * Concat at tdg_yyx@foxmail.com 21 | */ 22 | private static final Logger LOGGER = LoggerFactory.getLogger(MsgPackEncoder.class); 23 | 24 | /** 25 | * 编码 Object -> byte[] 26 | */ 27 | @Override 28 | protected void encode(ChannelHandlerContext channelHandlerContext, Object msg, ByteBuf out) throws Exception { 29 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 30 | "\t├ [编码]: {}\n" + 31 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", msg); 32 | MessagePack messagePack = new MessagePack(); 33 | // 序列化 34 | byte[] write = messagePack.write(msg); 35 | out.writeBytes(write); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.client; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 7 | import io.netty.handler.codec.LengthFieldPrepender; 8 | import io.netty.handler.timeout.IdleStateHandler; 9 | import org.yyx.netty.util.ObjectCodec; 10 | 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | *

15 | * 16 | * @author 叶云轩 at tdg_yyx@foxmail.com 17 | * @date 2018/11/1-17:17 18 | */ 19 | public class NettyClientHandler extends ChannelInitializer { 20 | 21 | @Override 22 | protected void initChannel(SocketChannel ch) throws Exception { 23 | ChannelPipeline pipeline = ch.pipeline(); 24 | // 基于定长的方式解决粘包/拆包问题 25 | pipeline.addLast(new LengthFieldPrepender(2)); 26 | pipeline.addLast(new LengthFieldBasedFrameDecoder(1024 * 1024, 0, 2, 0, 2)); 27 | // 序列化方式 可使用 MsgPack 或 Protobuf 进行序列化扩展 具体可参考study-netty项目下的相关使用例子 28 | pipeline.addLast(new ObjectCodec()); 29 | // 心跳机制 30 | pipeline.addLast(new IdleStateHandler(3, 10, 0, TimeUnit.SECONDS)); 31 | pipeline.addLast(new NettyClientHandlerAdapter()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | study-netty 7 | 0.0.1-SNAPSHOT 8 | pom 9 | 10 | study-netty 11 | study netty 12 | 13 | 14 | study-server 15 | study-client 16 | 17 | 18 | 19 | org.yyx.netty 20 | spring-boot-netty 21 | 0.0.1-SNAPSHOT 22 | 23 | 24 | 25 | 26 | org.yyx.netty 27 | netty-commons 28 | 0.0.1-SNAPSHOT 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketChildHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | import io.netty.handler.codec.http.HttpObjectAggregator; 7 | import io.netty.handler.codec.http.HttpServerCodec; 8 | import io.netty.handler.stream.ChunkedWriteHandler; 9 | 10 | /** 11 | * webSocketChildHandler 12 | *

13 | * create by 叶云轩 at 2018/5/15-下午4:54 14 | * contact by tdg_yyx@foxmail.com 15 | */ 16 | public class WebSocketChildHandler extends ChannelInitializer { 17 | /** 18 | * 初始化Channel 19 | * 20 | * @param socketChannel socketChannel 21 | */ 22 | @Override 23 | protected void initChannel(SocketChannel socketChannel) throws Exception { 24 | ChannelPipeline pipeline = socketChannel.pipeline(); 25 | // 将请求与应答消息编码或者解码为HTTP消息 26 | pipeline.addLast("http-codec", new HttpServerCodec()); 27 | // 将http消息的多个部分组合成一条完整的HTTP消息 28 | pipeline.addLast("aggregator", new HttpObjectAggregator(65536)); 29 | // 向客户端发送HTML5文件。主要用于支持浏览器和服务端进行WebSocket通信 30 | pipeline.addLast("http-chunked", new ChunkedWriteHandler()); 31 | // 服务端Handler 32 | pipeline.addLast("handler", new WebSocketServerHandler()); 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/impl/DemoServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.server.impl; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.yyx.netty.entity.User; 5 | import org.yyx.netty.exception.ErrorParamsException; 6 | import org.yyx.netty.rpc.service.DemoService; 7 | 8 | /** 9 | * 测试Service实现类 10 | *

11 | * 12 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 13 | * @date 2018/7/9 - 上午9:36 14 | */ 15 | @Service 16 | public class DemoServiceImpl implements DemoService { 17 | 18 | @Override 19 | public double division(int numberA, int numberB) throws ErrorParamsException { 20 | if (numberB == 0) { 21 | throw new ErrorParamsException("除数不能为0"); 22 | } 23 | return numberA / numberB; 24 | } 25 | 26 | @Override 27 | public User getUserInfo() { 28 | User leader = new User(); 29 | leader.setId(1); 30 | leader.setName("上级"); 31 | leader.setSource(100); 32 | User user = new User(); 33 | user.setSource(80); 34 | user.setId(0); 35 | user.setName("基层"); 36 | user.setLeader(leader); 37 | return user; 38 | } 39 | 40 | @Override 41 | public String print() { 42 | return "这是来自服务器中DemoService接口的print方法打印的消息"; 43 | } 44 | 45 | @Override 46 | public int sum(int numberA, int numberB) { 47 | return numberA + numberB; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/delimiter/EchoServerHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.delimiter; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerAdapter; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | *

12 | * create by 叶云轩 at 2018/4/12-下午4:12 13 | * contact by tdg_yyx@foxmail.com 14 | */ 15 | public class EchoServerHandler extends ChannelHandlerAdapter { 16 | 17 | /** 18 | * MessagePackServerHandler 日志控制器 19 | * Create by 叶云轩 at 2018/4/12 下午4:20 20 | * Concat at tdg_yyx@foxmail.com 21 | */ 22 | private static final Logger LOGGER = LoggerFactory.getLogger(EchoServerHandler.class); 23 | /** 24 | * 计数器 25 | */ 26 | private int counter; 27 | 28 | @Override 29 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 30 | LOGGER.info("--- [发生异常] 释放资源"); 31 | ctx.close(); 32 | } 33 | 34 | @Override 35 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 36 | String body = (String) msg; 37 | LOGGER.info("--- [第{}次接收客户端消息] {}", ++counter, body); 38 | body += "$_$"; 39 | ByteBuf echo = Unpooled.copiedBuffer(body.getBytes()); 40 | ctx.writeAndFlush(echo); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | netty-demo 7 | 0.0.1-SNAPSHOT 8 | pom 9 | 10 | netty-demo 11 | 可以正常使用的Netty与SpringBoot集成的Demo 12 | 13 | 14 | org.yyx.netty 15 | spring-boot-netty 16 | 0.0.1-SNAPSHOT 17 | 18 | 19 | 20 | netty-server 21 | netty-client 22 | 23 | 24 | 25 | UTF-8 26 | UTF-8 27 | 1.8 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-maven-plugin 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | netty-client 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | netty-client 11 | netty-client with spring boot 12 | 13 | 14 | org.yyx.netty 15 | netty-demo 16 | 0.0.1-SNAPSHOT 17 | 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-configuration-processor 23 | true 24 | 25 | 26 | org.yyx.netty 27 | netty-commons 28 | 0.0.1-SNAPSHOT 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-maven-plugin 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/RemoteMethodInvokeUtil.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.rpc.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.yyx.netty.entity.MethodInvokeMeta; 7 | 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | *

13 | * 14 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 15 | * @date 2018/8/15 - 12:28 16 | */ 17 | public class RemoteMethodInvokeUtil implements ApplicationContextAware { 18 | 19 | private ApplicationContext applicationContext; 20 | 21 | public Object processMethod(MethodInvokeMeta methodInvokeMeta) throws InvocationTargetException, IllegalAccessException { 22 | Class interfaceClass = methodInvokeMeta.getInterfaceClass(); 23 | Object bean = applicationContext.getBean(interfaceClass); 24 | Method[] declaredMethods = interfaceClass.getDeclaredMethods(); 25 | Method method = null; 26 | for (Method declaredMethod : declaredMethods) { 27 | if (methodInvokeMeta.getMethodName().equals(declaredMethod.getName())) { 28 | method = declaredMethod; 29 | } 30 | } 31 | Object invoke = method.invoke(bean, methodInvokeMeta.getArgs()); 32 | return invoke; 33 | } 34 | 35 | @Override 36 | public void setApplicationContext(ApplicationContext app) throws BeansException { 37 | applicationContext = app; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | netty-server 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | netty-server 11 | 12 | 基于SpringBoot的Netty服务端整合代码 13 | 14 | 15 | 16 | org.yyx.netty 17 | netty-demo 18 | 0.0.1-SNAPSHOT 19 | 20 | 21 | 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-configuration-processor 27 | true 28 | 29 | 30 | org.yyx.netty 31 | netty-commons 32 | 0.0.1-SNAPSHOT 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-maven-plugin 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/codec/msgpack/MsgPackDecoder.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.codec.msgpack; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.MessageToMessageDecoder; 6 | import org.msgpack.MessagePack; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * MsgPack解码器 14 | *

15 | * create by 叶云轩 at 2018/4/12-下午7:31 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class MsgPackDecoder extends MessageToMessageDecoder { 19 | 20 | 21 | /** 22 | * MsgPackDecoder 日志控制器 23 | * Create by 叶云轩 at 2018/5/3 下午3:15 24 | * Concat at tdg_yyx@foxmail.com 25 | */ 26 | private static final Logger LOGGER = LoggerFactory.getLogger(MsgPackDecoder.class); 27 | 28 | /** 29 | * 解码 byte[] -> Object 30 | */ 31 | @Override 32 | protected void decode(ChannelHandlerContext channelHandlerContext 33 | , ByteBuf msg, List out) throws Exception { 34 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 35 | "\t├ [解码]: {}\n" + 36 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", msg); 37 | final int length = msg.readableBytes(); 38 | final byte[] array = new byte[length]; 39 | msg.getBytes(msg.readerIndex(), array, 0, length); 40 | MessagePack messagePack = new MessagePack(); 41 | out.add(messagePack.read(array)); 42 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 43 | "\t├ [out]: {}\n" + 44 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", out); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/ChildChannelHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo3; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.socket.SocketChannel; 5 | import io.netty.handler.codec.LineBasedFrameDecoder; 6 | import io.netty.handler.codec.string.StringDecoder; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | * I/O事件处理类 12 | *

13 | * create by 叶云轩 at 2018/4/11-下午6:34 14 | * contact by tdg_yyx@foxmail.com 15 | */ 16 | public class ChildChannelHandler extends ChannelInitializer { 17 | 18 | /** 19 | * ChildChannelHandler 日志控制器 20 | * Create by 叶云轩 at 2018/4/12 上午11:29 21 | * Concat at tdg_yyx@foxmail.com 22 | */ 23 | private static final Logger LOGGER = LoggerFactory.getLogger(ChildChannelHandler.class); 24 | 25 | 26 | /** 27 | * LineBasedFrameDecoder: 以换行符为结束标志的解码器 28 | * 依次遍历ByteBuf中的可读字节, 29 | * 如果有"\n" 或者 "\r\n" -> 就以此位置为结束位置 之后将可读索引到结束位置区间的字节组成一行 30 | */ 31 | 32 | /** 33 | * 创建NioSocketChannel成功之后,进行初始化时, 34 | * 将ChannelHandler设置到ChannelPipeline中, 35 | * 同样,用于处理网络I/O事件 36 | * 37 | * @param ch 38 | * @throws Exception 39 | */ 40 | @Override 41 | protected void initChannel(SocketChannel ch) throws Exception { 42 | LOGGER.info("--- [通道初始化]"); 43 | // region 解决粘包/拆包问题相关代码 44 | ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); 45 | // 将接收到的对象转成字符串 46 | ch.pipeline().addLast(new StringDecoder()); 47 | // endregion 48 | 49 | ch.pipeline().addLast(new TimeServerHandler()); 50 | } 51 | } -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketHandlerClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import io.netty.channel.ChannelInitializer; 4 | import io.netty.channel.ChannelPipeline; 5 | import io.netty.channel.socket.SocketChannel; 6 | import io.netty.handler.codec.http.HttpClientCodec; 7 | import io.netty.handler.codec.http.HttpObjectAggregator; 8 | import io.netty.handler.stream.ChunkedWriteHandler; 9 | import org.yyx.netty.study.codec.msgpack.MsgPackDecoder; 10 | import org.yyx.netty.study.codec.msgpack.MsgPackEncoder; 11 | 12 | /** 13 | * webSocketChildHandler 14 | *

15 | * create by 叶云轩 at 2018/5/15-下午4:54 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class WebSocketHandlerClient extends ChannelInitializer { 19 | 20 | private WebSocketClientHandler webSocketClientHandler; 21 | 22 | public WebSocketHandlerClient(WebSocketClientHandler webSocketClientHandler) { 23 | this.webSocketClientHandler = webSocketClientHandler; 24 | } 25 | 26 | /** 27 | * 初始化Channel 28 | * 29 | * @param socketChannel socketChannel 30 | */ 31 | @Override 32 | protected void initChannel(SocketChannel socketChannel) throws Exception { 33 | ChannelPipeline pipeline = socketChannel.pipeline(); 34 | // 将请求与应答消息编码或者解码为HTTP消息 35 | pipeline.addLast(new HttpClientCodec()); 36 | // 将http消息的多个部分组合成一条完整的HTTP消息 37 | pipeline.addLast("aggregator", new HttpObjectAggregator(65536)); 38 | pipeline.addLast("http-chunked", new ChunkedWriteHandler()); 39 | // 客户端Handler 40 | pipeline.addLast("handler", webSocketClientHandler); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/delimiter/EchoClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.delimiter; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.netty.channel.ChannelHandlerAdapter; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | *

11 | * create by 叶云轩 at 2018/4/12-下午4:57 12 | * contact by tdg_yyx@foxmail.com 13 | */ 14 | public class EchoClientHandler extends ChannelHandlerAdapter { 15 | static final String ECHO_REQ = "Hi,Welcome to Netty World.$_$"; 16 | /** 17 | * MessagePackClientHandler 日志控制器 18 | * Create by 叶云轩 at 2018/4/12 下午4:57 19 | * Concat at tdg_yyx@foxmail.com 20 | */ 21 | private static final Logger LOGGER = LoggerFactory.getLogger(EchoClientHandler.class); 22 | /** 23 | * 计数器 24 | */ 25 | private int counter; 26 | 27 | @Override 28 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 29 | LOGGER.error("--- [异常] {}", cause.getMessage()); 30 | ctx.close(); 31 | } 32 | 33 | @Override 34 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 35 | for (int i = 0; i < 10; i++) { 36 | ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes())); 37 | } 38 | } 39 | 40 | @Override 41 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 42 | LOGGER.info("--- [第{}次接收到服务器的消息] {} | [消息] {}", ++counter, msg); 43 | } 44 | 45 | @Override 46 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 47 | ctx.flush(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/fixlength/EchoClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.fixlength; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.netty.channel.ChannelHandlerAdapter; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | /** 10 | *

11 | * create by 叶云轩 at 2018/4/12-下午4:57 12 | * contact by tdg_yyx@foxmail.com 13 | */ 14 | public class EchoClientHandler extends ChannelHandlerAdapter { 15 | static final String ECHO_REQ = "Hi,Welcome to Netty World.$_$"; 16 | /** 17 | * MessagePackClientHandler 日志控制器 18 | * Create by 叶云轩 at 2018/4/12 下午4:57 19 | * Concat at tdg_yyx@foxmail.com 20 | */ 21 | private static final Logger LOGGER = LoggerFactory.getLogger(EchoClientHandler.class); 22 | /** 23 | * 计数器 24 | */ 25 | private int counter; 26 | 27 | @Override 28 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 29 | LOGGER.error("--- [异常] {}", cause.getMessage()); 30 | ctx.close(); 31 | } 32 | 33 | @Override 34 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 35 | for (int i = 0; i < 10; i++) { 36 | ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes())); 37 | } 38 | } 39 | 40 | @Override 41 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 42 | LOGGER.info("--- [第{}次接收到服务器的消息] {} | [消息] {}", ++counter, msg); 43 | } 44 | 45 | @Override 46 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 47 | ctx.flush(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketServer.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.Channel; 5 | import io.netty.channel.EventLoopGroup; 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | import io.netty.channel.socket.nio.NioServerSocketChannel; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | 12 | /** 13 | * webSocket服务器 14 | *

15 | * create by 叶云轩 at 2018/5/11-上午11:42 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class WebSocketServer { 19 | /** 20 | * WebSocketServer 日志控制器 21 | * Create by 叶云轩 at 2018/5/11 上午11:48 22 | * Concat at tdg_yyx@foxmail.com 23 | */ 24 | private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class); 25 | 26 | public void run(int port) throws Exception { 27 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 28 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 29 | try { 30 | ServerBootstrap bootstrap = new ServerBootstrap(); 31 | bootstrap.group(bossGroup, workerGroup) 32 | .channel(NioServerSocketChannel.class) 33 | .childHandler(new WebSocketChildHandler()); 34 | Channel ch = bootstrap.bind(port).sync().channel(); 35 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 36 | "\t├ [服务器启动]: {}\n" + 37 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", port); 38 | ch.closeFuture().sync(); 39 | } finally { 40 | bossGroup.shutdownGracefully(); 41 | workerGroup.shutdownGracefully(); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/action/MainAction.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.action; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.stereotype.Component; 6 | import org.yyx.netty.entity.User; 7 | import org.yyx.netty.rpc.service.DemoService; 8 | 9 | import javax.annotation.Resource; 10 | 11 | /** 12 | * 主要用来进行模拟测试的类.就不用写接口来进行测试了 13 | *

14 | * 15 | * @author 叶云轩 at tdg_yyx@foxmail.com 16 | * @date 2018/11/1-17:05 17 | */ 18 | @Component 19 | public class MainAction { 20 | 21 | /** 22 | * MainAction 日志输出 23 | */ 24 | private static final Logger LOGGER = LoggerFactory.getLogger(MainAction.class); 25 | 26 | /** 27 | * 测试业务 28 | */ 29 | @Resource 30 | private DemoService demoService; 31 | 32 | /** 33 | * 真正远程调用的方法 34 | * @throws InterruptedException interruptedException 35 | */ 36 | public void call() throws InterruptedException { 37 | // 用于模拟服务器正常启动后,人工调用远程服务代码 38 | Thread.sleep(10 * 1000); 39 | LOGGER.warn("[准备进行业务测试]"); 40 | LOGGER.warn("[rpc测试] "); 41 | int sum = demoService.sum(5, 8); 42 | LOGGER.warn("[rpc测试结果] {}", sum); 43 | LOGGER.warn("[字符串测试] "); 44 | String print = demoService.print(); 45 | LOGGER.warn("[字符串测试结果] {}", print); 46 | LOGGER.warn("[对象测试] "); 47 | User userInfo = demoService.getUserInfo(); 48 | LOGGER.warn("[对象测试结果] {}", userInfo); 49 | LOGGER.warn("[异常测试]"); 50 | try { 51 | double division = demoService.division(3, 0); 52 | LOGGER.warn("[异常测试结果] {}", division); 53 | } catch (Exception e) { 54 | LOGGER.error("[服务器异常] {}", e.getMessage()); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackServerHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.megpack; 2 | 3 | import io.netty.channel.ChannelHandlerAdapter; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.yyx.netty.entity.User; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | *

13 | * create by 叶云轩 at 2018/4/12-下午4:12 14 | * contact by tdg_yyx@foxmail.com 15 | * 16 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 17 | * @date 2018/8/15 - 12:32 18 | */ 19 | public class MessagePackServerHandler extends ChannelHandlerAdapter { 20 | 21 | /** 22 | * MessagePackServerHandler 日志控制器 23 | * Create by 叶云轩 at 2018/4/12 下午4:20 24 | * Concat at tdg_yyx@foxmail.com 25 | */ 26 | private static final Logger LOGGER = LoggerFactory.getLogger(MessagePackServerHandler.class); 27 | 28 | @Override 29 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 30 | LOGGER.info("--- [发生异常] 释放资源: {}", cause.getMessage()); 31 | // todo 32 | ctx.close(); 33 | } 34 | 35 | @Override 36 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 37 | ctx.writeAndFlush("Server connect success"); 38 | } 39 | 40 | @Override 41 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 42 | List userInfo = (List) msg; 43 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 44 | "\t├ [接收 ]: {}\n" + 45 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", userInfo); 46 | } 47 | 48 | @Override 49 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 50 | ctx.flush(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/User.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.entity; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import org.msgpack.annotation.Message; 6 | 7 | import java.io.Serializable; 8 | import java.nio.ByteBuffer; 9 | 10 | /** 11 | * 用户实体 12 | *

13 | * create by 叶云轩 at 2018/3/3-下午1:48 14 | * contact by tdg_yyx@foxmail.com 15 | * 16 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 17 | * @date 2018/8/15 - 11:55 18 | */ 19 | @Data 20 | @NoArgsConstructor 21 | @Message 22 | public class User implements Serializable { 23 | /** 24 | * 序列化标识 25 | */ 26 | private static final long serialVersionUID = -5462474276911290451L; 27 | /** 28 | * 编号 29 | */ 30 | private int id; 31 | /** 32 | * 姓名 33 | */ 34 | private String name; 35 | /** 36 | * 分数 37 | */ 38 | private double source; 39 | /** 40 | * 领导 41 | */ 42 | private User leader; 43 | 44 | public byte[] codeC() { 45 | ByteBuffer allocate = ByteBuffer.allocate(1024); 46 | byte[] bytes = this.name.getBytes(); 47 | allocate.putInt(bytes.length); 48 | allocate.put(bytes); 49 | allocate.putInt(this.id); 50 | allocate.flip(); 51 | bytes = null; 52 | byte[] res = new byte[allocate.remaining()]; 53 | allocate.get(res); 54 | return res; 55 | } 56 | 57 | public byte[] codeC(ByteBuffer buffer) { 58 | buffer.clear(); 59 | byte[] bytes = this.name.getBytes(); 60 | buffer.putInt(bytes.length); 61 | buffer.put(bytes); 62 | buffer.putInt(this.id); 63 | buffer.flip(); 64 | bytes = null; 65 | byte[] res = new byte[buffer.remaining()]; 66 | buffer.get(res); 67 | return res; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientListener.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.client; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.boot.CommandLineRunner; 6 | import org.springframework.core.annotation.Order; 7 | import org.springframework.stereotype.Component; 8 | import org.yyx.netty.action.MainAction; 9 | import org.yyx.netty.config.NettyConfig; 10 | 11 | import javax.annotation.Resource; 12 | 13 | /** 14 | * netty客户端监听器 15 | *

16 | * 主要用于延迟测试RPC和启动NettyClient 17 | * 18 | * @author 叶云轩 at tdg_yyx@foxmail.com 19 | * @date 2018/11/1-17:03 20 | */ 21 | @Order(0) 22 | @Component 23 | public class NettyClientListener implements CommandLineRunner { 24 | /** 25 | * NettyClientListener 日志输出 26 | */ 27 | private static final Logger LOGGER = LoggerFactory.getLogger(NettyClientListener.class); 28 | 29 | /** 30 | * netty客户端配置 31 | */ 32 | @Resource 33 | private NettyConfig nettyConfig; 34 | /** 35 | * 主要用于测试RPC场景的类。集成到自己的业务中就不需要此依赖 36 | */ 37 | @Resource 38 | private MainAction mainAction; 39 | 40 | @Override 41 | public void run(String... args) throws Exception { 42 | LOGGER.info("{} -> [准备进行与服务端通信]", this.getClass().getName()); 43 | // region 模拟RPC场景 44 | Thread t1 = new Thread(() -> { 45 | try { 46 | mainAction.call(); 47 | } catch (InterruptedException e) { 48 | e.printStackTrace(); 49 | } 50 | }); 51 | // 使用一个线程模拟Client启动完毕后RPC的场景 52 | t1.start(); 53 | // endregion 54 | // 获取服务器监听的端口 55 | int port = nettyConfig.getPort(); 56 | // 获取服务器IP地址 57 | String url = nettyConfig.getUrl(); 58 | // 启动NettyClient 59 | new NettyClient(port, url).start(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/TimeServerHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo3; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerAdapter; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * 针对网络事件进行读写操作的类 14 | *

15 | * create by 叶云轩 at 2018/4/11-下午6:35 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class TimeServerHandler extends ChannelHandlerAdapter { 19 | /** 20 | * TimeServerHandler 日志控制器 21 | * Create by 叶云轩 at 2018/4/12 上午11:30 22 | * Concat at tdg_yyx@foxmail.com 23 | */ 24 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeServerHandler.class); 25 | /** 26 | * 模拟粘包/拆包问题计数器 27 | */ 28 | private int counter; 29 | 30 | @Override 31 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 32 | ctx.close(); 33 | } 34 | 35 | /** 36 | * 读事件 37 | * 38 | * @param ctx ChannelHandlerContext 39 | * @param msg 消息 40 | * 41 | * @throws Exception 42 | */ 43 | @Override 44 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 45 | // region 解决模拟粘包/拆包问题相关代码 46 | String body = (String) msg; 47 | LOGGER.info("--- [接收到的数据] {} | [counter] {}", body, ++counter); 48 | String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER"; 49 | currentTime = currentTime + System.getProperty("line.separator"); 50 | ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); 51 | // endregion 52 | 53 | // 异步发送应答消息给Client 54 | ctx.writeAndFlush(resp); // --> 将消息放到发送缓冲数组中 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.Channel; 5 | import io.netty.channel.EventLoopGroup; 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | import io.netty.channel.socket.nio.NioSocketChannel; 8 | import io.netty.handler.codec.http.DefaultHttpHeaders; 9 | import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; 10 | import io.netty.handler.codec.http.websocketx.WebSocketVersion; 11 | 12 | import java.net.URI; 13 | 14 | /** 15 | * webSocketClient 16 | *

17 | * create by 叶云轩 at 2018/5/17-下午6:04 18 | * contact by tdg_yyx@foxmail.com 19 | */ 20 | public class WebSocketClient { 21 | 22 | public void connect(int port, String host, String userName) throws Exception { 23 | // 配置客户端的NIO线程组 24 | EventLoopGroup clientGroup = new NioEventLoopGroup(); 25 | try { 26 | // Client辅助启动类 27 | Bootstrap bootstrap = new Bootstrap(); 28 | // 配置bootstrap 29 | WebSocketClientHandler webSocketClientHandler = new WebSocketClientHandler( 30 | WebSocketClientHandshakerFactory.newHandshaker(new URI("ws://172.0.0.1:9999/websocket/" + userName) 31 | , WebSocketVersion.V13, null, false, new DefaultHttpHeaders()) 32 | ); 33 | bootstrap.group(clientGroup) 34 | .channel(NioSocketChannel.class) 35 | .handler(new WebSocketHandlerClient(webSocketClientHandler)); 36 | // 发起异步连接操作 同步方法待成功 37 | Channel channel = bootstrap.connect(host, port).sync().channel(); 38 | // 等待客户端链路关闭 39 | channel.closeFuture().sync(); 40 | } finally { 41 | // 优雅退出,释放NIO线程组 42 | clientGroup.shutdownGracefully(); 43 | } 44 | 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/entity/MethodInvokeMeta.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.entity; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.NoArgsConstructor; 6 | import lombok.ToString; 7 | 8 | import java.io.Serializable; 9 | 10 | /** 11 | * 记录调用方法的元信息 12 | * 13 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 14 | * @date 2018/8/14 - 22:46 15 | */ 16 | @Data 17 | @NoArgsConstructor 18 | @EqualsAndHashCode 19 | @ToString 20 | public class MethodInvokeMeta implements Serializable { 21 | private static final long serialVersionUID = 5429914235135594820L; 22 | /** 23 | * 接口 24 | */ 25 | private Class interfaceClass; 26 | /** 27 | * 方法名 28 | */ 29 | private String methodName; 30 | /** 31 | * 参数 32 | */ 33 | private Object[] args; 34 | /** 35 | * 返回值类型 36 | */ 37 | private Class returnType; 38 | /** 39 | * 参数类型 40 | */ 41 | private Class[] parameterTypes; 42 | 43 | public Object[] getArgs() { 44 | return args; 45 | } 46 | 47 | public void setArgs(Object[] args) { 48 | this.args = args; 49 | } 50 | 51 | public Class getInterfaceClass() { 52 | return interfaceClass; 53 | } 54 | 55 | public void setInterfaceClass(Class interfaceClass) { 56 | this.interfaceClass = interfaceClass; 57 | } 58 | 59 | public String getMethodName() { 60 | return methodName; 61 | } 62 | 63 | public void setMethodName(String methodName) { 64 | this.methodName = methodName; 65 | } 66 | 67 | public Class[] getParameterTypes() { 68 | return parameterTypes; 69 | } 70 | 71 | public void setParameterTypes(Class[] parameterTypes) { 72 | this.parameterTypes = parameterTypes; 73 | } 74 | 75 | public Class getReturnType() { 76 | return returnType; 77 | } 78 | 79 | public void setReturnType(Class returnType) { 80 | this.returnType = returnType; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/fixlength/EchoClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.fixlength; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelInitializer; 8 | import io.netty.channel.ChannelOption; 9 | import io.netty.channel.EventLoopGroup; 10 | import io.netty.channel.nio.NioEventLoopGroup; 11 | import io.netty.channel.socket.SocketChannel; 12 | import io.netty.channel.socket.nio.NioSocketChannel; 13 | import io.netty.handler.codec.FixedLengthFrameDecoder; 14 | import io.netty.handler.codec.string.StringDecoder; 15 | 16 | /** 17 | * MessagePackClient 18 | *

19 | * create by 叶云轩 at 2018/4/12-下午4:28 20 | * contact by tdg_yyx@foxmail.com 21 | */ 22 | public class EchoClient { 23 | 24 | public void connect(int port, String host) throws Exception { 25 | EventLoopGroup group = new NioEventLoopGroup(); 26 | try { 27 | Bootstrap bootstrap = new Bootstrap(); 28 | bootstrap.group(group).channel(NioSocketChannel.class) 29 | .option(ChannelOption.TCP_NODELAY, true) 30 | .handler(new ChannelInitializer() { 31 | @Override 32 | protected void initChannel(SocketChannel ch) throws Exception { 33 | ByteBuf delimiter = Unpooled.copiedBuffer("$_$".getBytes()); 34 | ch.pipeline().addLast(new FixedLengthFrameDecoder(20)); 35 | ch.pipeline().addLast(new StringDecoder()); 36 | ch.pipeline().addLast(new EchoClientHandler()); 37 | } 38 | }); 39 | ChannelFuture future = bootstrap.connect(host, port).sync(); 40 | future.channel().closeFuture().sync(); 41 | } finally { 42 | // 优雅退出,释放线程池资源 43 | group.shutdownGracefully(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-commons/src/main/java/org/yyx/netty/util/ObjectSerializerUtils.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.*; 7 | 8 | /** 9 | * 对象序列化工具 10 | *

11 | * create by 叶云轩 at 2018/3/3-下午1:43 12 | * contact by tdg_yyx@foxmail.com 13 | */ 14 | public class ObjectSerializerUtils { 15 | /** 16 | * ObjectSerializerUtils 日志控制器 17 | * Create by 叶云轩 at 2018/3/3 下午1:43 18 | * Concat at tdg_yyx@foxmail.com 19 | */ 20 | private static final Logger LOGGER = LoggerFactory.getLogger(ObjectSerializerUtils.class); 21 | 22 | /** 23 | * 反序列化 24 | * 25 | * @param data 26 | * @return 27 | */ 28 | public static Object deSerilizer(byte[] data) { 29 | if (data != null && data.length > 0) { 30 | try { 31 | ByteArrayInputStream bis = new ByteArrayInputStream(data); 32 | ObjectInputStream ois = new ObjectInputStream(bis); 33 | return ois.readObject(); 34 | } catch (Exception e) { 35 | LOGGER.info("[异常信息] {}", e.getMessage()); 36 | e.printStackTrace(); 37 | } 38 | return null; 39 | } else { 40 | LOGGER.info("[反序列化] 入参为空"); 41 | return null; 42 | } 43 | } 44 | 45 | /** 46 | * 序列化对象 47 | * 48 | * @param obj 49 | * @return 50 | */ 51 | public static byte[] serilizer(Object obj) { 52 | if (obj != null) { 53 | try { 54 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 55 | ObjectOutputStream oos = new ObjectOutputStream(bos); 56 | oos.writeObject(obj); 57 | oos.flush(); 58 | oos.close(); 59 | return bos.toByteArray(); 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | } 63 | return null; 64 | } else { 65 | return null; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.megpack; 2 | 3 | import io.netty.channel.ChannelHandlerAdapter; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.yyx.netty.entity.User; 8 | 9 | /** 10 | *

11 | * create by 叶云轩 at 2018/4/12-下午4:57 12 | * contact by tdg_yyx@foxmail.com 13 | */ 14 | public class MessagePackClientHandler extends ChannelHandlerAdapter { 15 | 16 | /** 17 | * MessagePackClientHandler 日志控制器 18 | * Create by 叶云轩 at 2018/4/12 下午4:57 19 | * Concat at tdg_yyx@foxmail.com 20 | */ 21 | private static final Logger LOGGER = LoggerFactory.getLogger(MessagePackClientHandler.class); 22 | 23 | private final int sendNumber; 24 | 25 | public MessagePackClientHandler(int sendNumber) { 26 | this.sendNumber = sendNumber; 27 | } 28 | 29 | @Override 30 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 31 | LOGGER.error("--- [异常] {}", cause.getMessage()); 32 | ctx.close(); 33 | } 34 | 35 | @Override 36 | public void channelActive(ChannelHandlerContext ctx) { 37 | User[] userInfo = UserInfo(); 38 | ctx.writeAndFlush(userInfo); 39 | for (User info : userInfo) { 40 | ctx.write(info); 41 | } 42 | ctx.flush(); 43 | } 44 | 45 | @Override 46 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 47 | LOGGER.info("--- [收到服务器的消息] {}", msg); 48 | } 49 | 50 | @Override 51 | public void channelReadComplete(ChannelHandlerContext ctx) { 52 | ctx.flush(); 53 | } 54 | 55 | private User[] UserInfo() { 56 | User[] users = new User[sendNumber]; 57 | User user; 58 | for (int i = 0; i < sendNumber; i++) { 59 | user = new User(); 60 | user.setId(i); 61 | user.setName("YYX --->" + i); 62 | users[i] = user; 63 | } 64 | return users; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/delimiter/EchoClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.delimiter; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelInitializer; 8 | import io.netty.channel.ChannelOption; 9 | import io.netty.channel.EventLoopGroup; 10 | import io.netty.channel.nio.NioEventLoopGroup; 11 | import io.netty.channel.socket.SocketChannel; 12 | import io.netty.channel.socket.nio.NioServerSocketChannel; 13 | import io.netty.channel.socket.nio.NioSocketChannel; 14 | import io.netty.handler.codec.DelimiterBasedFrameDecoder; 15 | import io.netty.handler.codec.string.StringDecoder; 16 | 17 | /** 18 | * MessagePackClient 19 | *

20 | * create by 叶云轩 at 2018/4/12-下午4:28 21 | * contact by tdg_yyx@foxmail.com 22 | */ 23 | public class EchoClient { 24 | 25 | public void connect(int port, String host) throws Exception { 26 | EventLoopGroup group = new NioEventLoopGroup(); 27 | try { 28 | Bootstrap bootstrap = new Bootstrap(); 29 | bootstrap.group(group).channel(NioSocketChannel.class) 30 | .option(ChannelOption.TCP_NODELAY, true) 31 | .handler(new ChannelInitializer() { 32 | @Override 33 | protected void initChannel(SocketChannel ch) throws Exception { 34 | ByteBuf delimiter = Unpooled.copiedBuffer("$_$".getBytes()); 35 | ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter)); 36 | ch.pipeline().addLast(new StringDecoder()); 37 | ch.pipeline().addLast(new EchoClientHandler()); 38 | } 39 | }); 40 | ChannelFuture future = bootstrap.connect(host,port).sync(); 41 | future.channel().closeFuture().sync(); 42 | } finally { 43 | // 优雅退出,释放线程池资源 44 | group.shutdownGracefully(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo1/TimeClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo1; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.channel.socket.nio.NioSocketChannel; 11 | 12 | /** 13 | * Netty时间服务器客户端 14 | *

15 | * create by 叶云轩 at 2018/4/12-上午9:53 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class TimeClient { 19 | 20 | public void connect(int port, String host) throws Exception { 21 | // 配置客户端的NIO线程组 22 | EventLoopGroup clientGroup = new NioEventLoopGroup(); 23 | try { 24 | // Client辅助启动类 25 | Bootstrap bootstrap = new Bootstrap(); 26 | // 配置bootstrap 27 | bootstrap.group(clientGroup) 28 | .channel(NioSocketChannel.class) 29 | .option(ChannelOption.TCP_NODELAY, true) 30 | .handler(new ChannelInitializer() { 31 | /** 32 | * 创建NioSocketChannel成功之后,进行初始化时, 33 | * 将ChannelHandler设置到ChannelPipeline中, 34 | * 同样,用于处理网络I/O事件 35 | * @param ch 36 | * @throws Exception 37 | */ 38 | @Override 39 | protected void initChannel(SocketChannel ch) throws Exception { 40 | ch.pipeline().addLast(new TimeClientHandler()); 41 | } 42 | }); 43 | // 发起异步连接操作 同步方法待成功 44 | ChannelFuture future = bootstrap.connect(host, port).sync(); 45 | // 等待客户端链路关闭 46 | future.channel().closeFuture().sync(); 47 | } finally { 48 | // 优雅退出,释放NIO线程组 49 | clientGroup.shutdownGracefully(); 50 | } 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo2/TimeClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo2; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.channel.socket.nio.NioSocketChannel; 11 | 12 | /** 13 | * Netty时间服务器客户端 14 | *

15 | * create by 叶云轩 at 2018/4/12-上午9:53 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class TimeClient { 19 | 20 | public void connect(int port, String host) throws Exception { 21 | // 配置客户端的NIO线程组 22 | EventLoopGroup clientGroup = new NioEventLoopGroup(); 23 | try { 24 | // Client辅助启动类 25 | Bootstrap bootstrap = new Bootstrap(); 26 | // 配置bootstrap 27 | bootstrap.group(clientGroup) 28 | .channel(NioSocketChannel.class) 29 | .option(ChannelOption.TCP_NODELAY, true) 30 | .handler(new ChannelInitializer() { 31 | /** 32 | * 创建NioSocketChannel成功之后,进行初始化时, 33 | * 将ChannelHandler设置到ChannelPipeline中, 34 | * 同样,用于处理网络I/O事件 35 | * @param ch 36 | * @throws Exception 37 | */ 38 | @Override 39 | protected void initChannel(SocketChannel ch) throws Exception { 40 | ch.pipeline().addLast(new TimeClientHandler()); 41 | } 42 | }); 43 | // 发起异步连接操作 同步方法待成功 44 | ChannelFuture future = bootstrap.connect(host, port).sync(); 45 | // 等待客户端链路关闭 46 | future.channel().closeFuture().sync(); 47 | } finally { 48 | // 优雅退出,释放NIO线程组 49 | clientGroup.shutdownGracefully(); 50 | } 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/test/java/org/yyx/netty/study/StudyServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | import org.yyx.netty.study.echo.megpack.MessagePackServer; 8 | import org.yyx.netty.study.websocket.WebSocketServer; 9 | 10 | @RunWith(SpringRunner.class) 11 | @SpringBootTest 12 | public class StudyServerApplicationTests { 13 | 14 | private final int port = 8080; 15 | 16 | // region 启动不考虑粘包/拆包问题的netty服务 17 | @Test 18 | public void startNettyServer1() throws Exception { 19 | new org.yyx.netty.study.time.demo1.TimeServer().bind(port); 20 | } 21 | // endregion 22 | 23 | // region 启动模拟粘包/拆包问题的netty服务 24 | @Test 25 | public void startNettyServer2() throws Exception { 26 | new org.yyx.netty.study.time.demo2.TimeServer().bind(port); 27 | } 28 | // endregion 29 | 30 | // region 启动已解决粘包/拆包问题的netty服务 - LineBasedFrameDecoder 实现 31 | @Test 32 | public void startNettyServer3() throws Exception { 33 | new org.yyx.netty.study.time.demo3.TimeServer().bind(port); 34 | } 35 | // endregion 36 | 37 | // region 启动已解决粘包/拆包问题的netty服务 - DelimiterBasedFrameDecoder 实现 38 | @Test 39 | public void startNettyServer4() throws Exception { 40 | new org.yyx.netty.study.echo.delimiter.EchoServer().bind(port); 41 | } 42 | // endregion 43 | 44 | // region 启动已解决粘包/拆包问题的netty服务 - FixedLengthFrameDecoder 实现 45 | @Test 46 | public void startNettyServer5() throws Exception { 47 | new org.yyx.netty.study.echo.fixlength.EchoServer().bind(port); 48 | } 49 | // endregion 50 | 51 | // region 启动不考虑粘包/拆包问题 基于MessagePack编解码的Netty服务 52 | @Test 53 | public void testMessagePackEchoServer() throws Exception { 54 | new MessagePackServer().bind(port); 55 | } 56 | // endregion 57 | 58 | // region 启动WebSocket服务器 59 | @Test 60 | public void startWebSocketServer() throws Exception { 61 | new WebSocketServer().run(9999); 62 | } 63 | // endregion 64 | } -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.megpack; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.EventLoopGroup; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.SocketChannel; 9 | import io.netty.channel.socket.nio.NioSocketChannel; 10 | import io.netty.handler.logging.LogLevel; 11 | import io.netty.handler.logging.LoggingHandler; 12 | import org.yyx.netty.study.codec.msgpack.MsgPackDecoder; 13 | import org.yyx.netty.study.codec.msgpack.MsgPackEncoder; 14 | 15 | /** 16 | * MessagePackClient 17 | *

18 | * create by 叶云轩 at 2018/4/12-下午4:28 19 | * contact by tdg_yyx@foxmail.com 20 | */ 21 | public class MessagePackClient { 22 | 23 | /** 24 | * 客户端连接方法 25 | * 26 | * @param port 端口号 27 | * @param host 主机名 28 | * 29 | * @throws Exception 异常 30 | */ 31 | public void connect(int port, String host, int sendNumber) throws Exception { 32 | EventLoopGroup group = new NioEventLoopGroup(); 33 | try { 34 | Bootstrap bootstrap = new Bootstrap(); 35 | bootstrap.group(group).channel(NioSocketChannel.class) 36 | .handler(new ChannelInitializer() { 37 | @Override 38 | protected void initChannel(SocketChannel ch) throws Exception { 39 | ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO)); 40 | // ch.pipeline().addLast(new LengthFieldPrepender(2)); 41 | ch.pipeline().addLast(new MsgPackEncoder()); 42 | // ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2)); 43 | ch.pipeline().addLast(new MsgPackDecoder()); 44 | ch.pipeline().addLast(new MessagePackClientHandler(sendNumber)); 45 | } 46 | }); 47 | ChannelFuture future = bootstrap.connect(host, port).sync(); 48 | future.channel().closeFuture().sync(); 49 | } finally { 50 | // 优雅退出,释放线程池资源 51 | group.shutdownGracefully(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.client; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.EventLoopGroup; 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | import io.netty.channel.socket.nio.NioSocketChannel; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * netty客户端第二个版本 13 | *

14 | * 15 | * @author 叶云轩 at tdg_yyx@foxmail.com 16 | * @date 2018/11/1-17:08 17 | */ 18 | public class NettyClient { 19 | /** 20 | * NettyClient 志控制器 21 | * Create by 叶云轩 at 2018/3/3 下午2:08 22 | * Concat at tdg_yyx@foxmail.com 23 | */ 24 | private static final Logger LOGGER = LoggerFactory.getLogger(NettyClient.class); 25 | private static int retry = 0; 26 | /** 27 | * 初始化Bootstrap实例, 此实例是netty客户端应用开发的入口 28 | */ 29 | private Bootstrap bootstrap; 30 | /** 31 | * 工人线程组 32 | */ 33 | private EventLoopGroup worker; 34 | /** 35 | * 远程端口 36 | */ 37 | private int port; 38 | /** 39 | * 远程服务器url 40 | */ 41 | private String url; 42 | /** 43 | * 默认重连机制为10次 44 | */ 45 | private int MAX_RETRY_TIMES = 10; 46 | 47 | public NettyClient(int port, String url) { 48 | this.port = port; 49 | this.url = url; 50 | bootstrap = new Bootstrap(); 51 | worker = new NioEventLoopGroup(); 52 | bootstrap.group(worker); 53 | bootstrap.channel(NioSocketChannel.class); 54 | } 55 | 56 | public void start() { 57 | LOGGER.info("{} -> [启动连接] {}:{}", this.getClass().getName(), url, port); 58 | bootstrap.handler(new NettyClientHandler()); 59 | ChannelFuture f = bootstrap.connect(url, port); 60 | try { 61 | f.channel().closeFuture().sync(); 62 | } catch (InterruptedException e) { 63 | retry++; 64 | if (retry > MAX_RETRY_TIMES) { 65 | throw new RuntimeException("调用Wrong"); 66 | } else { 67 | try { 68 | Thread.sleep(100); 69 | } catch (InterruptedException e1) { 70 | e1.printStackTrace(); 71 | } 72 | LOGGER.info("第{}次尝试....失败", retry); 73 | start(); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo1/TimeClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo1; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerAdapter; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | *

12 | * create by 叶云轩 at 2018/4/12-上午10:16 13 | * contact by tdg_yyx@foxmail.com 14 | */ 15 | public class TimeClientHandler extends ChannelHandlerAdapter { 16 | 17 | /** 18 | * TimeClientHandler 日志控制器 19 | * Create by 叶云轩 at 2018/4/12 上午10:16 20 | * Concat at tdg_yyx@foxmail.com 21 | */ 22 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeClientHandler.class); 23 | /** 24 | * 25 | */ 26 | private final ByteBuf firstMessage; 27 | /** 28 | * 29 | */ 30 | private byte[] req; 31 | 32 | public TimeClientHandler() { 33 | byte[] req = "QUERY TIME ORDER".getBytes(); 34 | firstMessage = Unpooled.buffer(req.length); 35 | firstMessage.writeBytes(req); 36 | } 37 | 38 | /** 39 | * 捕捉异常 40 | * 41 | * @param ctx 42 | * @param cause 43 | * 44 | * @throws Exception 45 | */ 46 | @Override 47 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 48 | LOGGER.warn("--- [异常,释放资源] {}", cause.getMessage()); 49 | ctx.close(); 50 | } 51 | 52 | /** 53 | * 当客户端和服务端TCP链路建立成功之后调用此方法 54 | * 发送指令给服务端,调用ChannelHandlerContext.writeAndFlush方法将请求消息发送给服务端 55 | * 56 | * @param ctx 57 | * 58 | * @throws Exception 59 | */ 60 | @Override 61 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 62 | ctx.writeAndFlush(firstMessage); 63 | } 64 | 65 | /** 66 | * 服务端返回应答消息时,调用此方法 67 | * 68 | * @param ctx 69 | * @param msg 70 | * 71 | * @throws Exception 72 | */ 73 | @Override 74 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 75 | ByteBuf byteBuf = (ByteBuf) msg; 76 | byte[] request = new byte[byteBuf.readableBytes()]; 77 | byteBuf.readBytes(request); 78 | String body = new String(request, "utf-8"); 79 | LOGGER.info("--- [Now is] {}", body); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo3/TimeClient.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo3; 2 | 3 | import io.netty.bootstrap.Bootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.channel.socket.nio.NioSocketChannel; 11 | import io.netty.handler.codec.LineBasedFrameDecoder; 12 | import io.netty.handler.codec.string.StringDecoder; 13 | 14 | /** 15 | * Netty时间服务器客户端 16 | *

17 | * create by 叶云轩 at 2018/4/12-上午9:53 18 | * contact by tdg_yyx@foxmail.com 19 | */ 20 | public class TimeClient { 21 | 22 | public void connect(int port, String host) throws Exception { 23 | // 配置客户端的NIO线程组 24 | EventLoopGroup clientGroup = new NioEventLoopGroup(); 25 | try { 26 | // Client辅助启动类 27 | Bootstrap bootstrap = new Bootstrap(); 28 | // 配置bootstrap 29 | bootstrap.group(clientGroup) 30 | .channel(NioSocketChannel.class) 31 | .option(ChannelOption.TCP_NODELAY, true) 32 | .handler(new ChannelInitializer() { 33 | /** 34 | * 创建NioSocketChannel成功之后,进行初始化时, 35 | * 将ChannelHandler设置到ChannelPipeline中, 36 | * 同样,用于处理网络I/O事件 37 | * @param ch 38 | * @throws Exception 39 | */ 40 | @Override 41 | protected void initChannel(SocketChannel ch) throws Exception { 42 | // region 解决粘包/拆包问题相关代码 43 | ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); 44 | ch.pipeline().addLast(new StringDecoder()); 45 | // endregion 46 | ch.pipeline().addLast(new TimeClientHandler()); 47 | } 48 | }); 49 | // 发起异步连接操作 同步方法待成功 50 | ChannelFuture future = bootstrap.connect(host, port).sync(); 51 | // 等待客户端链路关闭 52 | future.channel().closeFuture().sync(); 53 | } finally { 54 | // 优雅退出,释放NIO线程组 55 | clientGroup.shutdownGracefully(); 56 | } 57 | 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/TimeServer.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo1; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelOption; 6 | import io.netty.channel.EventLoopGroup; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.nio.NioServerSocketChannel; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | *

14 | * create by 叶云轩 at 2018/4/11-下午6:33 15 | * contact by tdg_yyx@foxmail.com 16 | */ 17 | public class TimeServer { 18 | /* 19 | NioEventLoopGroup本质是一个线程组,包含一组NIO线程专门用于网络事件的处理 20 | 一个用于服务端接受客户端的连接 21 | 一个用于进行SocketChannel的网络读写 22 | */ 23 | 24 | /** 25 | * TimeServer 日志控制器 26 | * Create by 叶云轩 at 2018/4/12 上午11:26 27 | * Concat at tdg_yyx@foxmail.com 28 | */ 29 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeServer.class); 30 | 31 | /** 32 | * 绑定端口 33 | * 34 | * @param port 端口号 35 | * @throws Exception 异常 36 | */ 37 | public void bind(int port) throws Exception { 38 | LOGGER.info("--- [绑定端口] {}", port); 39 | // 声明Boss线程组 40 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 41 | // 声明Worker线程组 42 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 43 | try { 44 | LOGGER.info("--- [启动NIO] "); 45 | // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度 46 | ServerBootstrap bootstrap = new ServerBootstrap(); 47 | // 将两个NIO线程组传递到ServerBootStrap中 48 | bootstrap.group(bossGroup, workerGroup) 49 | // NioServerSocketChannel 相当于NIO中的ServerSocketChannel类 50 | .channel(NioServerSocketChannel.class) 51 | // 配置NioServerSocketChannel的TCP参数 backlog设置为1024 52 | .option(ChannelOption.SO_BACKLOG, 1024) 53 | // 绑定I/O事件处理类 54 | .childHandler(new ChildChannelHandler()); 55 | // 绑定端口,同步等待成功 56 | // channelFuture 相当于JDK的java.util.concurrent.Future用于异步操作通知回调 57 | ChannelFuture channelFuture = bootstrap.bind(port).sync(); 58 | // 等待服务端监听端口关闭 59 | channelFuture.channel().closeFuture().sync(); 60 | } finally { 61 | // 优雅退出,释放线程池资源 62 | bossGroup.shutdownGracefully(); 63 | workerGroup.shutdownGracefully(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/TimeServer.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo2; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelOption; 6 | import io.netty.channel.EventLoopGroup; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.nio.NioServerSocketChannel; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | *

14 | * create by 叶云轩 at 2018/4/11-下午6:33 15 | * contact by tdg_yyx@foxmail.com 16 | */ 17 | public class TimeServer { 18 | /* 19 | NioEventLoopGroup本质是一个线程组,包含一组NIO线程专门用于网络事件的处理 20 | 一个用于服务端接受客户端的连接 21 | 一个用于进行SocketChannel的网络读写 22 | */ 23 | 24 | /** 25 | * TimeServer 日志控制器 26 | * Create by 叶云轩 at 2018/4/12 上午11:26 27 | * Concat at tdg_yyx@foxmail.com 28 | */ 29 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeServer.class); 30 | 31 | /** 32 | * 绑定端口 33 | * 34 | * @param port 端口号 35 | * @throws Exception 异常 36 | */ 37 | public void bind(int port) throws Exception { 38 | LOGGER.info("--- [绑定端口] {}", port); 39 | // 声明Boss线程组 40 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 41 | // 声明Worker线程组 42 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 43 | try { 44 | LOGGER.info("--- [启动NIO] "); 45 | // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度 46 | ServerBootstrap bootstrap = new ServerBootstrap(); 47 | // 将两个NIO线程组传递到ServerBootStrap中 48 | bootstrap.group(bossGroup, workerGroup) 49 | // NioServerSocketChannel 相当于NIO中的ServerSocketChannel类 50 | .channel(NioServerSocketChannel.class) 51 | // 配置NioServerSocketChannel的TCP参数 backlog设置为1024 52 | .option(ChannelOption.SO_BACKLOG, 1024) 53 | // 绑定I/O事件处理类 54 | .childHandler(new ChildChannelHandler()); 55 | // 绑定端口,同步等待成功 56 | // channelFuture 相当于JDK的java.util.concurrent.Future用于异步操作通知回调 57 | ChannelFuture channelFuture = bootstrap.bind(port).sync(); 58 | // 等待服务端监听端口关闭 59 | channelFuture.channel().closeFuture().sync(); 60 | } finally { 61 | // 优雅退出,释放线程池资源 62 | bossGroup.shutdownGracefully(); 63 | workerGroup.shutdownGracefully(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo3/TimeServer.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo3; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelOption; 6 | import io.netty.channel.EventLoopGroup; 7 | import io.netty.channel.nio.NioEventLoopGroup; 8 | import io.netty.channel.socket.nio.NioServerSocketChannel; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | *

14 | * create by 叶云轩 at 2018/4/11-下午6:33 15 | * contact by tdg_yyx@foxmail.com 16 | */ 17 | public class TimeServer { 18 | /* 19 | NioEventLoopGroup本质是一个线程组,包含一组NIO线程专门用于网络事件的处理 20 | 一个用于服务端接受客户端的连接 21 | 一个用于进行SocketChannel的网络读写 22 | */ 23 | 24 | /** 25 | * TimeServer 日志控制器 26 | * Create by 叶云轩 at 2018/4/12 上午11:26 27 | * Concat at tdg_yyx@foxmail.com 28 | */ 29 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeServer.class); 30 | 31 | /** 32 | * 绑定端口 33 | * 34 | * @param port 端口号 35 | * 36 | * @throws Exception 异常 37 | */ 38 | public void bind(int port) throws Exception { 39 | LOGGER.info("--- [绑定端口] {}", port); 40 | // 声明Boss线程组 41 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 42 | // 声明Worker线程组 43 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 44 | try { 45 | LOGGER.info("--- [启动NIO] "); 46 | // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度 47 | ServerBootstrap bootstrap = new ServerBootstrap(); 48 | // 将两个NIO线程组传递到ServerBootStrap中 49 | bootstrap.group(bossGroup, workerGroup) 50 | // NioServerSocketChannel 相当于NIO中的ServerSocketChannel类 51 | .channel(NioServerSocketChannel.class) 52 | // 配置NioServerSocketChannel的TCP参数 backlog设置为1024 53 | .option(ChannelOption.SO_BACKLOG, 1024) 54 | // 绑定I/O事件处理类 55 | .childHandler(new ChildChannelHandler()); 56 | // 绑定端口,同步等待成功 57 | // channelFuture 相当于JDK的java.util.concurrent.Future用于异步操作通知回调 58 | ChannelFuture channelFuture = bootstrap.bind(port).sync(); 59 | // 等待服务端监听端口关闭 60 | channelFuture.channel().closeFuture().sync(); 61 | } finally { 62 | // 优雅退出,释放线程池资源 63 | bossGroup.shutdownGracefully(); 64 | workerGroup.shutdownGracefully(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo3/TimeClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo3; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerAdapter; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | *

12 | * create by 叶云轩 at 2018/4/12-上午10:16 13 | * contact by tdg_yyx@foxmail.com 14 | */ 15 | public class TimeClientHandler extends ChannelHandlerAdapter { 16 | 17 | /** 18 | * TimeClientHandler 日志控制器 19 | * Create by 叶云轩 at 2018/4/12 上午10:16 20 | * Concat at tdg_yyx@foxmail.com 21 | */ 22 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeClientHandler.class); 23 | /** 24 | * 模拟粘包/拆包问题计数器 25 | */ 26 | private int counter; 27 | 28 | /** 29 | * 30 | */ 31 | private byte[] req; 32 | 33 | public TimeClientHandler() { 34 | // region 模拟粘包/拆包问题相关代码 35 | req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes(); 36 | // endregion 37 | } 38 | 39 | /** 40 | * 捕捉异常 41 | * 42 | * @param ctx 43 | * @param cause 44 | * @throws Exception 45 | */ 46 | @Override 47 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 48 | LOGGER.warn("--- [异常,释放资源] {}", cause.getMessage()); 49 | ctx.close(); 50 | } 51 | 52 | /** 53 | * 当客户端和服务端TCP链路建立成功之后调用此方法 54 | * 发送指令给服务端,调用ChannelHandlerContext.writeAndFlush方法将请求消息发送给服务端 55 | * 56 | * @param ctx 57 | * @throws Exception 58 | */ 59 | @Override 60 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 61 | // region 模拟粘包/拆包问题相关代码 62 | ByteBuf message; 63 | for (int i = 0; i < 100; i++) { 64 | message = Unpooled.buffer(req.length); 65 | message.writeBytes(req); 66 | ctx.writeAndFlush(message); 67 | } 68 | // endregion 69 | } 70 | 71 | /** 72 | * 服务端返回应答消息时,调用此方法 73 | * 74 | * @param ctx 75 | * @param msg 76 | * @throws Exception 77 | */ 78 | @Override 79 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 80 | // region 解决粘包/拆包问题相关代码 81 | String body = (String) msg; 82 | // endregion 83 | LOGGER.info("--- [Now is] {} | [counter] {}", body, ++counter); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo2/TimeServerHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo2; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerAdapter; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * 针对网络事件进行读写操作的类 14 | *

15 | * create by 叶云轩 at 2018/4/11-下午6:35 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class TimeServerHandler extends ChannelHandlerAdapter { 19 | /** 20 | * TimeServerHandler 日志控制器 21 | * Create by 叶云轩 at 2018/4/12 上午11:30 22 | * Concat at tdg_yyx@foxmail.com 23 | */ 24 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeServerHandler.class); 25 | /** 26 | * 模拟粘包/拆包问题计数器 27 | */ 28 | private int counter; 29 | 30 | @Override 31 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 32 | ctx.close(); 33 | } 34 | 35 | /** 36 | * 读事件 37 | * 38 | * @param ctx ChannelHandlerContext 39 | * @param msg 消息 40 | * @throws Exception 41 | */ 42 | @Override 43 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 44 | // 将消息转换成ByteBuf 45 | ByteBuf buf = (ByteBuf) msg; 46 | // 获取缓冲区中的字节数 47 | byte[] req = new byte[buf.readableBytes()]; 48 | buf.readBytes(req); 49 | // region 模拟粘包/拆包问题相关代码 50 | String body = new String(req, "utf-8").substring(0, req.length - System.getProperty("line.separator").length()); 51 | LOGGER.info("--- [接收到的数据] {} | [counter] {}", body, ++counter); 52 | String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER"; 53 | currentTime = currentTime + System.getProperty("line.separator"); 54 | ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); 55 | // endregion 56 | // 异步发送应答消息给Client 57 | ctx.writeAndFlush(resp); // --> 将消息放到发送缓冲数组中 58 | } 59 | 60 | /** 61 | * 读完之后 62 | * 63 | * @param ctx 64 | * @throws Exception 65 | */ 66 | @Override 67 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 68 | LOGGER.info("--- [服务器写消息] "); 69 | // 将消息发送队列中的消息写到SocketChannel中 70 | ctx.flush(); // --> 将消息写到 SocketChannel 中 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/test/java/org/yyx/netty/study/StudyClientApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | import org.yyx.netty.study.echo.megpack.MessagePackClient; 8 | import org.yyx.netty.study.websocket.WebSocketClient; 9 | import org.yyx.netty.study.websocket.WebSocketUsers; 10 | 11 | @RunWith(SpringRunner.class) 12 | @SpringBootTest 13 | public class StudyClientApplicationTests { 14 | 15 | /** 16 | * 测试端口 17 | */ 18 | private final int port = 8080; 19 | /** 20 | * 测试IP 21 | */ 22 | private final String host = "127.0.0.1"; 23 | 24 | // region 启动未解决粘包/拆包问题的netty客户端 25 | @Test 26 | public void startNettyClient1() throws Exception { 27 | new org.yyx.netty.study.time.demo1.TimeClient().connect(port, host); 28 | } 29 | // endregion 30 | 31 | // region 启动模拟粘包/拆包问题的netty客户端 32 | @Test 33 | public void startNettyClient2() throws Exception { 34 | new org.yyx.netty.study.time.demo2.TimeClient().connect(port, host); 35 | } 36 | // endregion 37 | 38 | // region 启动已解决粘包/拆包问题的netty客户端 - LineBasedFrameDecoder 实现 39 | @Test 40 | public void startNettyClient3() throws Exception { 41 | new org.yyx.netty.study.time.demo3.TimeClient().connect(port, host); 42 | } 43 | // endregion 44 | 45 | // region 启动已解决粘包/拆包问题的netty客户端 - DelimiterBasedFrameDecoder 实现 46 | @Test 47 | public void startNettyClient4() throws Exception { 48 | new org.yyx.netty.study.echo.delimiter.EchoClient().connect(port, host); 49 | } 50 | // endregion 51 | 52 | // region 启动已解决粘包/拆包问题的netty服务 - FixedLengthFrameDecoder 实现 53 | @Test 54 | public void startNettyServer5() throws Exception { 55 | new org.yyx.netty.study.echo.fixlength.EchoClient().connect(port, host); 56 | } 57 | // endregion 58 | 59 | // region 启动不考虑粘包/拆包问题 基于MessagePack编解码的Netty客户端 60 | @Test 61 | public void testMessagePackEchoClient() throws Exception { 62 | new MessagePackClient().connect(port, host, 20); 63 | } 64 | // endregion 65 | 66 | // region 启动WebSocketClient 67 | @Test 68 | public void startWebSocketClient() throws Exception { 69 | new WebSocketClient().connect(9999, host, "yyx"); 70 | } 71 | 72 | @Test 73 | public void startWebSocketClient2() throws Exception { 74 | new WebSocketClient().connect(9999, host, "YeYunXuan"); 75 | } 76 | // endregion 77 | } 78 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/time/demo2/TimeClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo2; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerAdapter; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | /** 11 | *

12 | * create by 叶云轩 at 2018/4/12-上午10:16 13 | * contact by tdg_yyx@foxmail.com 14 | */ 15 | public class TimeClientHandler extends ChannelHandlerAdapter { 16 | 17 | /** 18 | * TimeClientHandler 日志控制器 19 | * Create by 叶云轩 at 2018/4/12 上午10:16 20 | * Concat at tdg_yyx@foxmail.com 21 | */ 22 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeClientHandler.class); 23 | /** 24 | * 模拟粘包/拆包问题计数器 25 | */ 26 | private int counter; 27 | 28 | /** 29 | * 30 | */ 31 | private byte[] req; 32 | 33 | public TimeClientHandler() { 34 | // region 模拟粘包/拆包问题相关代码 35 | req = ("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes(); 36 | // endregion 37 | } 38 | 39 | /** 40 | * 捕捉异常 41 | * 42 | * @param ctx 43 | * @param cause 44 | * @throws Exception 45 | */ 46 | @Override 47 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 48 | LOGGER.warn("--- [异常,释放资源] {}", cause.getMessage()); 49 | ctx.close(); 50 | } 51 | 52 | /** 53 | * 当客户端和服务端TCP链路建立成功之后调用此方法 54 | * 发送指令给服务端,调用ChannelHandlerContext.writeAndFlush方法将请求消息发送给服务端 55 | * 56 | * @param ctx 57 | * @throws Exception 58 | */ 59 | @Override 60 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 61 | // region 模拟粘包/拆包问题相关代码 62 | ByteBuf message; 63 | for (int i = 0; i < 100; i++) { 64 | message = Unpooled.buffer(req.length); 65 | message.writeBytes(req); 66 | ctx.writeAndFlush(message); 67 | } 68 | // endregion 69 | } 70 | 71 | /** 72 | * 服务端返回应答消息时,调用此方法 73 | * 74 | * @param ctx 75 | * @param msg 76 | * @throws Exception 77 | */ 78 | @Override 79 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 80 | ByteBuf byteBuf = (ByteBuf) msg; 81 | byte[] request = new byte[byteBuf.readableBytes()]; 82 | byteBuf.readBytes(request); 83 | String body = new String(request, "utf-8"); 84 | LOGGER.info("--- [Now is] {} | [counter] {}", body, ++counter); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/fixlength/EchoServer.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.fixlength; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | import io.netty.handler.codec.FixedLengthFrameDecoder; 12 | import io.netty.handler.codec.string.StringDecoder; 13 | import io.netty.handler.logging.LogLevel; 14 | import io.netty.handler.logging.LoggingHandler; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | /** 19 | * EchoServer服务端 20 | *

21 | * create by 叶云轩 at 2018/4/12-下午4:07 22 | * contact by tdg_yyx@foxmail.com 23 | */ 24 | public class EchoServer { 25 | /** 26 | * MessagePackServer 日志控制器 27 | * Create by 叶云轩 at 2018/4/12 下午4:09 28 | * Concat at tdg_yyx@foxmail.com 29 | */ 30 | private static final Logger LOGGER = LoggerFactory.getLogger(EchoServer.class); 31 | 32 | 33 | public void bind(int port) throws Exception { 34 | LOGGER.info("--- [绑定端口] {}", port); 35 | // 声明Boss线程组 36 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 37 | // 声明Worker线程组 38 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 39 | try { 40 | LOGGER.info("--- [启动NIO] "); 41 | // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度 42 | ServerBootstrap bootstrap = new ServerBootstrap(); 43 | // 将两个NIO线程组传递到ServerBootStrap中 44 | bootstrap.group(bossGroup, workerGroup) 45 | .channel(NioServerSocketChannel.class) 46 | .option(ChannelOption.SO_BACKLOG, 100) 47 | .handler(new LoggingHandler(LogLevel.INFO)) 48 | .childHandler(new ChannelInitializer() { 49 | @Override 50 | protected void initChannel(SocketChannel ch) throws Exception { 51 | ch.pipeline().addLast(new FixedLengthFrameDecoder(20)); 52 | ch.pipeline().addLast(new StringDecoder()); 53 | ch.pipeline().addLast(new EchoServerHandler()); 54 | } 55 | }); 56 | ChannelFuture future = bootstrap.bind(port).sync(); 57 | future.channel().closeFuture().sync(); 58 | } finally { 59 | // 优雅退出,释放线程池资源 60 | bossGroup.shutdownGracefully(); 61 | workerGroup.shutdownGracefully(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/NettyBeanScanner.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.rpc.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 5 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 6 | import org.springframework.beans.factory.support.BeanDefinitionBuilder; 7 | import org.springframework.beans.factory.support.DefaultListableBeanFactory; 8 | import org.yyx.netty.client.RPCProxyFactoryBean; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * 主要用于Netty框架初始化远程服务类 14 | *

15 | * BeanFactoryPostProcessor : Spring初始化bean时对外暴露的扩展点,即可以在Spring工厂初始化的时候做点什么,属于Spring知识点 16 | * 17 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 18 | * @date 2018/8/15 - 12:28 19 | */ 20 | public class NettyBeanScanner implements BeanFactoryPostProcessor { 21 | 22 | /** 23 | * 装载bean的工厂 24 | */ 25 | private DefaultListableBeanFactory beanFactory; 26 | /** 27 | * 包名 28 | */ 29 | private String basePackage; 30 | /** 31 | * bean名(引用名) 32 | */ 33 | private String clientName; 34 | 35 | /** 36 | * 有参构造 37 | * 38 | * @param basePackage 待扫描包名 39 | * @param clientName netty客户端beanName 40 | */ 41 | public NettyBeanScanner(String basePackage, String clientName) { 42 | this.basePackage = basePackage; 43 | this.clientName = clientName; 44 | } 45 | 46 | 47 | /** 48 | * 注册远程接口Bean到Spring的bean工厂 49 | * 50 | * @param beanFactory 装载bean的工厂 51 | * @throws BeansException bean异常 52 | */ 53 | @Override 54 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 55 | this.beanFactory = (DefaultListableBeanFactory) beanFactory; 56 | // 从目录中加载远程服务的接口 57 | List resolverClass = PackageClassUtils.resolver(basePackage); 58 | for (String clazz : resolverClass) { 59 | // 获取接口名 60 | String simpleName; 61 | // 接口全限定名 62 | if (clazz.lastIndexOf('.') != -1) { 63 | simpleName = clazz.substring(clazz.lastIndexOf('.') + 1); 64 | } else { 65 | simpleName = clazz; 66 | } 67 | // 使用建造者模式创建一个Bean定义 68 | BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(RPCProxyFactoryBean.class); 69 | // 对应 RPCProxyFactoryBean 类的 interfaceClass 属性 70 | beanDefinitionBuilder.addPropertyValue("interfaceClass", clazz); 71 | // 对应 RPCProxyFactoryBean 的nettyClient 属性 -- 已删 72 | // beanDefinitionBuilder.addPropertyReference("nettyClient", clientName); 73 | // 注册对bean的定义 74 | this.beanFactory.registerBeanDefinition(simpleName, beanDefinitionBuilder.getRawBeanDefinition()); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/time/demo1/TimeServerHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.time.demo1; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.channel.ChannelHandlerAdapter; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.Date; 11 | 12 | /** 13 | * 针对网络事件进行读写操作的类 14 | *

15 | * create by 叶云轩 at 2018/4/11-下午6:35 16 | * contact by tdg_yyx@foxmail.com 17 | */ 18 | public class TimeServerHandler extends ChannelHandlerAdapter { 19 | /** 20 | * TimeServerHandler 日志控制器 21 | * Create by 叶云轩 at 2018/4/12 上午11:30 22 | * Concat at tdg_yyx@foxmail.com 23 | */ 24 | private static final Logger LOGGER = LoggerFactory.getLogger(TimeServerHandler.class); 25 | /** 26 | * 模拟粘包/拆包问题计数器 27 | */ 28 | private int counter; 29 | 30 | @Override 31 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 32 | ctx.close(); 33 | } 34 | 35 | /** 36 | * 读事件 37 | * 38 | * @param ctx ChannelHandlerContext 39 | * @param msg 消息 40 | * 41 | * @throws Exception 42 | */ 43 | @Override 44 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 45 | // 将消息转换成ByteBuf 46 | ByteBuf buf = (ByteBuf) msg; 47 | // 获取缓冲区中的字节数 48 | byte[] req = new byte[buf.readableBytes()]; 49 | buf.readBytes(req); 50 | String body = new String(req, "utf-8"); 51 | LOGGER.info("--- [接收到的数据] {}", body); 52 | String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : 53 | "BAD ORDER"; 54 | ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); 55 | 56 | // region 模拟粘包/拆包问题相关代码 57 | // String body = new String(req, "utf-8").substring(0, req.length - System.getProperty("line.separator").length()); 58 | // LOGGER.info("--- [接收到的数据] {} | [counter] {}", body, ++counter); 59 | // String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER"; 60 | // currentTime = currentTime + System.getProperty("line.separator"); 61 | // ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); 62 | // endregion 63 | 64 | 65 | // 异步发送应答消息给Client 66 | ctx.write(resp); // --> 将消息放到发送缓冲数组中 67 | } 68 | 69 | /** 70 | * 读完之后 71 | * 72 | * @param ctx 73 | * 74 | * @throws Exception 75 | */ 76 | @Override 77 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 78 | LOGGER.info("--- [服务器写消息] "); 79 | // 将消息发送队列中的消息写到SocketChannel中 80 | ctx.flush(); // --> 将消息写到 SocketChannel 中 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/NettyClientHandlerAdapter.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.client; 2 | 3 | import io.netty.channel.ChannelHandlerAdapter; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.ChannelPromise; 6 | import io.netty.handler.timeout.IdleStateEvent; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.yyx.netty.rpc.util.ChannelUtil; 10 | 11 | /** 12 | * 自定义的NettyClientHandler 13 | *

14 | * 15 | * @author 叶云轩 at tdg_yyx@foxmail.com 16 | * @date 2018/11/1-17:20 17 | */ 18 | public class NettyClientHandlerAdapter extends ChannelHandlerAdapter { 19 | 20 | /** 21 | * NettyClientHandlerAdapter 日志输出 22 | */ 23 | private static final Logger LOGGER = LoggerFactory.getLogger(NettyClientHandlerAdapter.class); 24 | 25 | @Override 26 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 27 | LOGGER.error("{} -> [通道异常] {}", this.getClass().getName(), ctx.channel().id()); 28 | ChannelUtil.remove(ctx.channel()); 29 | } 30 | 31 | @Override 32 | public void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception { 33 | LOGGER.info("{} -> [连接建立成功] {}", this.getClass().getName(), channelHandlerContext.channel().id()); 34 | // 注册通道 35 | ChannelUtil.registerChannel(channelHandlerContext.channel()); 36 | } 37 | 38 | @Override 39 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 40 | if (!(msg instanceof Exception)) { 41 | LOGGER.info("{} -> [客户端收到的消息] {}", this.getClass().getName(), msg); 42 | } 43 | String longText = ctx.channel().id().asLongText(); 44 | String resultKey = ChannelUtil.getResultKey(longText); 45 | ChannelUtil.calculateResult(resultKey, msg); 46 | } 47 | 48 | @Override 49 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { 50 | LOGGER.info("{} -> [客户端消息接收完毕] {}", this.getClass().getName(), ctx.channel().id()); 51 | super.channelReadComplete(ctx); 52 | boolean active = ctx.channel().isActive(); 53 | LOGGER.info("{} -> [此时通道状态] {}", this.getClass().getName(), active); 54 | } 55 | 56 | @Override 57 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 58 | LOGGER.info("{} -> [客户端心跳监测发送] 通道编号:{}", this.getClass().getName(), ctx.channel().id()); 59 | if (evt instanceof IdleStateEvent) { 60 | ctx.writeAndFlush("ping-pong-ping-pong"); 61 | } else { 62 | super.userEventTriggered(ctx, evt); 63 | } 64 | } 65 | 66 | @Override 67 | public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { 68 | LOGGER.info("{} -> [关闭通道] {}", this.getClass().getName(), ctx.channel().id()); 69 | super.close(ctx, promise); 70 | // ChannelUtil.remove(ctx.channel()); 71 | } 72 | } -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/megpack/MessagePackServer.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.megpack; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.EventLoopGroup; 8 | import io.netty.channel.nio.NioEventLoopGroup; 9 | import io.netty.channel.socket.SocketChannel; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | import io.netty.handler.logging.LogLevel; 12 | import io.netty.handler.logging.LoggingHandler; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.yyx.netty.study.codec.msgpack.MsgPackDecoder; 16 | import org.yyx.netty.study.codec.msgpack.MsgPackEncoder; 17 | 18 | /** 19 | * EchoServer服务端 20 | *

21 | * create by 叶云轩 at 2018/4/12-下午4:07 22 | * contact by tdg_yyx@foxmail.com 23 | */ 24 | public class MessagePackServer { 25 | /** 26 | * MessagePackServer 日志控制器 27 | * Create by 叶云轩 at 2018/4/12 下午4:09 28 | * Concat at tdg_yyx@foxmail.com 29 | */ 30 | private static final Logger LOGGER = LoggerFactory.getLogger(MessagePackServer.class); 31 | 32 | 33 | public void bind(int port) throws Exception { 34 | LOGGER.info("--- [绑定端口] {}", port); 35 | // 声明Boss线程组 36 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 37 | // 声明Worker线程组 38 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 39 | try { 40 | LOGGER.info("--- [启动NIO] "); 41 | // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度 42 | ServerBootstrap bootstrap = new ServerBootstrap(); 43 | // 将两个NIO线程组传递到ServerBootStrap中 44 | bootstrap.group(bossGroup, workerGroup) 45 | .channel(NioServerSocketChannel.class) 46 | .option(ChannelOption.SO_BACKLOG, 100) 47 | .childHandler(new ChannelInitializer() { 48 | @Override 49 | protected void initChannel(SocketChannel ch) throws Exception { 50 | ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO)); 51 | // ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2)); 52 | ch.pipeline().addLast(new MsgPackDecoder()); 53 | // ch.pipeline().addLast(new LengthFieldPrepender(2)); 54 | ch.pipeline().addLast(new MsgPackEncoder()); 55 | ch.pipeline().addLast(new MessagePackServerHandler()); 56 | } 57 | }); 58 | ChannelFuture future = bootstrap.bind(port).sync(); 59 | future.channel().closeFuture().sync(); 60 | } finally { 61 | // 优雅退出,释放线程池资源 62 | bossGroup.shutdownGracefully(); 63 | workerGroup.shutdownGracefully(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/echo/delimiter/EchoServer.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.echo.delimiter; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelInitializer; 8 | import io.netty.channel.ChannelOption; 9 | import io.netty.channel.EventLoopGroup; 10 | import io.netty.channel.nio.NioEventLoopGroup; 11 | import io.netty.channel.socket.SocketChannel; 12 | import io.netty.channel.socket.nio.NioServerSocketChannel; 13 | import io.netty.handler.codec.DelimiterBasedFrameDecoder; 14 | import io.netty.handler.codec.string.StringDecoder; 15 | import io.netty.handler.logging.LogLevel; 16 | import io.netty.handler.logging.LoggingHandler; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | /** 21 | * EchoServer服务端 22 | *

23 | * create by 叶云轩 at 2018/4/12-下午4:07 24 | * contact by tdg_yyx@foxmail.com 25 | */ 26 | public class EchoServer { 27 | /** 28 | * MessagePackServer 日志控制器 29 | * Create by 叶云轩 at 2018/4/12 下午4:09 30 | * Concat at tdg_yyx@foxmail.com 31 | */ 32 | private static final Logger LOGGER = LoggerFactory.getLogger(EchoServer.class); 33 | 34 | 35 | public void bind(int port) throws Exception { 36 | LOGGER.info("--- [绑定端口] {}", port); 37 | // 声明Boss线程组 38 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 39 | // 声明Worker线程组 40 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 41 | try { 42 | LOGGER.info("--- [启动NIO] "); 43 | // Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度 44 | ServerBootstrap bootstrap = new ServerBootstrap(); 45 | // 将两个NIO线程组传递到ServerBootStrap中 46 | bootstrap.group(bossGroup, workerGroup) 47 | .channel(NioServerSocketChannel.class) 48 | .option(ChannelOption.SO_BACKLOG, 100) 49 | .handler(new LoggingHandler(LogLevel.INFO)) 50 | .childHandler(new ChannelInitializer() { 51 | @Override 52 | protected void initChannel(SocketChannel ch) throws Exception { 53 | // 设置分隔符为 $_$ 54 | ByteBuf delimiter = Unpooled.copiedBuffer("$_$".getBytes()); 55 | // 单条消息最大长度不能超过1024 56 | ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter)); 57 | ch.pipeline().addLast(new StringDecoder()); 58 | ch.pipeline().addLast(new EchoServerHandler()); 59 | } 60 | }); 61 | ChannelFuture future = bootstrap.bind(port).sync(); 62 | future.channel().closeFuture().sync(); 63 | } finally { 64 | // 优雅退出,释放线程池资源 65 | bossGroup.shutdownGracefully(); 66 | workerGroup.shutdownGracefully(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/client/RPCProxyFactoryBean.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.client; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.config.AbstractFactoryBean; 6 | import org.yyx.netty.entity.MethodInvokeMeta; 7 | import org.yyx.netty.exception.ErrorParamsException; 8 | import org.yyx.netty.rpc.util.ChannelUtil; 9 | import org.yyx.netty.rpc.util.WrapMethodUtils; 10 | 11 | import java.lang.reflect.InvocationHandler; 12 | import java.lang.reflect.Method; 13 | import java.lang.reflect.Proxy; 14 | import java.util.UUID; 15 | 16 | /** 17 | * JDK动态代理类 18 | *

19 | * 20 | * @author 叶云轩 contact by marquis_xuan@163.com 21 | * @date 2018/11/1 - 15:49 22 | */ 23 | public class RPCProxyFactoryBean extends AbstractFactoryBean implements InvocationHandler { 24 | /** 25 | * RPCProxyFactoryBean 日志输出 26 | */ 27 | private static final Logger LOGGER = LoggerFactory.getLogger(RPCProxyFactoryBean.class); 28 | /** 29 | * 远程服务接口 30 | */ 31 | private Class interfaceClass; 32 | 33 | @Override 34 | public Class getObjectType() { 35 | return interfaceClass; 36 | } 37 | 38 | /** 39 | * 创建实例的方法 40 | * 41 | * @return 由工厂创建的实例 42 | */ 43 | @Override 44 | protected Object createInstance() { 45 | LOGGER.info("[代理工厂] 初始化代理Bean : {}", interfaceClass); 46 | // 返回代理类 47 | return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, this); 48 | } 49 | 50 | /** 51 | * 动态调用方法的方法 52 | * 该方法不会显示调用 53 | * 54 | * @param proxy 被代理的实例 55 | * @param method 调用的方法 56 | * @param args 参数列表 57 | * @return 返回值 58 | */ 59 | @Override 60 | public Object invoke(Object proxy, Method method, Object[] args) throws ErrorParamsException { 61 | LOGGER.info("{} -> [准备进行远程服务调用] ", this.getClass().getName()); 62 | LOGGER.info("{} -> [封装调用信息] ", this.getClass().getName()); 63 | final MethodInvokeMeta methodInvokeMeta = WrapMethodUtils.readMethod(interfaceClass, method, args); 64 | LOGGER.info("{} -> [远程服务调用封装完毕] 调用接口 -> {}\n调用方法 -> {}\n参数列表 -> {} \n 参数类型 -> {}" + 65 | "\n 返回值类型 -> {}", this.getClass().getName(), methodInvokeMeta.getInterfaceClass(), methodInvokeMeta.getMethodName() 66 | , methodInvokeMeta.getArgs(), methodInvokeMeta.getParameterTypes(), methodInvokeMeta.getReturnType()); 67 | // 构造一个时间戳 68 | String uuid = System.currentTimeMillis() + UUID.randomUUID().toString(); 69 | // 真正开始使用netty进行通信的方法 70 | ChannelUtil.remoteCall(methodInvokeMeta, uuid); 71 | Object result; 72 | do { 73 | // 接收返回信息 74 | result = ChannelUtil.getResult(uuid); 75 | } while (result == null); 76 | // 服务器有可能返回异常信息,所以在这里可以进行异常处理 77 | if (result instanceof ErrorParamsException) { 78 | throw (ErrorParamsException) result; 79 | } 80 | return result; 81 | } 82 | 83 | public void setInterfaceClass(Class interfaceClass) { 84 | this.interfaceClass = interfaceClass; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/test/java/org/yyx/netty/study/CodeCTest.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study; 2 | 3 | import org.junit.Test; 4 | import org.msgpack.MessagePack; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.yyx.netty.entity.User; 8 | 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.IOException; 11 | import java.io.ObjectOutputStream; 12 | import java.nio.ByteBuffer; 13 | 14 | /** 15 | *

16 | * create by 叶云轩 at 2018/4/12-下午6:50 17 | * contact by tdg_yyx@foxmail.com 18 | * 19 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 20 | * @date 2018/8/15 - 12:34 21 | */ 22 | public class CodeCTest { 23 | /** 24 | * CodeCTest 日志控制器 25 | * Create by 叶云轩 at 2018/4/12 下午6:53 26 | * Concat at tdg_yyx@foxmail.com 27 | */ 28 | private static final Logger LOGGER = LoggerFactory.getLogger(CodeCTest.class); 29 | 30 | // region jdk序列化与基于ByteBuffer的二进制序列化字节数对比 31 | @Test 32 | public void testCodeC() throws IOException { 33 | User user = new User(); 34 | user.setName("yyx"); 35 | user.setId(1); 36 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 37 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); 38 | objectOutputStream.writeObject(user); 39 | objectOutputStream.flush(); 40 | objectOutputStream.close(); 41 | byte[] bytes = byteArrayOutputStream.toByteArray(); 42 | LOGGER.info("--- [JDK] {}", bytes.length); 43 | byteArrayOutputStream.close(); 44 | LOGGER.info("--- [byte] {}", user.codeC().length); 45 | 46 | MessagePack messagePack = new MessagePack(); 47 | // 序列化 48 | byte[] write = messagePack.write(user); 49 | LOGGER.info("--- [MessagePack] {}", write.length); 50 | } 51 | // endregion 52 | 53 | // region jdk序列化效率与基于ByteBuffer的二进制序列化效率对比 54 | @Test 55 | public void testCodec() throws Exception { 56 | User user = new User(); 57 | user.setName("yyx"); 58 | user.setId(1); 59 | int loop = 1000000; 60 | ByteArrayOutputStream bos; 61 | ObjectOutputStream os; 62 | long startTime = System.currentTimeMillis(); 63 | for (int i = 0; i < loop; i++) { 64 | bos = new ByteArrayOutputStream(); 65 | os = new ObjectOutputStream(bos); 66 | os.writeObject(user); 67 | os.flush(); 68 | os.close(); 69 | byte[] bytes = bos.toByteArray(); 70 | bos.close(); 71 | } 72 | long endTime = System.currentTimeMillis(); 73 | LOGGER.info("--- [jdk耗时] {}", endTime - startTime + "ms"); 74 | ByteBuffer buffer = ByteBuffer.allocate(1024); 75 | startTime = System.currentTimeMillis(); 76 | for (int i = 0; i < loop; i++) { 77 | byte[] bytes = user.codeC(buffer); 78 | } 79 | endTime = System.currentTimeMillis(); 80 | LOGGER.info("--- [byte耗时] {}", endTime - startTime + "ms"); 81 | startTime = System.currentTimeMillis(); 82 | MessagePack messagePack = new MessagePack(); 83 | for (int i = 0; i < loop; i++) { 84 | // 序列化 85 | byte[] write = messagePack.write(user); 86 | } 87 | endTime = System.currentTimeMillis(); 88 | LOGGER.info("--- [MessagePack 耗时] {}", endTime - startTime + "ms"); 89 | 90 | 91 | } 92 | // endregion 93 | 94 | } 95 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/ChannelUtil.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.rpc.util; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelId; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.yyx.netty.entity.MethodInvokeMeta; 9 | import org.yyx.netty.exception.NoUseableChannel; 10 | 11 | import java.util.Iterator; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | import java.util.concurrent.ConcurrentSkipListSet; 16 | 17 | /** 18 | * 通道Util 19 | *

20 | * 21 | * @author 叶云轩 at marquis_xuan@163.com 22 | * @date 2018/11/2-15:39 23 | */ 24 | public class ChannelUtil { 25 | /** 26 | * 用于记录c-s连接后建立的通道 27 | */ 28 | private static final Set CHANNELS = new ConcurrentSkipListSet<>(); 29 | /** 30 | * ChannelUtil日志输出 31 | */ 32 | private static final Logger LOGGER = LoggerFactory.getLogger(ChannelUtil.class); 33 | /** 34 | * 用于记录通道响应的结果集 35 | */ 36 | private static final Map RESULT_MAP = new ConcurrentHashMap<>(); 37 | 38 | private ChannelUtil() { 39 | } 40 | 41 | /** 42 | * 计算结果集(存储响应结果) 43 | * 44 | * @param key 唯一标识 45 | * @param result 结果集 46 | */ 47 | public static void calculateResult(String key, Object result) { 48 | RESULT_MAP.put(key, result); 49 | } 50 | 51 | /** 52 | * 获取结果集的key 53 | * 54 | * @param key 保存的唯一标识 55 | * @return 结果集的 key (通道标识) 56 | */ 57 | public static String getResultKey(String key) { 58 | return (String) getResult(key); 59 | } 60 | 61 | /** 62 | * 根据结果集的 key 获取结果集 63 | * 64 | * @param key 结果集的key 65 | * @return 结果集 66 | */ 67 | public static Object getResult(String key) { 68 | return RESULT_MAP.get(key); 69 | } 70 | 71 | /** 72 | * 注册通道 73 | * 74 | * @param channel 通道 75 | */ 76 | public static void registerChannel(Channel channel) { 77 | ChannelId id = channel.id(); 78 | LOGGER.info("{} -> [添加通道] {}", ChannelUtil.class.getName(), id); 79 | CHANNELS.add(channel); 80 | } 81 | 82 | /** 83 | * 获取回调结果 84 | * 85 | * @param methodInvokeMeta 远程调用方法信息 86 | * @param key 用于取结果的key值 87 | */ 88 | public static void remoteCall(MethodInvokeMeta methodInvokeMeta, String key) { 89 | LOGGER.info("{} -> [远程调用] ", ChannelUtil.class.getName()); 90 | Iterator iterator = CHANNELS.iterator(); 91 | Channel channel; 92 | if (iterator.hasNext()) { 93 | channel = iterator.next(); 94 | } else { 95 | LOGGER.error("{} -> [没有活跃的通道] ", ChannelUtil.class); 96 | throw new NoUseableChannel("没有活跃的通道"); 97 | } 98 | // 将用于获取结果的key保存,以通道id为键 99 | String channelID = channel.id().asLongText(); 100 | LOGGER.info("{} -> [保存获取结果的key] key - {} 通道id - {}", ChannelUtil.class, key, channelID); 101 | RESULT_MAP.put(channelID, key); 102 | ChannelFuture channelFuture = channel.writeAndFlush(methodInvokeMeta); 103 | // channelFuture.addListener(ChannelFutureListener.CLOSE); 104 | } 105 | 106 | public static void remove(Channel channel) { 107 | CHANNELS.remove(channel); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-client/src/main/java/org/yyx/netty/rpc/util/PackageClassUtils.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.rpc.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.io.File; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | *

12 | * 13 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 14 | * @date 2018/8/15 - 12:28 15 | */ 16 | public class PackageClassUtils { 17 | private final static Logger LOGGER = LoggerFactory.getLogger(PackageClassUtils.class); 18 | 19 | /** 20 | * 获取一个目录下的所有文件 21 | * 22 | * @param s 23 | * @param file 24 | * @param classStrs 25 | */ 26 | private static void getAllFile(String s, File file, List classStrs) { 27 | if (file.isDirectory()) { 28 | File[] files = file.listFiles(); 29 | if (files != null) 30 | for (File file1 : files) { 31 | getAllFile(s, file1, classStrs); 32 | } 33 | } else { 34 | String path = file.getPath(); 35 | String cleanPath = path.replaceAll("/", "."); 36 | String fileName = cleanPath.substring(cleanPath.indexOf(s), cleanPath.length()); 37 | LOGGER.info("[加载完成] 类文件:{}", fileName); 38 | classStrs.add(fileName); 39 | } 40 | } 41 | 42 | /** 43 | * 添加全限定类名到集合 44 | * 45 | * @param classStrs 集合 46 | * @return 类名集合 47 | */ 48 | private static List getClassReferenceList(List classStrs, File file, String s) { 49 | File[] listFiles = file.listFiles(); 50 | if (listFiles != null && listFiles.length != 0) { 51 | for (File file2 : listFiles) { 52 | if (file2.isFile()) { 53 | String name = file2.getName(); 54 | String fileName = s + "." + name.substring(0, name.lastIndexOf('.')); 55 | LOGGER.info("[加载完成] 类文件:{}", fileName); 56 | classStrs.add(fileName); 57 | } 58 | } 59 | } 60 | return classStrs; 61 | } 62 | 63 | /** 64 | * 解析包参数 65 | * 66 | * @param basePackage 包名 67 | * @return 包名字符串集合 68 | */ 69 | public static List resolver(String basePackage) { 70 | // 以";"分割开多个包名 71 | String[] splitFHs = basePackage.split(";"); 72 | List classStrs = new ArrayList<>(); 73 | // s: com.yyx.util.* 74 | for (String s : splitFHs) { 75 | LOGGER.info("[加载类目录] {}", s); 76 | // 路径中是否存在".*" com.yyx.util.* 77 | boolean contains = s.contains(".*"); 78 | if (contains) { 79 | // 截断星号 com.yyx.util 80 | String filePathStr = s.substring(0, s.lastIndexOf(".*")); 81 | // 组装路径 com/yyx/util 82 | String filePath = filePathStr.replaceAll("\\.", "/"); 83 | // 获取路径 xxx/classes/com/yyx/util 84 | File file = new File(PackageClassUtils.class.getResource("/").getPath() + "/" + filePath); 85 | // 获取目录下获取文件 86 | getAllFile(filePathStr, file, classStrs); 87 | } else { 88 | String filePath = s.replaceAll("\\.", "/"); 89 | File file = new File(PackageClassUtils.class.getResource("/").getPath() + "/" + filePath); 90 | classStrs = getClassReferenceList(classStrs, file, s); 91 | } 92 | } 93 | return classStrs; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketUsers.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 5 | import io.netty.util.internal.PlatformDependent; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.Collection; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import java.util.concurrent.ConcurrentMap; 13 | 14 | /** 15 | * WebSocket用户集 16 | */ 17 | public class WebSocketUsers { 18 | 19 | /** 20 | * 用户集 21 | */ 22 | private static final ConcurrentMap USERS = PlatformDependent.newConcurrentHashMap(); 23 | /** 24 | * WebSocketUsers 日志控制器 25 | * Create by 叶云轩 at 2018/5/15 下午5:41 26 | * Concat at tdg_yyx@foxmail.com 27 | */ 28 | private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class); 29 | 30 | private static WebSocketUsers ourInstance = new WebSocketUsers(); 31 | 32 | private WebSocketUsers() { 33 | } 34 | 35 | public static WebSocketUsers getInstance() { 36 | return ourInstance; 37 | } 38 | 39 | /** 40 | * 存储通道 41 | * 42 | * @param key 唯一键 43 | * @param channel 通道 44 | */ 45 | public static void put(String key, Channel channel) { 46 | USERS.put(key, channel); 47 | } 48 | 49 | /** 50 | * 移除通道 51 | * 52 | * @param channel 通道 53 | * 54 | * @return 移除结果 55 | */ 56 | public static boolean remove(Channel channel) { 57 | String key = null; 58 | boolean b = USERS.containsValue(channel); 59 | if (b) { 60 | Set> entries = USERS.entrySet(); 61 | for (Map.Entry entry : entries) { 62 | Channel value = entry.getValue(); 63 | if (value.equals(channel)) { 64 | key = entry.getKey(); 65 | break; 66 | } 67 | } 68 | } else { 69 | return true; 70 | } 71 | return remove(key); 72 | } 73 | 74 | /** 75 | * 移出通道 76 | * 77 | * @param key 键 78 | */ 79 | public static boolean remove(String key) { 80 | Channel remove = USERS.remove(key); 81 | boolean containsValue = USERS.containsValue(remove); 82 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 83 | "\t├ [移出结果]: {}\n" + 84 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", containsValue ? "失败" : "成功"); 85 | return containsValue; 86 | } 87 | 88 | /** 89 | * 获取在线用户列表 90 | * 91 | * @return 返回用户集合 92 | */ 93 | public static ConcurrentMap getUSERS() { 94 | return USERS; 95 | } 96 | 97 | /** 98 | * 群发消息 99 | * 100 | * @param message 消息内容 101 | */ 102 | public static void sendMessageToUsers(String message) { 103 | Collection values = USERS.values(); 104 | for (Channel value : values) { 105 | value.write(new TextWebSocketFrame(message)); 106 | value.flush(); 107 | } 108 | } 109 | 110 | /** 111 | * 给某个人发送消息 112 | * 113 | * @param userName key 114 | * @param message 消息 115 | */ 116 | public static void sendMessageToUser(String userName, String message) { 117 | Channel channel = USERS.get(userName); 118 | channel.write(new TextWebSocketFrame(message)); 119 | channel.flush(); 120 | } 121 | } -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketUsers.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 5 | import io.netty.util.internal.PlatformDependent; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.Collection; 10 | import java.util.Map; 11 | import java.util.Set; 12 | import java.util.concurrent.ConcurrentMap; 13 | 14 | /** 15 | * WebSocket用户集 16 | */ 17 | public class WebSocketUsers { 18 | 19 | /** 20 | * 用户集 21 | */ 22 | private static final ConcurrentMap USERS = PlatformDependent.newConcurrentHashMap(); 23 | /** 24 | * WebSocketUsers 日志控制器 25 | * Create by 叶云轩 at 2018/5/15 下午5:41 26 | * Concat at tdg_yyx@foxmail.com 27 | */ 28 | private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class); 29 | 30 | private static WebSocketUsers ourInstance = new WebSocketUsers(); 31 | 32 | private WebSocketUsers() { 33 | } 34 | 35 | public static WebSocketUsers getInstance() { 36 | return ourInstance; 37 | } 38 | 39 | /** 40 | * 存储通道 41 | * 42 | * @param key 唯一键 43 | * @param channel 通道 44 | */ 45 | public static void put(String key, Channel channel) { 46 | USERS.put(key, channel); 47 | } 48 | 49 | /** 50 | * 移除通道 51 | * 52 | * @param channel 通道 53 | * 54 | * @return 移除结果 55 | */ 56 | public static boolean remove(Channel channel) { 57 | String key = null; 58 | boolean b = USERS.containsValue(channel); 59 | if (b) { 60 | Set> entries = USERS.entrySet(); 61 | for (Map.Entry entry : entries) { 62 | Channel value = entry.getValue(); 63 | if (value.equals(channel)) { 64 | key = entry.getKey(); 65 | break; 66 | } 67 | } 68 | } else { 69 | return true; 70 | } 71 | return remove(key); 72 | } 73 | 74 | /** 75 | * 移出通道 76 | * 77 | * @param key 键 78 | */ 79 | public static boolean remove(String key) { 80 | Channel remove = USERS.remove(key); 81 | boolean containsValue = USERS.containsValue(remove); 82 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 83 | "\t├ [移出结果]: {}\n" + 84 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", containsValue ? "失败" : "成功"); 85 | return containsValue; 86 | } 87 | 88 | /** 89 | * 获取在线用户列表 90 | * 91 | * @return 返回用户集合 92 | */ 93 | public static ConcurrentMap getUSERS() { 94 | return USERS; 95 | } 96 | 97 | /** 98 | * 群发消息 99 | * 100 | * @param message 消息内容 101 | */ 102 | public static void sendMessageToUsers(String message) { 103 | Collection values = USERS.values(); 104 | for (Channel value : values) { 105 | value.write(new TextWebSocketFrame(message)); 106 | value.flush(); 107 | } 108 | } 109 | 110 | /** 111 | * 给某个人发送消息 112 | * 113 | * @param userName key 114 | * @param message 消息 115 | */ 116 | public static void sendMessageToUser(String userName, String message) { 117 | Channel channel = USERS.get(userName); 118 | channel.write(new TextWebSocketFrame(message)); 119 | channel.flush(); 120 | } 121 | } -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/adapter/ServerChannelHandlerAdapter.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.server.adapter; 2 | 3 | import io.netty.channel.ChannelHandler.Sharable; 4 | import io.netty.channel.ChannelHandlerAdapter; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import io.netty.handler.timeout.IdleState; 7 | import io.netty.handler.timeout.IdleStateEvent; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.stereotype.Component; 11 | import org.yyx.netty.entity.MethodInvokeMeta; 12 | import org.yyx.netty.server.dispatcher.RequestDispatcher; 13 | 14 | import javax.annotation.Resource; 15 | 16 | /** 17 | * NettyServer通道适配器 18 | *

19 | * 20 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 21 | * @date 2018/7/9 - 上午9:39 22 | */ 23 | @Component 24 | @Sharable 25 | public class ServerChannelHandlerAdapter extends ChannelHandlerAdapter { 26 | /** 27 | * ServerChannelHandlerAdapter 日志控制器 28 | * Create by 叶云轩 at 2018/3/3 下午1:25 29 | * Concat at tdg_yyx@foxmail.com 30 | */ 31 | private static final Logger LOGGER = LoggerFactory.getLogger(ServerChannelHandlerAdapter.class); 32 | /** 33 | * 注入请求分排器 34 | */ 35 | @Resource 36 | private RequestDispatcher dispatcher; 37 | private int lossConnectCount = 0; 38 | 39 | @Override 40 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 41 | LOGGER.error("{} -> [连接异常] {}通道异常,异常原因:{}", this.getClass().getName(), 42 | ctx.channel().id(), cause.getMessage()); 43 | ctx.close(); 44 | } 45 | 46 | /** 47 | * 服务器接收到消息时进行进行的处理 48 | * 49 | * @param channelHandlerContext channelHandlerContext 50 | * @param msg msg 51 | */ 52 | @Override 53 | public void channelRead(ChannelHandlerContext channelHandlerContext, Object msg) { 54 | if (msg instanceof String) { 55 | if ("ping-pong-ping-pong".equals(msg)) { 56 | LOGGER.info("{} -> [心跳监测] {}:通道活跃", this.getClass().getName(), channelHandlerContext.channel().id()); 57 | // 心跳消息 58 | lossConnectCount = 0; 59 | return; 60 | } 61 | } 62 | // 转换为MethodInvokeMeta 63 | MethodInvokeMeta invokeMeta = (MethodInvokeMeta) msg; 64 | LOGGER.info("{} -> [客户端信息] \n 方法名 - > {} \n 参数列表 -> {} \n " + 65 | "返回值 -> {} ", this.getClass().getName(), invokeMeta.getMethodName(), invokeMeta.getArgs() 66 | , invokeMeta.getReturnType()); 67 | // 具体的处理类 68 | this.dispatcher.dispatcher(channelHandlerContext, invokeMeta); 69 | } 70 | 71 | /** 72 | * 触发器 73 | * 74 | * @param channelHandlerContext channelHandlerContext 75 | * @param evt 76 | * @throws Exception exception 77 | */ 78 | @Override 79 | public void userEventTriggered(ChannelHandlerContext channelHandlerContext, Object evt) throws Exception { 80 | LOGGER.info("{} -> [已经有5秒中没有接收到客户端的消息了]", this.getClass().getName()); 81 | if (evt instanceof IdleStateEvent) { 82 | IdleStateEvent idleStateEvent = (IdleStateEvent) evt; 83 | if (idleStateEvent.state() == IdleState.READER_IDLE) { 84 | lossConnectCount++; 85 | if (lossConnectCount > 2) { 86 | LOGGER.info("{} -> [释放不活跃通道] {}", this.getClass().getName(), channelHandlerContext.channel().id()); 87 | channelHandlerContext.channel().close(); 88 | } 89 | } 90 | } else { 91 | super.userEventTriggered(channelHandlerContext, evt); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /spring-boot-netty/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.yyx.netty 7 | spring-boot-netty 8 | 0.0.1-SNAPSHOT 9 | pom 10 | 11 | spring-boot-netty 12 | 13 | Netty 学习之路 14 | netty-server是基于Netty开发的一款Server端代码 15 | netty-client是基于Netty开发的一款Client端代码 16 | study-netty是学习Netty的相关Demo,记录了从0到1的整合过程等 17 | 18 | 19 | 20 | study-netty 21 | netty-commons 22 | netty-demo 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-parent 28 | 2.0.1.RELEASE 29 | 30 | 31 | 32 | UTF-8 33 | UTF-8 34 | 1.8 35 | 5.0.0.Alpha2 36 | 1.16.20 37 | 0.6.12 38 | 1.2.47 39 | 2.0.5.Final 40 | 41 | 42 | 43 | 44 | 45 | org.springframework.boot 46 | spring-boot-starter 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-test 53 | test 54 | 55 | 56 | 57 | 58 | io.netty 59 | netty-all 60 | ${netty} 61 | 62 | 63 | 64 | 65 | org.projectlombok 66 | lombok 67 | ${lombok} 68 | 69 | 70 | 71 | 72 | 73 | org.jboss.marshalling 74 | jboss-marshalling 75 | ${jboss.marshalling} 76 | 77 | 78 | 79 | 80 | org.msgpack 81 | msgpack 82 | ${msgpack} 83 | 84 | 85 | 86 | 87 | com.alibaba 88 | fastjson 89 | ${fastjson} 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-maven-plugin 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/listener/NettyServerListener.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.server.listener; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.ChannelFuture; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.ChannelOption; 7 | import io.netty.channel.ChannelPipeline; 8 | import io.netty.channel.EventLoopGroup; 9 | import io.netty.channel.nio.NioEventLoopGroup; 10 | import io.netty.channel.socket.SocketChannel; 11 | import io.netty.channel.socket.nio.NioServerSocketChannel; 12 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 13 | import io.netty.handler.codec.LengthFieldPrepender; 14 | import io.netty.handler.logging.LogLevel; 15 | import io.netty.handler.logging.LoggingHandler; 16 | import io.netty.handler.timeout.IdleStateHandler; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.springframework.stereotype.Component; 20 | import org.yyx.netty.server.adapter.ServerChannelHandlerAdapter; 21 | import org.yyx.netty.server.config.NettyServerConfig; 22 | import org.yyx.netty.util.ObjectCodec; 23 | 24 | import javax.annotation.PreDestroy; 25 | import javax.annotation.Resource; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | /** 29 | * Netty服务器监听器 30 | *

31 | * create by 叶云轩 at 2018/3/3-下午12:21 32 | * contact by tdg_yyx@foxmail.com 33 | * 34 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 35 | * @date 2018/8/15 - 12:26 36 | */ 37 | @Component 38 | public class NettyServerListener { 39 | /** 40 | * NettyServerListener 日志控制器 41 | * Create by 叶云轩 at 2018/3/3 下午12:21 42 | * Concat at tdg_yyx@foxmail.com 43 | */ 44 | private static final Logger LOGGER = LoggerFactory.getLogger(NettyServerListener.class); 45 | 46 | /** 47 | * 创建bootstrap 48 | */ 49 | private ServerBootstrap serverBootstrap = new ServerBootstrap(); 50 | /** 51 | * BOSS 52 | */ 53 | private EventLoopGroup boss = new NioEventLoopGroup(); 54 | /** 55 | * Worker 56 | */ 57 | private EventLoopGroup work = new NioEventLoopGroup(); 58 | /** 59 | * 通道适配器 60 | */ 61 | @Resource 62 | private ServerChannelHandlerAdapter channelHandlerAdapter; 63 | /** 64 | * NETT服务器配置类 65 | */ 66 | @Resource 67 | private NettyServerConfig nettyConfig; 68 | 69 | /** 70 | * 关闭服务器方法 71 | */ 72 | @PreDestroy 73 | public void close() { 74 | LOGGER.info("关闭服务器...."); 75 | //优雅退出 76 | boss.shutdownGracefully(); 77 | work.shutdownGracefully(); 78 | } 79 | 80 | /** 81 | * 开启及服务线程 82 | */ 83 | public void start() { 84 | // 从配置文件中(application.yml)获取服务端监听端口号 85 | int port = nettyConfig.getPort(); 86 | serverBootstrap.group(boss, work) 87 | .channel(NioServerSocketChannel.class) 88 | .option(ChannelOption.SO_BACKLOG, 100) 89 | .handler(new LoggingHandler(LogLevel.INFO)); 90 | try { 91 | //设置事件处理 92 | serverBootstrap.childHandler(new ChannelInitializer() { 93 | @Override 94 | protected void initChannel(SocketChannel ch) { 95 | ChannelPipeline pipeline = ch.pipeline(); 96 | // 添加心跳支持 97 | pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS)); 98 | // 基于定长的方式解决粘包/拆包问题 99 | pipeline.addLast(new LengthFieldBasedFrameDecoder(nettyConfig.getMaxFrameLength() 100 | , 0, 2, 0, 2)); 101 | pipeline.addLast(new LengthFieldPrepender(2)); 102 | // 序列化 103 | pipeline.addLast(new ObjectCodec()); 104 | pipeline.addLast(channelHandlerAdapter); 105 | } 106 | }); 107 | LOGGER.info("netty服务器在[{}]端口启动监听", port); 108 | ChannelFuture f = serverBootstrap.bind(port).sync(); 109 | f.channel().closeFuture().sync(); 110 | } catch (InterruptedException e) { 111 | LOGGER.info("[出现异常] 释放资源"); 112 | boss.shutdownGracefully(); 113 | work.shutdownGracefully(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /spring-boot-netty/netty-demo/netty-server/src/main/java/org/yyx/netty/server/dispatcher/RequestDispatcher.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.server.dispatcher; 2 | 3 | import io.netty.channel.ChannelFuture; 4 | import io.netty.channel.ChannelFutureListener; 5 | import io.netty.channel.ChannelHandlerContext; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.BeansException; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.context.ApplicationContextAware; 11 | import org.springframework.stereotype.Component; 12 | import org.yyx.netty.entity.MethodInvokeMeta; 13 | import org.yyx.netty.entity.NullWritable; 14 | import org.yyx.netty.exception.ErrorParamsException; 15 | 16 | import java.lang.reflect.InvocationTargetException; 17 | import java.lang.reflect.Method; 18 | 19 | /** 20 | * 请求分排器 21 | *

22 | * 23 | * @author 叶云轩 contact by tdg_yyx@foxmail.com 24 | * @date 2018/7/9 - 上午9:37 25 | */ 26 | @Component 27 | public class RequestDispatcher implements ApplicationContextAware { 28 | 29 | /** 30 | * RequestDispatcher 日志输出 31 | */ 32 | private static final Logger LOGGER = LoggerFactory.getLogger(RequestDispatcher.class); 33 | /** 34 | * Spring上下文 35 | */ 36 | private ApplicationContext applicationContext; 37 | 38 | /** 39 | * 发送 40 | * 41 | * @param channelHandlerContext channelHandlerContext 42 | * @param invokeMeta invokeMeta:用于记录远程调用的服务信息,即调用哪个接口中的哪个方法 43 | */ 44 | public void dispatcher(final ChannelHandlerContext channelHandlerContext, final MethodInvokeMeta invokeMeta) { 45 | ChannelFuture channelFuture = null; 46 | // 指向的接口类 47 | Class interfaceClass = invokeMeta.getInterfaceClass(); 48 | // 所调用的方法名 49 | String name = invokeMeta.getMethodName(); 50 | // 方法的参数列表 51 | Object[] args = invokeMeta.getArgs(); 52 | // 方法的参数列表按顺序对应的类型 53 | Class[] parameterTypes = invokeMeta.getParameterTypes(); 54 | // 通过Spring获取实际对象 55 | Object targetObject = this.applicationContext.getBean(interfaceClass); 56 | // 声明调用的方法对象 57 | Method method; 58 | try { 59 | // 获取调用的方法对象 60 | method = targetObject.getClass().getMethod(name, parameterTypes); 61 | } catch (NoSuchMethodException e) { 62 | // 尚未执行方法调用出现异常 63 | LOGGER.error("[获取远程方法异常] {}", e.getMessage()); 64 | // 响应给客户端 65 | channelFuture = channelHandlerContext.writeAndFlush(e); 66 | return; 67 | } finally { 68 | if (channelFuture != null) { 69 | channelFuture.addListener(ChannelFutureListener.CLOSE); 70 | } 71 | } 72 | try { 73 | // 执行方法 74 | Object result = method.invoke(targetObject, args); 75 | if (result == null) { 76 | // 方法没有返回值,返回NullWritable对象 77 | LOGGER.info("{} -> [方法没有返回值,返回NullWritable对象]", this.getClass().getName()); 78 | channelFuture = channelHandlerContext.writeAndFlush(NullWritable.nullWritable()); 79 | } else { 80 | // 将方法执行结果写入到Channel 81 | LOGGER.info("{} -> [返回结果] {}", this.getClass().getName(), result); 82 | channelFuture = channelHandlerContext.writeAndFlush(result); 83 | } 84 | /* 85 | 虽然可以通过ChannelFuture的get()方法获取异步操作的结果,但完成时间是无法预测的,若不设置超时时间则有可能导致线程长时间被阻塞; 86 | 若是不能精确的设置超时时间则可能导致I/O操作中断. 87 | 因此,Netty建议通过GenericFutureListener接口执行异步操作结束后的回调. 88 | */ 89 | /// ChannelFutureListener.CLOSE = new ChannelFutureListener() { 90 | /// @Override 91 | /// public void operationComplete(ChannelFuture future) { 92 | /// future.channel().close(); 93 | /// } 94 | /// }; 95 | } catch (Exception e) { 96 | Throwable targetException = ((InvocationTargetException) e).getTargetException(); 97 | LOGGER.error("{} -> [方法执行异常] {}", this.getClass().getName()); 98 | if (targetException instanceof ErrorParamsException) { 99 | LOGGER.error("{} -> [异常信息] {}", this.getClass().getName(), targetException.getMessage()); 100 | } 101 | channelFuture = channelHandlerContext.writeAndFlush(targetException); 102 | } finally { 103 | if (channelFuture != null) { 104 | // channelFuture.addListener(ChannelFutureListener.CLOSE); 105 | } 106 | } 107 | } 108 | 109 | @Override 110 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 111 | this.applicationContext = applicationContext; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-client/src/main/java/org/yyx/netty/study/websocket/WebSocketClientHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | 4 | import com.alibaba.fastjson.JSONObject; 5 | import io.netty.channel.Channel; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.ChannelPromise; 8 | import io.netty.channel.SimpleChannelInboundHandler; 9 | import io.netty.handler.codec.http.FullHttpResponse; 10 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 11 | import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; 12 | import io.netty.handler.codec.http.websocketx.WebSocketFrame; 13 | import io.netty.util.CharsetUtil; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | /** 18 | *

19 | * create by 叶云轩 at 2018/5/17-下午6:06 20 | * contact by tdg_yyx@foxmail.com 21 | */ 22 | public class WebSocketClientHandler extends SimpleChannelInboundHandler { 23 | /** 24 | * WebSocketClientHandler 日志控制器 25 | * Create by 叶云轩 at 2018/5/17 下午6:10 26 | * Concat at tdg_yyx@foxmail.com 27 | */ 28 | private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketClientHandler.class); 29 | 30 | private final WebSocketClientHandshaker webSocketClientHandshaker; 31 | private ChannelPromise handshakeFuture; 32 | 33 | WebSocketClientHandler(WebSocketClientHandshaker webSocketClientHandshaker) { 34 | this.webSocketClientHandshaker = webSocketClientHandshaker; 35 | } 36 | 37 | @Override 38 | public void handlerAdded(ChannelHandlerContext ctx) { 39 | handshakeFuture = ctx.newPromise(); 40 | } 41 | 42 | /** 43 | * 异常 44 | * 45 | * @param channelHandlerContext channelHandlerContext 46 | * @param cause 异常 47 | */ 48 | @Override 49 | public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception { 50 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 51 | "\t├ [exception]: {}\n" + 52 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", cause.getMessage()); 53 | channelHandlerContext.close(); 54 | } 55 | 56 | /** 57 | * 当客户端主动链接服务端的链接后,调用此方法 58 | * 59 | * @param channelHandlerContext ChannelHandlerContext 60 | */ 61 | @Override 62 | public void channelActive(ChannelHandlerContext channelHandlerContext) throws Exception { 63 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 64 | "\t├ [建立连接]\n" + 65 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓"); 66 | 67 | Channel channel = channelHandlerContext.channel(); 68 | // 握手 69 | webSocketClientHandshaker.handshake(channel); 70 | } 71 | 72 | /** 73 | * 与服务端断开连接时 74 | * 75 | * @param channelHandlerContext channelHandlerContext 76 | */ 77 | @Override 78 | public void channelInactive(ChannelHandlerContext channelHandlerContext) { 79 | Channel channel = channelHandlerContext.channel(); 80 | WebSocketUsers.remove(channel); 81 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 82 | "\t├ [断开连接]:client [{}]\n" + 83 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", channel.remoteAddress()); 84 | 85 | } 86 | 87 | /** 88 | * 读完之后调用的方法 89 | * 90 | * @param channelHandlerContext ChannelHandlerContext 91 | */ 92 | @Override 93 | public void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception { 94 | channelHandlerContext.flush(); 95 | } 96 | 97 | @Override 98 | protected void messageReceived(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception { 99 | // 获取通道 100 | Channel channel = channelHandlerContext.channel(); 101 | // 如果没有握手完成进行握手 102 | if (!webSocketClientHandshaker.isHandshakeComplete()) { 103 | webSocketClientHandshaker.finishHandshake(channel, (FullHttpResponse) msg); 104 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 105 | "\t├ [握手成功]\n" + 106 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓"); 107 | handshakeFuture.setSuccess(); 108 | // 将当前登陆用户保存起来 109 | WebSocketUsers.put("client1-" + getUserNameInPath(), channel); 110 | return; 111 | } 112 | channelHandlerContext.flush(); 113 | 114 | if (msg instanceof FullHttpResponse) { 115 | FullHttpResponse response = (FullHttpResponse) msg; 116 | throw new IllegalStateException( 117 | "Unexpected FullHttpResponse (getStatus=" + response.status() + 118 | ", content=" + response.content().toString(CharsetUtil.UTF_8) + ')'); 119 | } 120 | 121 | WebSocketFrame frame = (WebSocketFrame) msg; 122 | 123 | if (frame instanceof TextWebSocketFrame) { 124 | TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) frame; 125 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 126 | "\t├ [服务器响应消息]: {}\n" + 127 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", textWebSocketFrame.text()); 128 | WebSocketMessage webSocketMessage = new WebSocketMessage(); 129 | webSocketMessage.setHeader(WebSocketMessage.Type.send_user); 130 | webSocketMessage.setAccept("yyx"); 131 | webSocketMessage.setContent("Hello I'm YeYunXuan"); 132 | String string = JSONObject.toJSONString(webSocketMessage); 133 | WebSocketUsers.sendMessageToUser("client1-YeYunXuan", string); 134 | } 135 | } 136 | 137 | /** 138 | * 获取登陆用户 139 | * 140 | * @return 用户名 141 | */ 142 | private String getUserNameInPath() { 143 | String path = webSocketClientHandshaker.uri().getPath(); 144 | int i = path.lastIndexOf("/"); 145 | return path.substring(i + 1, path.length()); 146 | } 147 | } 148 | 149 | -------------------------------------------------------------------------------- /spring-boot-netty/study-netty/study-server/src/main/java/org/yyx/netty/study/websocket/WebSocketServerHandler.java: -------------------------------------------------------------------------------- 1 | package org.yyx.netty.study.websocket; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.channel.Channel; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.channel.SimpleChannelInboundHandler; 8 | import io.netty.handler.codec.http.FullHttpRequest; 9 | import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame; 10 | import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame; 11 | import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; 12 | import io.netty.handler.codec.http.websocketx.PongWebSocketFrame; 13 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 14 | import io.netty.handler.codec.http.websocketx.WebSocketFrame; 15 | import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; 16 | import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; 17 | import org.msgpack.MessagePack; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | 21 | import java.io.IOException; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.concurrent.ConcurrentMap; 25 | 26 | /** 27 | *

28 | * create by 叶云轩 at 2018/5/11-上午11:49 29 | * contact by tdg_yyx@foxmail.com 30 | */ 31 | public class WebSocketServerHandler extends SimpleChannelInboundHandler { 32 | /** 33 | * WebSocketServerHandler 日志控制器 34 | * Create by 叶云轩 at 2018/5/11 上午11:50 35 | * Concat at tdg_yyx@foxmail.com 36 | */ 37 | private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServerHandler.class); 38 | 39 | private WebSocketServerHandshaker socketServerHandShaker; 40 | 41 | /** 42 | * 异常 43 | * 44 | * @param channelHandlerContext channelHandlerContext 45 | * @param cause 异常 46 | */ 47 | @Override 48 | public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause) throws Exception { 49 | channelHandlerContext.close(); 50 | } 51 | 52 | /** 53 | * 当客户端主动链接服务端的链接后,调用此方法 54 | * 55 | * @param channelHandlerContext ChannelHandlerContext 56 | */ 57 | @Override 58 | public void channelActive(ChannelHandlerContext channelHandlerContext) { 59 | // 使用一个结构存储通道 60 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 61 | "\t├ [建立连接]: client [{}]\n" + 62 | "\t├ [当前在线人数]: {}\n" + 63 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", channelHandlerContext.channel().remoteAddress() 64 | , WebSocketUsers.getUSERS().size() + 1); 65 | } 66 | 67 | /** 68 | * 与客户端断开连接时 69 | * 70 | * @param channelHandlerContext channelHandlerContext 71 | */ 72 | @Override 73 | public void channelInactive(ChannelHandlerContext channelHandlerContext) { 74 | Channel channel = channelHandlerContext.channel(); 75 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 76 | "\t├ [断开连接]:client [{}]\n" + 77 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", channel.remoteAddress()); 78 | WebSocketUsers.remove(channel); 79 | ConcurrentMap users = WebSocketUsers.getUSERS(); 80 | LOGGER.info("\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓"); 81 | for (String s : users.keySet()) { 82 | LOGGER.info( 83 | "\t├ [当前在线]: {}", s); 84 | } 85 | LOGGER.info("\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓"); 86 | } 87 | 88 | /** 89 | * 读完之后调用的方法 90 | * 91 | * @param channelHandlerContext ChannelHandlerContext 92 | */ 93 | @Override 94 | public void channelReadComplete(ChannelHandlerContext channelHandlerContext) throws Exception { 95 | channelHandlerContext.flush(); 96 | } 97 | 98 | /** 99 | * 接收客户端发送的消息 100 | * 101 | * @param ctx ChannelHandlerContext 102 | * @param msg 消息 103 | */ 104 | @Override 105 | protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { 106 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 107 | "\t├ [收到客户端消息类型]: {} - {}\n" + 108 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", msg.getClass(), msg.toString()); 109 | // 传统http接入 第一次需要使用http建立握手 110 | if (msg instanceof FullHttpRequest) { 111 | handlerHttpRequest(ctx, (FullHttpRequest) msg); 112 | ctx.channel().write(new TextWebSocketFrame("连接成功")); 113 | } 114 | // WebSocket接入 115 | else if (msg instanceof WebSocketFrame) { 116 | handlerWebSocketFrame(ctx, (WebSocketFrame) msg); 117 | } 118 | 119 | } 120 | 121 | /** 122 | * 第一次握手 123 | * 124 | * @param channelHandlerContext channelHandlerContext 125 | * @param req 请求 126 | */ 127 | private void handlerHttpRequest(ChannelHandlerContext channelHandlerContext, FullHttpRequest req) { 128 | 129 | // 构造握手响应返回,本机测试 130 | WebSocketServerHandshakerFactory wsFactory 131 | = new WebSocketServerHandshakerFactory("ws://localhost:9999/websocket/{uri}", 132 | null, false); 133 | String uri = req.uri(); 134 | String[] split = uri.split("/"); 135 | String userName = split[2]; 136 | // 加入在线用户 137 | WebSocketUsers.put(userName, channelHandlerContext.channel()); 138 | socketServerHandShaker = wsFactory.newHandshaker(req); 139 | if (socketServerHandShaker == null) { 140 | WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(channelHandlerContext.channel()); 141 | } else { 142 | socketServerHandShaker.handshake(channelHandlerContext.channel(), req); 143 | } 144 | } 145 | 146 | /** 147 | * webSocket处理逻辑 148 | * 149 | * @param channelHandlerContext channelHandlerContext 150 | * @param frame webSocketFrame 151 | */ 152 | private void handlerWebSocketFrame(ChannelHandlerContext channelHandlerContext, WebSocketFrame frame) throws IOException { 153 | Channel channel = channelHandlerContext.channel(); 154 | // region 判断是否是关闭链路的指令 155 | if (frame instanceof CloseWebSocketFrame) { 156 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 157 | "\t├ [关闭与客户端的链接]\n" + 158 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓"); 159 | socketServerHandShaker.close(channel, (CloseWebSocketFrame) frame.retain()); 160 | return; 161 | } 162 | // endregion 163 | // region 判断是否是ping消息 164 | if (frame instanceof PingWebSocketFrame) { 165 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 166 | "\t├ [Ping消息]\n" + 167 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓"); 168 | channel.write(new PongWebSocketFrame(frame.content().retain())); 169 | return; 170 | } 171 | // endregion 172 | if (frame instanceof TextWebSocketFrame) { 173 | String text = ((TextWebSocketFrame) frame).text(); 174 | WebSocketMessage webSocketMessage = JSONObject.parseObject(text, WebSocketMessage.class); 175 | String accept = webSocketMessage.getAccept(); 176 | WebSocketUsers.sendMessageToUser(accept, webSocketMessage.getContent()); 177 | } 178 | if (frame instanceof BinaryWebSocketFrame) { 179 | BinaryWebSocketFrame binaryWebSocketFrame = (BinaryWebSocketFrame) frame; 180 | ByteBuf content = binaryWebSocketFrame.content(); 181 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 182 | "\t├ [二进制数据]:{}\n" + 183 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", content); 184 | final int length = content.readableBytes(); 185 | final byte[] array = new byte[length]; 186 | content.getBytes(content.readerIndex(), array, 0, length); 187 | MessagePack messagePack = new MessagePack(); 188 | List list = new ArrayList<>(); 189 | list.add(messagePack.read(array)); 190 | for (Object o : list) { 191 | LOGGER.info("\n\t⌜⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓\n" + 192 | "\t├ [解码数据]: {}\n" + 193 | "\t⌞⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓⎓", o); 194 | } 195 | 196 | } 197 | // 非文本消息处理方式 198 | if (!(frame instanceof TextWebSocketFrame)) { 199 | throw new UnsupportedOperationException(String.format( 200 | "%s 暂不支持非文本消息", frame.getClass().getName() 201 | )); 202 | } 203 | } 204 | } 205 | --------------------------------------------------------------------------------