├── .gitignore
├── README.md
├── codenow-im-chat
└── pom.xml
├── codenow-rpc
└── pom.xml
├── java-network-programming
├── README.md
└── pom.xml
├── netty-boot
├── dependency-reduced-pom.xml
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── fun
│ │ └── codenow
│ │ └── netty
│ │ ├── NettyBootServer.java
│ │ ├── handler
│ │ ├── ApplicationLoggingHandler.java
│ │ ├── HeartBeatResponseHandler.java
│ │ └── ServerChannelInitializer.java
│ │ └── message
│ │ └── CustomMessageProto.java
│ └── resources
│ └── log4j.properties
├── netty-common
├── pom.xml
└── src
│ └── main
│ └── java
│ └── fun
│ └── codenow
│ └── netty
│ └── common
│ ├── CustomMessageProto.java
│ └── codec
│ ├── CustomDecode.java
│ └── CustomEncode.java
├── netty-demo
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── fun
│ │ └── codenow
│ │ └── netty
│ │ └── socket
│ │ ├── NettySocketApplication.java
│ │ ├── coder
│ │ └── protobuf
│ │ │ ├── ClientOne.java
│ │ │ └── ServerOne.java
│ │ ├── demo
│ │ ├── EchoClient.java
│ │ ├── EchoClientHandler.java
│ │ ├── EchoServer.java
│ │ ├── EchoServerHandler.java
│ │ └── WebSocketServer.java
│ │ ├── heart
│ │ ├── CustomHeartBeatClientHandler.java
│ │ ├── CustomHeartBeatInitializer.java
│ │ ├── CustomHeartBeathandler.java
│ │ ├── CustomHeartbeatClientInitializer.java
│ │ ├── HeartbeatClient.java
│ │ └── HeartbeatServer.java
│ │ ├── heartbeat
│ │ ├── CustomProtocol.java
│ │ ├── HeartBeatServer.java
│ │ ├── HeartBeatSimpleHandle.java
│ │ ├── HeartbeatDecoder.java
│ │ ├── HeartbeatEncode.java
│ │ ├── HeartbeatInitializer.java
│ │ ├── NettySocketHolder.java
│ │ ├── client
│ │ │ ├── CustomerHandleInitializer.java
│ │ │ ├── EchoClientHandle.java
│ │ │ └── HeartbeatClient.java
│ │ └── util
│ │ │ └── SpringBeanFactory.java
│ │ ├── inaction
│ │ ├── Client.java
│ │ ├── ClientHandler.java
│ │ ├── Server.java
│ │ └── ServerHandler.java
│ │ ├── protobuf
│ │ ├── SubscribeReqProto.java
│ │ └── SubscribeRespProto.java
│ │ ├── protocol
│ │ ├── custom
│ │ │ └── CustomProtocolServer.java
│ │ ├── http
│ │ │ └── HttpNettyServer.java
│ │ └── websocket
│ │ │ └── WebSocketNettyServer.java
│ │ ├── subscribebook
│ │ ├── SubscribeBookClient.java
│ │ ├── SubscribeBookClientHandler.java
│ │ ├── SubscribeBookServer.java
│ │ └── SubscribeBookServerHandler.java
│ │ ├── test
│ │ ├── EchoClientTest.java
│ │ └── EchoServerTest.java
│ │ ├── test2
│ │ ├── EchoClientTest2.java
│ │ ├── EchoClientTest2ChannelHandler.java
│ │ ├── EchoServer2ChannelHanlder.java
│ │ └── EchoServerTest2.java
│ │ ├── timeserver
│ │ ├── TimeClient.java
│ │ ├── TimeClientHandler.java
│ │ ├── TimeServer.java
│ │ ├── TimeServerHandler.java
│ │ └── TimeServerInitializer.java
│ │ └── uptime
│ │ ├── UptimeClient.java
│ │ ├── UptimeClientHandler.java
│ │ ├── UptimeServer.java
│ │ └── UptimeServerHandler.java
│ └── resources
│ └── application.yml
├── netty-file-system
└── pom.xml
├── netty-heartbeat
├── heartbeat-client
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── fun
│ │ │ └── codenow
│ │ │ └── netty
│ │ │ └── heartbeat
│ │ │ └── client
│ │ │ ├── HeartBeatClientApplication.java
│ │ │ ├── client
│ │ │ ├── Client.java
│ │ │ └── CustomClientChannelInitializer.java
│ │ │ └── handler
│ │ │ └── HeartBeatClientHandler.java
│ │ └── resources
│ │ └── application.yml
├── heartbeat-server
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── fun
│ │ │ └── codenow
│ │ │ └── netty
│ │ │ └── heartbeat
│ │ │ └── server
│ │ │ ├── HeartBeatServerApplication.java
│ │ │ ├── handler
│ │ │ └── HeartBeatServerHandler.java
│ │ │ └── server
│ │ │ ├── CustomServerChannelInitializer.java
│ │ │ └── Server.java
│ │ └── resources
│ │ └── application.yml
└── pom.xml
├── netty-mqtt
├── pom.xml
└── src
│ └── main
│ └── java
│ └── fun
│ └── codenow
│ └── mqtt
│ ├── ChannelClosedException.java
│ ├── MqttChannelHandler.java
│ ├── MqttClient.java
│ ├── MqttClientCallback.java
│ ├── MqttClientConfig.java
│ ├── MqttClientImpl.java
│ ├── MqttConnectResult.java
│ ├── MqttHandler.java
│ ├── MqttIncomingQos2Publish.java
│ ├── MqttLastWill.java
│ ├── MqttPendingPublish.java
│ ├── MqttPendingSubscription.java
│ ├── MqttPendingUnsubscription.java
│ ├── MqttPingHandler.java
│ ├── MqttSubscription.java
│ └── RetransmissionHandler.java
├── netty-private-protocol
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── fun
│ │ └── codenow
│ │ └── netty
│ │ └── privateprotocol
│ │ ├── NettyPrivateProtocolApplication.java
│ │ ├── client
│ │ ├── ClientChannelInitializer.java
│ │ ├── HeartBeatRequestHandler.java
│ │ └── privateProtocoClient.java
│ │ ├── codec
│ │ ├── CustomDecode.java
│ │ └── CustomEncode.java
│ │ ├── protobuf
│ │ └── CustomMessageProto.java
│ │ ├── server
│ │ ├── HeartBeatResponseHandler.java
│ │ ├── PrivateProtocolServer.java
│ │ └── ServerChannelInitializer.java
│ │ └── struct
│ │ └── MessageType.java
│ └── resources
│ └── application.yml
├── netty-websocket
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── fun
│ │ └── codenow
│ │ └── netty
│ │ └── websocket
│ │ └── server
│ │ ├── ServerChannelInitializer.java
│ │ ├── ServerHandler.java
│ │ ├── WebSocketServer.java
│ │ └── WebSocketServerHandler.java
│ └── resources
│ └── templates
│ └── WebSocketServer.html
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # maven #
2 | target
3 |
4 | logs
5 |
6 | # windows #
7 | Thumbs.db
8 |
9 | # Mac #
10 | .DS_Store
11 |
12 | # eclipse #
13 | .settings
14 | .project
15 | .classpath
16 | .log
17 | **.log
18 | *.class
19 |
20 | # idea #
21 | .idea
22 | *.iml
23 |
24 | # Package Files #
25 | *.jar
26 | *.war
27 | *.ear
28 | /target
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # codenow-netty
2 |
3 | Netty是一个基于Java NIO的网络编程框架,提供了一套高效的、事件驱动的异步网络通信机制。简化了网络应用程序的开发过程,提供了可靠的、高性能的网络传输。
4 |
5 | 作为高性能的网络通信框架,Netty被广泛应用于各种开源组件中,比如Dubbo、RocketMQ、Elasticsearch、gRPC、Kafka、Hadoop……
6 |
7 |
8 | 本项目主要是为了更深入的理解netty、rpc等框架的实现原理,基于Netty实现
9 |
10 | - [x] Echo服务器
11 |
12 | - [x] 心跳检测
13 |
14 | - [x] 自定义编解码机制
15 |
16 | - [x] 断连重连
17 |
18 | - [x] 握手和安全认证
19 |
20 | - [x] 私有协议栈开发
21 |
22 | - [ ] 通信聊天系统
23 |
24 | - [ ] WebSocket服务器
25 |
26 | - [ ] 文件服务器
27 |
28 | - [ ] 手写RPC框架
--------------------------------------------------------------------------------
/codenow-im-chat/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | codenow-netty
7 | fun.codenow
8 | 1.0.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | codenow-im-chat
13 |
14 |
15 |
--------------------------------------------------------------------------------
/codenow-rpc/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | codenow-netty
7 | fun.codenow
8 | 1.0.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | codenow-rpc
13 |
14 |
15 |
--------------------------------------------------------------------------------
/java-network-programming/README.md:
--------------------------------------------------------------------------------
1 | Java 网络编程 基础
2 |
3 | Java原生的网络类库
--------------------------------------------------------------------------------
/java-network-programming/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | codenow-netty
7 | fun.codenow
8 | 1.0.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | java-network-programming
13 |
14 |
15 |
--------------------------------------------------------------------------------
/netty-boot/dependency-reduced-pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | fun.codenow.netty
5 | netty-boot
6 | 1.0.2
7 |
8 |
9 |
10 | maven-shade-plugin
11 | 3.2.4
12 |
13 |
14 | package
15 |
16 | shade
17 |
18 |
19 |
20 |
21 | fun.codenow.netty.NettyBootServer
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | UTF-8
32 | 1.8
33 | UTF-8
34 | 1.8
35 |
36 |
37 |
--------------------------------------------------------------------------------
/netty-boot/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | fun.codenow.netty
8 | netty-boot
9 | 1.0.2
10 | jar
11 |
12 |
13 | UTF-8
14 | UTF-8
15 |
16 | 1.8
17 | 1.8
18 |
19 |
20 |
21 | io.netty
22 | netty-all
23 | 4.1.54.Final
24 |
25 |
26 |
27 | com.google.protobuf
28 | protobuf-java
29 | 3.14.0
30 |
31 |
32 |
33 | com.google.code.gson
34 | gson
35 | 2.8.6
36 |
37 |
38 | org.slf4j
39 | slf4j-api
40 | 2.0.0-alpha1
41 |
42 |
43 |
44 | org.slf4j
45 | slf4j-simple
46 | 2.0.0-alpha1
47 |
48 |
49 |
50 | org.projectlombok
51 | lombok
52 | 1.18.20
53 |
54 |
55 |
56 |
57 |
58 |
59 | org.apache.maven.plugins
60 | maven-shade-plugin
61 | 3.2.4
62 |
63 |
64 | package
65 |
66 | shade
67 |
68 |
69 |
70 |
71 | fun.codenow.netty.NettyBootServer
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/netty-boot/src/main/java/fun/codenow/netty/NettyBootServer.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty;
2 |
3 | import fun.codenow.netty.handler.ApplicationLoggingHandler;
4 | import fun.codenow.netty.handler.ServerChannelInitializer;
5 | import io.netty.bootstrap.ServerBootstrap;
6 | import io.netty.channel.ChannelFuture;
7 | import io.netty.channel.EventLoopGroup;
8 | import io.netty.channel.nio.NioEventLoopGroup;
9 | import io.netty.channel.socket.nio.NioServerSocketChannel;
10 | import io.netty.handler.logging.LogLevel;
11 |
12 | import java.net.InetSocketAddress;
13 |
14 | /**
15 | * @Author Jack Wu
16 | * @Description
17 | * @Version V1.0
18 | * @Date2021/6/30 11:03
19 | **/
20 | public class NettyBootServer {
21 | public static void main(String[] args) throws InterruptedException {
22 | EventLoopGroup bossGroup=new NioEventLoopGroup();
23 | EventLoopGroup workerGroup=new NioEventLoopGroup();
24 | ServerBootstrap serverBootstrap=new ServerBootstrap();
25 | serverBootstrap
26 | .group(bossGroup,workerGroup)
27 | .channel(NioServerSocketChannel.class)
28 | .localAddress(new InetSocketAddress(8888))
29 | .handler(new ApplicationLoggingHandler(LogLevel.INFO))
30 | .childHandler(new ServerChannelInitializer());
31 | //.option()
32 | try {
33 | ChannelFuture channelFuture= serverBootstrap.bind().sync();
34 | channelFuture.channel().closeFuture().sync();
35 | } finally {
36 | bossGroup.shutdownGracefully().sync();
37 | workerGroup.shutdownGracefully().sync();
38 | }
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/netty-boot/src/main/java/fun/codenow/netty/handler/HeartBeatResponseHandler.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.handler;
2 |
3 | import com.google.gson.Gson;
4 | import fun.codenow.netty.message.CustomMessageProto;
5 | import io.netty.buffer.ByteBuf;
6 | import io.netty.channel.ChannelHandlerContext;
7 | import io.netty.channel.ChannelInboundHandlerAdapter;
8 | import io.netty.handler.timeout.IdleState;
9 | import io.netty.handler.timeout.IdleStateEvent;
10 | import lombok.extern.slf4j.Slf4j;
11 |
12 | /**
13 | * @Author Jack Wu
14 | * @Description
15 | * @Version V1.0
16 | * @Date2021/6/30 15:52
17 | **/
18 | @Slf4j
19 | public class HeartBeatResponseHandler extends ChannelInboundHandlerAdapter {
20 | @Override
21 | public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
22 | super.channelRegistered(ctx);
23 | }
24 |
25 | @Override
26 | public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
27 | super.channelUnregistered(ctx);
28 | }
29 |
30 | @Override
31 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
32 | super.channelActive(ctx);
33 | }
34 |
35 | @Override
36 | public void channelInactive(ChannelHandlerContext ctx) throws Exception {
37 | super.channelInactive(ctx);
38 | }
39 |
40 | @Override
41 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
42 | CustomMessageProto.CustomMessage message= (CustomMessageProto.CustomMessage) msg;
43 | if (message.getHeader().getType().getNumber()== CustomMessageProto.CustomMessage.CustomHeader.MessgeType.PING_VALUE){
44 | log.info("接收心跳消息:{}"+message);
45 | CustomMessageProto.CustomMessage.Builder heartBeatRespMsg=CustomMessageProto.CustomMessage.newBuilder()
46 | .setHeader(
47 | CustomMessageProto.CustomMessage.CustomHeader.newBuilder()
48 | .setTypeValue(0xABEF)
49 | .setType(CustomMessageProto.CustomMessage.CustomHeader.MessgeType.PONG)
50 | );
51 | ctx.channel().writeAndFlush(heartBeatRespMsg);
52 | }else {
53 | log.info("消息未处理:{}"+message);
54 | }
55 |
56 | }
57 |
58 | @Override
59 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
60 | super.channelReadComplete(ctx);
61 | }
62 |
63 | @Override
64 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
65 | //超过40s没接收心跳消息则强制断开连接,从列表移除
66 | if (evt instanceof IdleStateEvent){
67 | IdleStateEvent event= (IdleStateEvent) evt;
68 | if (event.state().equals(IdleState.READER_IDLE)){
69 | //判断上一次心跳时间,如果超过40s则直接断开连接
70 | //否则尝试发送心跳
71 | log.info("已经5秒没有收到客户端心跳消息了,发送心跳检测给客户端");
72 | CustomMessageProto.CustomMessage.Builder heartBeatRespMsg=CustomMessageProto.CustomMessage.newBuilder()
73 | .setHeader(
74 | CustomMessageProto.CustomMessage.CustomHeader.newBuilder()
75 | .setTypeValue(0xABEF)
76 | .setType(CustomMessageProto.CustomMessage.CustomHeader.MessgeType.PONG)
77 | );
78 | ctx.channel().writeAndFlush(heartBeatRespMsg);
79 | }
80 | }
81 | super.userEventTriggered(ctx, evt);
82 | }
83 |
84 | @Override
85 | public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
86 | super.channelWritabilityChanged(ctx);
87 | }
88 |
89 | @Override
90 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
91 | log.error("心跳检测发生异常:");
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/netty-boot/src/main/java/fun/codenow/netty/handler/ServerChannelInitializer.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.handler;
2 |
3 | import fun.codenow.netty.message.CustomMessageProto;
4 | import io.netty.channel.ChannelInitializer;
5 | import io.netty.channel.socket.SocketChannel;
6 | import io.netty.handler.codec.protobuf.ProtobufDecoder;
7 | import io.netty.handler.codec.protobuf.ProtobufEncoder;
8 | import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
9 | import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
10 | import io.netty.handler.timeout.IdleStateHandler;
11 |
12 | import java.util.concurrent.TimeUnit;
13 |
14 | /**
15 | * @Author Jack Wu
16 | * @Description
17 | * @Version V1.0
18 | * @Date2021/6/30 15:51
19 | **/
20 | public class ServerChannelInitializer extends ChannelInitializer {
21 | @Override
22 | protected void initChannel(SocketChannel socketChannel) throws Exception {
23 | socketChannel.pipeline()
24 | .addLast(new IdleStateHandler(10,0,0, TimeUnit.SECONDS))
25 |
26 | .addLast(new ProtobufVarint32FrameDecoder())
27 | .addLast(new ProtobufDecoder(CustomMessageProto.CustomMessage.getDefaultInstance()))
28 | .addLast(new ProtobufVarint32LengthFieldPrepender())
29 | .addLast(new ProtobufEncoder())
30 | .addLast(new HeartBeatResponseHandler());
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/netty-boot/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # Set root logger level to DEBUG and its only appender to A1.
2 | log4j.rootLogger=DEBUG, A1
3 |
4 | # A1 is set to be a ConsoleAppender.
5 | log4j.appender.A1=org.apache.log4j.ConsoleAppender
6 |
7 | # A1 uses PatternLayout.
8 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout
9 | log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
10 |
--------------------------------------------------------------------------------
/netty-common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | codenow-netty
7 | fun.codenow
8 | 1.0.0-SNAPSHOT
9 |
10 | 4.0.0
11 | jar
12 |
13 | netty-common
14 |
15 |
16 |
17 | com.google.protobuf
18 | protobuf-java
19 |
20 |
21 |
--------------------------------------------------------------------------------
/netty-common/src/main/java/fun/codenow/netty/common/codec/CustomDecode.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.common.codec;
2 |
3 | /**
4 | * @Author Jack Wu
5 | * @Description
6 | * @Version V1.0
7 | * @Date2020/12/11 15:19
8 | **/
9 | public class CustomDecode {
10 | }
11 |
--------------------------------------------------------------------------------
/netty-common/src/main/java/fun/codenow/netty/common/codec/CustomEncode.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.common.codec;
2 |
3 | /**
4 | * @Author Jack Wu
5 | * @Description
6 | * @Version V1.0
7 | * @Date2020/12/11 15:19
8 | **/
9 | public class CustomEncode {
10 | }
11 |
--------------------------------------------------------------------------------
/netty-demo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | codenow-netty
7 | fun.codenow
8 | 1.0.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | netty-demo
13 |
14 |
15 |
16 | org.springframework.boot
17 | spring-boot-starter-web
18 |
19 |
20 | org.springframework.boot
21 | spring-boot-starter-tomcat
22 |
23 |
24 |
25 |
26 |
27 | io.netty
28 | netty-all
29 |
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-jetty
34 |
35 |
36 |
37 | org.projectlombok
38 | lombok
39 |
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-websocket
44 |
45 |
46 |
47 | com.google.code.gson
48 | gson
49 | 2.8.5
50 |
51 |
52 |
53 | com.google.protobuf
54 | protobuf-java
55 |
56 |
57 |
58 |
59 |
60 |
61 | org.springframework.boot
62 | spring-boot-maven-plugin
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/NettySocketApplication.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * @Author Jack Wu
8 | * @Description
9 | * @Version V1.0
10 | * @Date2020/11/24 18:03
11 | **/
12 | @SpringBootApplication
13 | public class NettySocketApplication {
14 | public static void main(String[] args) {
15 | SpringApplication.run(NettySocketApplication.class);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/coder/protobuf/ClientOne.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.coder.protobuf;
2 |
3 | /**
4 | * @Author Jack Wu
5 | * @Description
6 | * @Version V1.0
7 | * @Date2020/12/1 11:50
8 | **/
9 | public class ClientOne {
10 | }
11 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/coder/protobuf/ServerOne.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.coder.protobuf;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.Channel;
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.nio.NioServerSocketChannel;
9 |
10 | import java.net.InetSocketAddress;
11 |
12 | /**
13 | * @Author Jack Wu
14 | * @Description
15 | * @Version V1.0
16 | * @Date2020/12/1 11:50
17 | **/
18 | public class ServerOne {
19 | public static void main(String[] args) {
20 | EventLoopGroup bossEventGroup=new NioEventLoopGroup();
21 | EventLoopGroup workerEventGroup=new NioEventLoopGroup();
22 |
23 | ServerBootstrap serverBootstrap=new ServerBootstrap();
24 | serverBootstrap
25 | .group(bossEventGroup,workerEventGroup)
26 | .channel(NioServerSocketChannel.class)
27 | .localAddress(new InetSocketAddress(8686))
28 | .childHandler(new ChannelInitializer() {
29 | @Override
30 | protected void initChannel(Channel channel) throws Exception {
31 |
32 | }
33 | });
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/demo/EchoClient.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.demo;
2 |
3 | import io.netty.bootstrap.Bootstrap;
4 | import io.netty.channel.Channel;
5 | import io.netty.channel.ChannelFuture;
6 | import io.netty.channel.ChannelInitializer;
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 | import java.net.InetSocketAddress;
13 |
14 | /**
15 | * @Author Jack Wu
16 | * @Description
17 | *
18 | * 创建一个 Bootstrap 来初始化客户端
19 | * 一个 NioEventLoopGroup 实例被分配给处理该事件的处理,这包括创建新的连接和处理入站和出站数据
20 | * 创建一个 InetSocketAddress 以连接到服务器
21 | * 连接好服务器之时,将安装一个 EchoClientHandler 在 pipeline
22 | * 之后 Bootstrap.connect()被调用连接到远程的 - 本例就是 echo(回声)服务器。
23 | *
24 | * @Version V1.0
25 | * @Date2020/11/25 16:43
26 | **/
27 | public class EchoClient {
28 | public static void main(String[] args) {
29 | EventLoopGroup group=new NioEventLoopGroup();
30 | Bootstrap bootstrap=new Bootstrap();
31 |
32 | bootstrap
33 | .group(group)
34 | .channel(NioSocketChannel.class)
35 | .remoteAddress( new InetSocketAddress(8999))
36 | .handler(new ChannelInitializer() { //5
37 | @Override
38 | public void initChannel(SocketChannel ch)
39 | throws Exception {
40 | ch.pipeline().addLast(
41 | new EchoClientHandler());
42 | }
43 | });
44 | try {
45 | ChannelFuture channelFuture=bootstrap.connect().sync();
46 | channelFuture.channel().closeFuture().sync();
47 | } catch (InterruptedException e) {
48 | e.printStackTrace();
49 | }finally {
50 | try {
51 | group.shutdownGracefully().sync();
52 | } catch (InterruptedException e) {
53 | e.printStackTrace();
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/demo/EchoClientHandler.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.demo;
2 |
3 | import io.netty.buffer.ByteBuf;
4 | import io.netty.buffer.Unpooled;
5 | import io.netty.channel.ChannelHandlerContext;
6 | import io.netty.channel.SimpleChannelInboundHandler;
7 | import io.netty.util.CharsetUtil;
8 |
9 | /**
10 | * @Author Jack Wu
11 | * @Description
12 | *
13 | * 客户端的工作内容:
14 | * 连接服务器
15 | * 发送信息
16 | * 发送的每个信息,等待和接收从服务器返回的同样的信息
17 | * 关闭连接
18 | *
19 | * 用 ChannelHandler 实现客户端逻辑
20 | * 跟写服务器一样,我们提供 ChannelInboundHandler 来处理数据。下面例子,我们用 SimpleChannelInboundHandler 来处理所有的任务,需要覆盖三个方法:
21 | *
22 | * channelActive() - 服务器的连接被建立后调用
23 | * channelRead0() - 数据后从服务器接收到调用
24 | * exceptionCaught() - 捕获一个异常时调用
25 | *
26 | *
27 | * @Version V1.0
28 | * @Date2020/11/25 17:11
29 | **/
30 | public class EchoClientHandler extends SimpleChannelInboundHandler {
31 | @Override
32 | public void channelActive(ChannelHandlerContext ctx) {
33 | ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",
34 | CharsetUtil.UTF_8));
35 | }
36 |
37 | @Override
38 | public void channelRead0(ChannelHandlerContext ctx,
39 | ByteBuf in) {
40 | System.out.println("Client received: " + in.toString(CharsetUtil.UTF_8));
41 | }
42 |
43 | @Override
44 | public void exceptionCaught(ChannelHandlerContext ctx,
45 | Throwable cause) {
46 | cause.printStackTrace();
47 | ctx.close();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/demo/EchoServer.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.demo;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
4 | import io.netty.channel.ChannelFuture;
5 | import io.netty.channel.ChannelInitializer;
6 | import io.netty.channel.nio.NioEventLoopGroup;
7 | import io.netty.channel.socket.SocketChannel;
8 | import io.netty.channel.socket.nio.NioServerSocketChannel;
9 |
10 | import java.net.InetSocketAddress;
11 |
12 | /**
13 | * @Author Jack Wu
14 | * @Description
15 | *
16 | * 1.设置端口值(抛出一个 NumberFormatException 如果该端口参数的格式不正确)
17 | *
18 | * 2.呼叫服务器的 start() 方法
19 | *
20 | * 3.创建 EventLoopGroup
21 | *
22 | * 4.创建 ServerBootstrap
23 | *
24 | * 5.指定使用 NIO 的传输 Channel
25 | *
26 | * 6.设置 socket 地址使用所选的端口
27 | *
28 | * 7.添加 EchoServerHandler 到 Channel 的 ChannelPipeline
29 | *
30 | * 8.绑定的服务器;sync 等待服务器关闭
31 | *
32 | * 9.关闭 channel 和 块,直到它被关闭
33 | *
34 | * 10.关机的 EventLoopGroup,释放所有资源。
35 | *
36 | * @Version V1.0
37 | * @Date2020/11/25 16:58
38 | **/
39 | public class EchoServer {
40 | public static void main(String[] args) throws InterruptedException {
41 | NioEventLoopGroup nioEventLoopGroup=new NioEventLoopGroup();
42 |
43 | ServerBootstrap serverBootstrap=new ServerBootstrap();
44 | serverBootstrap
45 | .group(nioEventLoopGroup)
46 | .channel(NioServerSocketChannel.class)
47 | .localAddress(new InetSocketAddress(8999))
48 | .childHandler(new ChannelInitializer() {
49 | @Override
50 | public void initChannel(SocketChannel ch)
51 | throws Exception {
52 | ch.pipeline().addLast(
53 | new EchoServerHandler());
54 | }
55 | });
56 | try {
57 | ChannelFuture f = serverBootstrap.bind().sync();
58 | System.out.println(EchoServer.class.getName() + " started and listen on " + f.channel().localAddress());
59 | f.channel().closeFuture().sync();
60 | } catch (InterruptedException e) {
61 | e.printStackTrace();
62 | } finally {
63 | nioEventLoopGroup.shutdownGracefully().sync();
64 | }
65 |
66 |
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/demo/EchoServerHandler.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.demo;
2 |
3 | import io.netty.buffer.ByteBuf;
4 | import io.netty.buffer.Unpooled;
5 | import io.netty.channel.ChannelFutureListener;
6 | import io.netty.channel.ChannelHandlerContext;
7 | import io.netty.channel.ChannelInboundHandlerAdapter;
8 | import io.netty.util.CharsetUtil;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | /**
12 | * @Author Jack Wu
13 | * @Description
14 | *
15 | * 一个Echo服务器 需要:
16 | * handler:这个组件实现了服务器的业务逻辑,决定了连接创建后和接收到信息后该如何处理
17 | * Bootstrapping: 这个是配置服务器的启动代码。最少需要设置服务器绑定的端口,用来监听连接请求。
18 | *
19 | *
20 | * 实现 ChannelInboundHandler 接口
21 | *
22 | * 继承ChannelInboundHandlerAdapter (提供了默认 ChannelInboundHandler 的实现)
23 | *
24 | * channelRead() - 每个信息入站都会调用
25 | * channelReadComplete() - 通知处理器最后的
26 | * channelread() 是当前批处理中的最后一条消息时调用
27 | * exceptionCaught()- 读操作时捕获到异常时调用
28 | *
29 | * @Version V1.0
30 | * @Date2020/11/25 16:43
31 | **/
32 | @Slf4j
33 | public class EchoServerHandler extends ChannelInboundHandlerAdapter {
34 | public EchoServerHandler() {
35 | super();
36 | }
37 |
38 | @Override
39 | public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
40 | super.channelRegistered(ctx);
41 | }
42 |
43 | @Override
44 | public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
45 | super.channelUnregistered(ctx);
46 | }
47 |
48 | @Override
49 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
50 | super.channelActive(ctx);
51 | }
52 |
53 | @Override
54 | public void channelInactive(ChannelHandlerContext ctx) throws Exception {
55 | super.channelInactive(ctx);
56 | }
57 |
58 | @Override
59 | public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
60 | ByteBuf byteBuf=(ByteBuf) msg;
61 | log.info("channelRead 调用:{}",byteBuf.toString(CharsetUtil.UTF_8));
62 | ctx.write(byteBuf);
63 | }
64 |
65 | @Override
66 | public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
67 | ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
68 | }
69 |
70 | @Override
71 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
72 | super.userEventTriggered(ctx, evt);
73 | }
74 |
75 | @Override
76 | public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
77 | super.channelWritabilityChanged(ctx);
78 | }
79 |
80 | @Override
81 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
82 | log.error("Echo 抛出异常,{}",cause.getMessage());
83 | ctx.close();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/demo/WebSocketServer.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.demo;
2 |
3 | import com.google.gson.Gson;
4 | import lombok.extern.slf4j.Slf4j;
5 | import org.eclipse.jetty.util.ConcurrentHashSet;
6 | import org.springframework.stereotype.Component;
7 |
8 | import javax.websocket.*;
9 | import javax.websocket.server.PathParam;
10 | import javax.websocket.server.ServerEndpoint;
11 | import java.util.Collections;
12 | import java.util.Set;
13 | import java.util.concurrent.ConcurrentHashMap;
14 |
15 | /**
16 | * @Author Jack Wu
17 | * @Description
18 | * @Version V1.0
19 | * @Date2020/11/25 10:14
20 | **/
21 | @ServerEndpoint("/socket/{connectionId}")
22 | @Component
23 | @Slf4j
24 | public class WebSocketServer {
25 | private static Set connectionIdSet= new ConcurrentHashSet();
26 | private static ConcurrentHashMap socketSessionPools=new ConcurrentHashMap<>();
27 |
28 | @OnOpen
29 | public void onSocketOpen(Session session, @PathParam(value = "connectionId") String connectionId){
30 | socketSessionPools.put(connectionId,session);
31 | connectionIdSet.add(connectionId);
32 | log.info("当前在线人数:{}",connectionIdSet.size());
33 | log.info("所有connctionId:{}",new Gson().toJson(connectionIdSet));
34 | }
35 |
36 | @OnMessage
37 | public void onMessage(String message, @PathParam(value = "connectionId") String connectionId){
38 | log.info("收到客户端{}的消息:{}",connectionId,message);
39 | //回复消息
40 | sendMessageBySessionId(connectionId,"服务端已接收消息:"+message);
41 | }
42 |
43 | @OnClose
44 | public void onClose( @PathParam(value = "connectionId") String connectionId){
45 | socketSessionPools.remove(connectionId);
46 | connectionIdSet.remove(connectionId);
47 | log.info("客户端{}断开连接,当前连接数:{}",connectionId,connectionIdSet.size());
48 | }
49 |
50 | @OnError
51 | public void OnError(Session session, Throwable throwable, @PathParam(value = "connectionId") String connectionId){
52 | log.error("socket连接异常,connectionId:{},errormessage:{}",connectionId,throwable.getMessage());
53 | }
54 |
55 | /**
56 | * send message by connectionId
57 | */
58 | public void sendMessageBySessionId(String connectionId,String message){
59 | Session session=socketSessionPools.get(connectionId);
60 | session.getAsyncRemote().sendText(message);
61 | }
62 | /**
63 | * send message to all connectionId
64 | */
65 | public void sendMessageToAll(String message){
66 | for (Session session: socketSessionPools.values()){
67 | if (session!=null){
68 | synchronized (session){
69 | session.getAsyncRemote().sendText(message);
70 | }
71 | }
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heart/CustomHeartBeatClientHandler.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heart;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.SimpleChannelInboundHandler;
5 |
6 | /**
7 | * @Author Jack Wu
8 | * @Description
9 | * @Version V1.0
10 | * @Date2020/11/27 17:19
11 | **/
12 | public class CustomHeartBeatClientHandler extends SimpleChannelInboundHandler {
13 | @Override
14 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, String string) throws Exception {
15 | System.out.println("客户端收到:"+string);
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heart/CustomHeartBeatInitializer.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heart;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelInitializer;
5 | import io.netty.channel.ChannelPipeline;
6 | import io.netty.handler.codec.string.StringDecoder;
7 | import io.netty.handler.codec.string.StringEncoder;
8 | import io.netty.handler.timeout.IdleStateHandler;
9 |
10 | import java.util.concurrent.TimeUnit;
11 |
12 | /**
13 | * @Author Jack Wu
14 | * @Description
15 | * @Version V1.0
16 | * @Date2020/11/27 17:08
17 | **/
18 | public class CustomHeartBeatInitializer extends ChannelInitializer {
19 | @Override
20 | protected void initChannel(Channel channel) throws Exception {
21 | ChannelPipeline pipeline=channel.pipeline();
22 | pipeline.addLast("decoder",new StringDecoder());
23 | pipeline.addLast("encoder",new StringEncoder());
24 | pipeline.addLast(new IdleStateHandler(2,2,2, TimeUnit.SECONDS));
25 | pipeline.addLast(new CustomHeartBeathandler());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heart/CustomHeartBeathandler.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heart;
2 |
3 | import io.netty.channel.ChannelHandlerContext;
4 | import io.netty.channel.SimpleChannelInboundHandler;
5 | import io.netty.handler.timeout.IdleStateEvent;
6 |
7 | /**
8 | * @Author Jack Wu
9 | * @Description
10 | * @Version V1.0
11 | * @Date2020/11/26 18:04
12 | **/
13 | public class CustomHeartBeathandler extends SimpleChannelInboundHandler {
14 | int readIdleTimes=0;
15 | @Override
16 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
17 | System.out.println(" Server HeartBeat received message:"+s);
18 | channelHandlerContext.writeAndFlush(" client reply : get message "+s);
19 |
20 | }
21 | @Override
22 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
23 | IdleStateEvent event= (IdleStateEvent) evt;
24 | String eventType=null;
25 | switch (event.state()){
26 | case WRITER_IDLE:
27 | eventType=" 读空闲";
28 | readIdleTimes++;
29 | break;
30 | case READER_IDLE:
31 | eventType="写空闲";
32 | break;
33 | case ALL_IDLE:
34 | eventType="读写空闲";
35 | break;
36 | }
37 | System.out.println("eventType:"+eventType);
38 | if (readIdleTimes>3){
39 | System.out.println("读空闲超过三次,关闭连接");
40 | ctx.channel().writeAndFlush("you are out");
41 | /*ctx.channel().close().sync();*/
42 | }
43 | }
44 |
45 | @Override
46 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
47 | System.out.println("= = = "+ctx.channel().remoteAddress()+"is active = = =");
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heart/CustomHeartbeatClientInitializer.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heart;
2 |
3 | import io.netty.channel.Channel;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.ChannelInitializer;
6 | import io.netty.channel.ChannelPipeline;
7 | import io.netty.handler.codec.string.StringDecoder;
8 | import io.netty.handler.codec.string.StringEncoder;
9 |
10 | /**
11 | * @Author Jack Wu
12 | * @Description
13 | * @Version V1.0
14 | * @Date2020/11/27 17:20
15 | **/
16 | public class CustomHeartbeatClientInitializer extends ChannelInitializer {
17 | @Override
18 | protected void initChannel(Channel channel) throws Exception {
19 | ChannelPipeline channelPipeline=channel.pipeline();
20 | channelPipeline.addLast("decoder",new StringDecoder());
21 | channelPipeline.addLast("encoder",new StringEncoder());
22 | channelPipeline.addLast(new CustomHeartBeatClientHandler());
23 |
24 | }
25 |
26 | @Override
27 | public void channelActive(ChannelHandlerContext ctx) throws Exception {
28 | ctx.channel().writeAndFlush(" client is alive");
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heart/HeartbeatClient.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heart;
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.SocketChannel;
8 | import io.netty.channel.socket.nio.NioSocketChannel;
9 |
10 | import java.net.InetSocketAddress;
11 |
12 | /**
13 | * @Author Jack Wu
14 | * @Description
15 | * @Version V1.0
16 | * @Date2020/11/27 17:14
17 | **/
18 | public class HeartbeatClient {
19 | private int port;
20 | private String host;
21 | public HeartbeatClient(String host,int port){
22 | this.port=port;
23 | this.host=host;
24 | }
25 | public void run() throws InterruptedException {
26 | Bootstrap bootstrap=new Bootstrap();
27 | EventLoopGroup eventLoopGroup=new NioEventLoopGroup();
28 | bootstrap
29 | .group(eventLoopGroup)
30 | .remoteAddress(new InetSocketAddress(port))
31 | .channel(NioSocketChannel.class)
32 | .handler(new CustomHeartbeatClientInitializer());
33 | try {
34 | ChannelFuture channelFuture= bootstrap.connect().sync();
35 |
36 | /*channelFuture.channel().close().sync();*/
37 | } finally {
38 | eventLoopGroup.shutdownGracefully().sync();
39 | }
40 | }
41 |
42 | public static void main(String[] args) throws InterruptedException {
43 | HeartbeatClient heartbeatClient=new HeartbeatClient("localhost",8888);
44 | heartbeatClient.run();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heart/HeartbeatServer.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heart;
2 |
3 | import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
8 | import io.netty.handler.logging.LogLevel;
9 | import io.netty.handler.logging.LoggingHandler;
10 |
11 | import java.net.InetSocketAddress;
12 |
13 | /**
14 | * @Author Jack Wu
15 | * @Description
16 | * @Version V1.0
17 | * @Date2020/11/26 17:58
18 | **/
19 | public class HeartbeatServer {
20 | private int port;
21 | private String host;
22 | public HeartbeatServer(int port,String host){
23 | this.port=port;
24 | this.host=host;
25 | }
26 |
27 | public void run() throws InterruptedException {
28 | ServerBootstrap serverBootstrap=new ServerBootstrap();
29 | EventLoopGroup bossEventLoopGroup=new NioEventLoopGroup();
30 | EventLoopGroup workerEventLoopGroup=new NioEventLoopGroup();
31 | serverBootstrap
32 | .group(bossEventLoopGroup,workerEventLoopGroup)
33 | .channel(NioServerSocketChannel.class)
34 | .localAddress(new InetSocketAddress(port))
35 | .handler(new LoggingHandler(LogLevel.INFO))
36 | .childHandler(new CustomHeartBeatInitializer());
37 | try {
38 | ChannelFuture channelFuture=serverBootstrap.bind().sync();
39 | channelFuture.channel().closeFuture().sync();
40 | } finally {
41 | bossEventLoopGroup.shutdownGracefully();
42 | workerEventLoopGroup.shutdownGracefully();
43 | }
44 |
45 |
46 | }
47 | public static void main(String[] args) throws InterruptedException {
48 | HeartbeatServer heartbeatServer=new HeartbeatServer(8888,"localhost");
49 | heartbeatServer.run();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heartbeat/CustomProtocol.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heartbeat;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * @Author Jack Wu
7 | * @Description
8 | * @Version V1.0
9 | * @Date2020/12/1 10:21
10 | **/
11 |
12 | public class CustomProtocol implements Serializable {
13 | private static final long serialVersionUID = 4671171056588401542L;
14 | private long id ;
15 | private String content ;
16 |
17 | public long getId() {
18 | return id;
19 | }
20 |
21 | public void setId(long id) {
22 | this.id = id;
23 | }
24 |
25 | public String getContent() {
26 | return content;
27 | }
28 |
29 | public void setContent(String content) {
30 | this.content = content;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heartbeat/HeartBeatServer.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heartbeat;
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 lombok.extern.slf4j.Slf4j;
10 | import org.springframework.stereotype.Component;
11 |
12 | import javax.annotation.PostConstruct;
13 | import javax.annotation.PreDestroy;
14 | import java.net.InetSocketAddress;
15 |
16 | /**
17 | * @Author Jack Wu
18 | * @Description
19 | * @Version V1.0
20 | * @Date2020/12/1 10:08
21 | **/
22 | @Component
23 | @Slf4j
24 | public class HeartBeatServer {
25 | private EventLoopGroup boss = new NioEventLoopGroup();
26 | private EventLoopGroup work = new NioEventLoopGroup();
27 |
28 |
29 | @PostConstruct
30 | public void start() throws InterruptedException {
31 |
32 | ServerBootstrap bootstrap = new ServerBootstrap()
33 | .group(boss, work)
34 | .channel(NioServerSocketChannel.class)
35 | .localAddress(new InetSocketAddress(9696))
36 | //保持长连接
37 | .childOption(ChannelOption.SO_KEEPALIVE, true)
38 | .childHandler(new HeartbeatInitializer());
39 |
40 | ChannelFuture future = bootstrap.bind().sync();
41 | if (future.isSuccess()) {
42 | log.info("启动 Netty 成功");
43 | }
44 | }
45 |
46 | @PreDestroy
47 | public void destroy() {
48 | boss.shutdownGracefully().syncUninterruptibly();
49 | work.shutdownGracefully().syncUninterruptibly();
50 | log.info("关闭 Netty 成功");
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heartbeat/HeartBeatSimpleHandle.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heartbeat;
2 |
3 | import io.netty.channel.ChannelFutureListener;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.channel.SimpleChannelInboundHandler;
6 | import io.netty.channel.socket.nio.NioSocketChannel;
7 | import io.netty.handler.timeout.IdleState;
8 | import io.netty.handler.timeout.IdleStateEvent;
9 | import lombok.extern.slf4j.Slf4j;
10 |
11 | import static io.netty.handler.codec.stomp.StompHeaders.HEART_BEAT;
12 |
13 | /**
14 | * @Author Jack Wu
15 | * @Description
16 | * @Version V1.0
17 | * @Date2020/12/1 10:20
18 | **/
19 | @Slf4j
20 | public class HeartBeatSimpleHandle extends SimpleChannelInboundHandler {
21 | @Override
22 | protected void channelRead0(ChannelHandlerContext channelHandlerContext, CustomProtocol customProtocol) throws Exception {
23 | log.info("收到customProtocol:{}",customProtocol);
24 | NettySocketHolder.put(customProtocol.getId(), (NioSocketChannel) channelHandlerContext.channel());
25 | }
26 |
27 | @Override
28 | public void channelInactive(ChannelHandlerContext ctx) throws Exception {
29 | NettySocketHolder.remove((NioSocketChannel) ctx.channel());
30 | }
31 |
32 | @Override
33 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
34 | if (evt instanceof IdleStateEvent){
35 | IdleStateEvent idleStateEvent = (IdleStateEvent) evt ;
36 |
37 | if (idleStateEvent.state() == IdleState.READER_IDLE){
38 | log.info("已经5秒没有收到信息!");
39 | log.info("NettySocketHolder MAP{}",NettySocketHolder.getMAP());
40 | //向客户端发送消息
41 | ctx.writeAndFlush(HEART_BEAT).addListener(ChannelFutureListener.CLOSE_ON_FAILURE) ;
42 | }
43 | }
44 | super.userEventTriggered(ctx, evt);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/netty-demo/src/main/java/fun/codenow/netty/socket/heartbeat/HeartbeatDecoder.java:
--------------------------------------------------------------------------------
1 | package fun.codenow.netty.socket.heartbeat;
2 |
3 | import io.netty.buffer.ByteBuf;
4 | import io.netty.channel.ChannelHandlerContext;
5 | import io.netty.handler.codec.ByteToMessageDecoder;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * @Author Jack Wu
11 | * @Description
12 | * @Version V1.0
13 | * @Date2020/12/1 10:20
14 | **/
15 | public class HeartbeatDecoder extends ByteToMessageDecoder {
16 | @Override
17 | protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List