# netty-server-client
**Repository Path**: dengfeng/netty-server-client
## Basic Information
- **Project Name**: netty-server-client
- **Description**: 完成服务端与客户端的长连接通信,基于netty4,实现了心跳监测和自动重连,实现了spring-boot-starter,引入即可使用。
- **Primary Language**: Java
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2022-05-07
- **Last Updated**: 2023-06-20
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### Server- Client 长连接组件
**Springboot2**集成 **netty4** ,使用protobuf作为数据封包,实现了服务端与多客户端的长连接,封装了自动登录、token验证、数据发送接收、心跳监测、断线重连等功能,并实现了spring-boot-starter,项目中引入即可使用。
### 开发环境
- JDK 8+
- springboot 2.4.13
- Netty 4+
- protobuf-java 3.3.0
### 已完成功能
- 客户端授权验证
- 数据包token校验
- 心跳检测
- 断线重连、断线通知
- 异步方式发送/接收消息
- 同步方式发送消息并获取返回结果
### 心跳实现详细过程
1. 客户端网络空闲10秒(默认)没有进行读写操作,进行发送一次ping心跳给服务端;
2. 客户端如果在下一个发送ping心跳周期来临时,还没有收到服务端pong的心跳应答,则失败心跳计数器加1,如果连续超过2次没有收到服务端的心跳回复,则断开当前连接,以5秒的间隔进行重连操作,直到重连成功;
3. 服务端网络空闲状态到达15秒(默认)后,服务端没有收到客户端的ping消息,将关闭链路,释放资源,等待客户端重连;
### 使用方法
**服务端**
1.引入netty server
```
com.heyi
netty-heart-server-starter
1.2.0
```
2.配置参数(无必配项,可不配置使用默认参数),在application.properties里设置
```
# 设置服务器IP和端口,不设置默认为0.0.0.0:10018
netty.server.host=127.0.0.1
netty.server.port=10018
# 修改netty性能设置,可不设置使用默认值(1, 4)
netty.server.bossCount=1
netty.server.workerCount=10
# 心跳间隔时间 不设置默认10秒
netty.server.heartSeconds = 10;
# 如果想不启动netty,设置
netty.server.enabled=false
```
3.接收客户端数据,实现ChannelDataListener接口
```
@Component
public class ServerNettyListener implements ChannelDataListener {
@Autowired
ClientService clientService;
@Override
public boolean authority(String clientId, long timestamp, String sing) {
// 验证规则 sign=md5(client+timestamp+clientSecret)
if(clientService.valid(clientId, sing)){
// 有需要的话,可进一步验证timestamp,如timestamp只允许提交一次,timestamp只在10秒内有效
log.info("新增一个客户端:"+clientId);
return true;
}
return false;
}
@Override
public void receiveData(MessageHandler messageHandler) {
log.info("接收到客户端"+messageHandler.getMessage().getClientId() + "的消息:"+ messageHandler.getMessage().getData());
// 如果客户端使用同步sendMessageAndReturn发送消息,则第一条消息为回复消息,后面的消息为新消息
messageHandler.sendMessage("回复消息");
}
@Override
public void disconnected(String clientId, Throwable cause) {
log.info("客户端"+clientId + "断开连接:"+ cause.getMessage());
}
}
```
4.向客户端发送消息
41. 异步方式(推荐)
```
// 注入NettyServer对象
@Autowired
NettyServer nettyServer;
// 使用sendMessage方法, 有返回消息的话在ChannelDataListener中处理
nettyServer.sendMessage(clientId, "测试消息");
// 还可以监听发送成功消息
nettyServer.sendMessage(clientId, "测试消息").addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if(channelFuture.isSuccess()){
log.info("消息发送成功:"+json);
}
}
});
```
42. 同步方式(此方式会阻塞线程,非必要不推荐使用)
```
// 注入NettyServer对象
@Autowired
NettyServer nettyServer;
// 向客户端[clientId]发送消息并获得客户端返回内容,若 10秒后客户端无返回则报超时错误
String msg = nettyServer.sendMessageAndReturn(clientId, "测试消息", 10);
!注意:sendMessageAndReturn方法需要客户端的ChannelDataListener中使用 messageHandler.sendMessage("回复消息")
语句回复消息(若有多条,则第一条为回复消息),而不能用 nettyClient.sendMessage(msg)
```
**客户端**
1.引入netty client
```
com.heyi
netty-heart-client-starter
1.2.0
```
2.配置参数,在application.properties里设置
```
# 设置客户端id和密钥
netty.clientId=test
netty.clientSecret=123456
# 设置服务器的IP和端口
netty.serverHost=127.0.0.1
netty.serverPort=10018
# 心跳间隔时间 不设置默认10秒
netty.heartSeconds = 10;
# 如果想不启动netty client,设置
netty.client.enabled=false
```
3.数据处理,实现ChannelDataListener接口
```
@Component
public class NettyClientListener implements ChannelDataListener {
@Override
public void authority(boolean auth) {
if(auth) {
log.info("连接服务端验证通过");
}else {
log.info("连接服务端验证没通过");
}
}
@Override
public void receiveData(MessageHandler messageHandler) {
log.info("收到数据:" + messageHandler.getMessage().getData());
// 如果有需要,可以回复消息
messageHandler.sendMessage("回复消息");
}
@Override
public void disconnected() {
log.info("连接已断开");
}
}
```
4.向服务端发送消息
41. 异步方式(推荐)
```
// 注入NettyClient对象
@Autowired
private NettyClient nettyClient;
// 使用sendMessage方法, 有返回消息的话在ChannelDataListener中处理
nettyClient.sendMessage(msg);
// 还可以监听发送成功消息
nettyClient.sendMessage("测试消息").addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if(channelFuture.isSuccess()){
log.info("消息发送成功:"+json);
}
}
});
```
42. 同步方式(此方式会阻塞线程,非必要不推荐使用)
```
// 注入NettyServer对象
@Autowired
NettyClient nettyClient;
// 向服务端发送消息并获得服务端返回内容,若 10秒后服务端无返回则报超时错误
String msg = nettyClient.sendMessageAndReturn("测试消息", 10);
!注意:sendMessageAndReturn方法需要服务端的ChannelDataListener中使用 messageHandler.sendMessage("回复消息")
语句回复消息,而不能用 nettyServer.sendMessage(msg)
```
### 版本记录
>**- 1.2.0**
>>- 已登录的clientId,其它终端无法重复登录
>>- 已登录验证的数据发送使用token,防止绕开验证直接发送数据
>>- 增加服务端和客户端同步发送消息方法,同步阻塞等待客户端返回消息
>**- 1.1.0**
>>- 将默认心跳从1分钟改为10秒(断线重连时间缩短到20S)
>>- 允许通过配置改变心跳间隔
>>- 登录验证没通过后,尝试3次后不再继续登录
>>- 已登录的clientId重复登录,前一个会释放资源并清除
>**- 1.0.0**
>>- 客户端授权验证
>>- 心跳检测
>>- 断线重连、断线通知
>>- 异步方式发送/接收消息