作者 crossoverJie

:art: Improving structure / format of the code.

1 package com.crossoverjie.cim.client.client; 1 package com.crossoverjie.cim.client.client;
2 2
  3 +import com.crossoverjie.cim.client.config.AppConfiguration;
3 import com.crossoverjie.cim.client.init.CIMClientHandleInitializer; 4 import com.crossoverjie.cim.client.init.CIMClientHandleInitializer;
  5 +import com.crossoverjie.cim.client.service.MsgHandle;
4 import com.crossoverjie.cim.client.service.RouteRequest; 6 import com.crossoverjie.cim.client.service.RouteRequest;
5 import com.crossoverjie.cim.client.vo.req.GoogleProtocolVO; 7 import com.crossoverjie.cim.client.vo.req.GoogleProtocolVO;
6 import com.crossoverjie.cim.client.vo.req.LoginReqVO; 8 import com.crossoverjie.cim.client.vo.req.LoginReqVO;
@@ -16,6 +18,7 @@ import io.netty.channel.EventLoopGroup; @@ -16,6 +18,7 @@ import io.netty.channel.EventLoopGroup;
16 import io.netty.channel.nio.NioEventLoopGroup; 18 import io.netty.channel.nio.NioEventLoopGroup;
17 import io.netty.channel.socket.SocketChannel; 19 import io.netty.channel.socket.SocketChannel;
18 import io.netty.channel.socket.nio.NioSocketChannel; 20 import io.netty.channel.socket.nio.NioSocketChannel;
  21 +import io.netty.util.concurrent.DefaultThreadFactory;
19 import org.slf4j.Logger; 22 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory; 23 import org.slf4j.LoggerFactory;
21 import org.springframework.beans.factory.annotation.Autowired; 24 import org.springframework.beans.factory.annotation.Autowired;
@@ -36,7 +39,7 @@ public class CIMClient { @@ -36,7 +39,7 @@ public class CIMClient {
36 39
37 private final static Logger LOGGER = LoggerFactory.getLogger(CIMClient.class); 40 private final static Logger LOGGER = LoggerFactory.getLogger(CIMClient.class);
38 41
39 - private EventLoopGroup group = new NioEventLoopGroup(); 42 + private EventLoopGroup group = new NioEventLoopGroup(0, new DefaultThreadFactory("cim-work"));
40 43
41 @Value("${cim.user.id}") 44 @Value("${cim.user.id}")
42 private long userId; 45 private long userId;
@@ -49,6 +52,17 @@ public class CIMClient { @@ -49,6 +52,17 @@ public class CIMClient {
49 @Autowired 52 @Autowired
50 private RouteRequest routeRequest; 53 private RouteRequest routeRequest;
51 54
  55 + @Autowired
  56 + private AppConfiguration configuration;
  57 +
  58 + @Autowired
  59 + private MsgHandle msgHandle;
  60 +
  61 + /**
  62 + * 重试次数
  63 + */
  64 + private int errorCount;
  65 +
52 @PostConstruct 66 @PostConstruct
53 public void start() throws Exception { 67 public void start() throws Exception {
54 68
@@ -70,14 +84,25 @@ public class CIMClient { @@ -70,14 +84,25 @@ public class CIMClient {
70 * @param cimServer 84 * @param cimServer
71 * @throws InterruptedException 85 * @throws InterruptedException
72 */ 86 */
73 - private void startClient(CIMServerResVO.ServerInfo cimServer) throws InterruptedException { 87 + private void startClient(CIMServerResVO.ServerInfo cimServer) {
74 Bootstrap bootstrap = new Bootstrap(); 88 Bootstrap bootstrap = new Bootstrap();
75 bootstrap.group(group) 89 bootstrap.group(group)
76 .channel(NioSocketChannel.class) 90 .channel(NioSocketChannel.class)
77 .handler(new CIMClientHandleInitializer()) 91 .handler(new CIMClientHandleInitializer())
78 ; 92 ;
79 93
80 - ChannelFuture future = bootstrap.connect(cimServer.getIp(), cimServer.getCimServerPort()).sync(); 94 + ChannelFuture future = null;
  95 + try {
  96 + future = bootstrap.connect(cimServer.getIp(), cimServer.getCimServerPort()).sync();
  97 + } catch (InterruptedException e) {
  98 + errorCount++;
  99 +
  100 + if (errorCount >= configuration.getErrorCount()) {
  101 + LOGGER.error("链接失败次数达到上限[{}]次", errorCount);
  102 + msgHandle.shutdown();
  103 + }
  104 + LOGGER.error("连接失败", e);
  105 + }
81 if (future.isSuccess()) { 106 if (future.isSuccess()) {
82 LOGGER.info("启动 cim client 成功"); 107 LOGGER.info("启动 cim client 成功");
83 } 108 }
@@ -90,10 +115,21 @@ public class CIMClient { @@ -90,10 +115,21 @@ public class CIMClient {
90 * @return 路由服务器信息 115 * @return 路由服务器信息
91 * @throws Exception 116 * @throws Exception
92 */ 117 */
93 - private CIMServerResVO.ServerInfo userLogin() throws Exception { 118 + private CIMServerResVO.ServerInfo userLogin() {
94 LoginReqVO loginReqVO = new LoginReqVO(userId, userName); 119 LoginReqVO loginReqVO = new LoginReqVO(userId, userName);
95 - CIMServerResVO.ServerInfo cimServer = routeRequest.getCIMServer(loginReqVO); 120 + CIMServerResVO.ServerInfo cimServer = null;
  121 + try {
  122 + cimServer = routeRequest.getCIMServer(loginReqVO);
96 LOGGER.info("cimServer=[{}]", cimServer.toString()); 123 LOGGER.info("cimServer=[{}]", cimServer.toString());
  124 + } catch (Exception e) {
  125 + errorCount++;
  126 +
  127 + if (errorCount >= configuration.getErrorCount()) {
  128 + LOGGER.error("重连次数达到上限[{}]次", errorCount);
  129 + msgHandle.shutdown();
  130 + }
  131 + LOGGER.error("登录失败", e);
  132 + }
97 return cimServer; 133 return cimServer;
98 } 134 }
99 135
@@ -145,11 +181,17 @@ public class CIMClient { @@ -145,11 +181,17 @@ public class CIMClient {
145 181
146 } 182 }
147 183
  184 +
  185 + public void reconnect() throws Exception {
  186 + start();
  187 + }
  188 +
148 /** 189 /**
149 * 关闭 190 * 关闭
  191 + *
150 * @throws InterruptedException 192 * @throws InterruptedException
151 */ 193 */
152 public void close() throws InterruptedException { 194 public void close() throws InterruptedException {
153 - channel.close() ; 195 + channel.close();
154 } 196 }
155 } 197 }
@@ -22,6 +22,15 @@ public class AppConfiguration { @@ -22,6 +22,15 @@ public class AppConfiguration {
22 @Value("${cim.msg.logger.path}") 22 @Value("${cim.msg.logger.path}")
23 private String msgLoggerPath ; 23 private String msgLoggerPath ;
24 24
  25 + @Value("${cim.clear.route.request.url}")
  26 + private String clearRouteUrl ;
  27 +
  28 + @Value("${cim.heartbeat.time}")
  29 + private long heartBeatTime ;
  30 +
  31 + @Value("${cim.reconnect.count}")
  32 + private int errorCount ;
  33 +
25 public Long getUserId() { 34 public Long getUserId() {
26 return userId; 35 return userId;
27 } 36 }
@@ -45,4 +54,30 @@ public class AppConfiguration { @@ -45,4 +54,30 @@ public class AppConfiguration {
45 public void setMsgLoggerPath(String msgLoggerPath) { 54 public void setMsgLoggerPath(String msgLoggerPath) {
46 this.msgLoggerPath = msgLoggerPath; 55 this.msgLoggerPath = msgLoggerPath;
47 } 56 }
  57 +
  58 +
  59 + public long getHeartBeatTime() {
  60 + return heartBeatTime;
  61 + }
  62 +
  63 + public void setHeartBeatTime(long heartBeatTime) {
  64 + this.heartBeatTime = heartBeatTime;
  65 + }
  66 +
  67 +
  68 + public String getClearRouteUrl() {
  69 + return clearRouteUrl;
  70 + }
  71 +
  72 + public void setClearRouteUrl(String clearRouteUrl) {
  73 + this.clearRouteUrl = clearRouteUrl;
  74 + }
  75 +
  76 + public int getErrorCount() {
  77 + return errorCount;
  78 + }
  79 +
  80 + public void setErrorCount(int errorCount) {
  81 + this.errorCount = errorCount;
  82 + }
48 } 83 }
@@ -82,6 +82,17 @@ public class BeanConfig { @@ -82,6 +82,17 @@ public class BeanConfig {
82 return productExecutor ; 82 return productExecutor ;
83 } 83 }
84 84
  85 +
  86 + @Bean("scheduledTask")
  87 + public ScheduledExecutorService buildSchedule(){
  88 + ThreadFactory sche = new ThreadFactoryBuilder()
  89 + .setNameFormat("scheduled-%d")
  90 + .setDaemon(true)
  91 + .build();
  92 + ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1,sche) ;
  93 + return scheduledExecutorService ;
  94 + }
  95 +
85 /** 96 /**
86 * 回调 bean 97 * 回调 bean
87 * @return 98 * @return
1 package com.crossoverjie.cim.client.handle; 1 package com.crossoverjie.cim.client.handle;
2 2
  3 +import com.crossoverjie.cim.client.thread.HeartBeatJob;
3 import com.crossoverjie.cim.client.util.SpringBeanFactory; 4 import com.crossoverjie.cim.client.util.SpringBeanFactory;
4 import com.crossoverjie.cim.common.constant.Constants; 5 import com.crossoverjie.cim.common.constant.Constants;
5 import com.crossoverjie.cim.common.protocol.CIMRequestProto; 6 import com.crossoverjie.cim.common.protocol.CIMRequestProto;
@@ -14,7 +15,9 @@ import io.netty.handler.timeout.IdleStateEvent; @@ -14,7 +15,9 @@ import io.netty.handler.timeout.IdleStateEvent;
14 import org.slf4j.Logger; 15 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory; 16 import org.slf4j.LoggerFactory;
16 17
  18 +import java.util.concurrent.ScheduledExecutorService;
17 import java.util.concurrent.ThreadPoolExecutor; 19 import java.util.concurrent.ThreadPoolExecutor;
  20 +import java.util.concurrent.TimeUnit;
18 21
19 /** 22 /**
20 * Function: 23 * Function:
@@ -32,6 +35,8 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt @@ -32,6 +35,8 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
32 35
33 private ThreadPoolExecutor threadPoolExecutor ; 36 private ThreadPoolExecutor threadPoolExecutor ;
34 37
  38 + private ScheduledExecutorService scheduledExecutorService ;
  39 +
35 40
36 @Override 41 @Override
37 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 42 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
@@ -50,7 +55,6 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt @@ -50,7 +55,6 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
50 }) ; 55 }) ;
51 } 56 }
52 57
53 -  
54 } 58 }
55 59
56 super.userEventTriggered(ctx, evt); 60 super.userEventTriggered(ctx, evt);
@@ -68,6 +72,7 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt @@ -68,6 +72,7 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
68 72
69 //心跳更新时间 73 //心跳更新时间
70 if (msg.getType() == Constants.CommandType.PING){ 74 if (msg.getType() == Constants.CommandType.PING){
  75 + LOGGER.info("收到服务端心跳!!!");
71 NettyAttrUtil.updateReaderTime(ctx.channel(),System.currentTimeMillis()); 76 NettyAttrUtil.updateReaderTime(ctx.channel(),System.currentTimeMillis());
72 } 77 }
73 78
@@ -78,6 +83,13 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt @@ -78,6 +83,13 @@ public class CIMClientHandle extends SimpleChannelInboundHandler<CIMResponseProt
78 LOGGER.info(msg.getResMsg()); 83 LOGGER.info(msg.getResMsg());
79 } 84 }
80 85
  86 + if (scheduledExecutorService == null){
  87 + scheduledExecutorService = SpringBeanFactory.getBean("scheduledTask",ScheduledExecutorService.class) ;
  88 + }
  89 +
  90 +
  91 + scheduledExecutorService.scheduleAtFixedRate(new HeartBeatJob(ctx),30,30, TimeUnit.SECONDS) ;
  92 +
81 } 93 }
82 94
83 /** 95 /**
@@ -25,7 +25,7 @@ public class CIMClientHandleInitializer extends ChannelInitializer<Channel> { @@ -25,7 +25,7 @@ public class CIMClientHandleInitializer extends ChannelInitializer<Channel> {
25 protected void initChannel(Channel ch) throws Exception { 25 protected void initChannel(Channel ch) throws Exception {
26 ch.pipeline() 26 ch.pipeline()
27 //30 秒没发送消息 将IdleStateHandler 添加到 ChannelPipeline 中 27 //30 秒没发送消息 将IdleStateHandler 添加到 ChannelPipeline 中
28 - .addLast(new IdleStateHandler(0, 30, 0)) 28 + .addLast(new IdleStateHandler(0, 50, 0))
29 29
30 //心跳解码 30 //心跳解码
31 //.addLast(new HeartbeatEncode()) 31 //.addLast(new HeartbeatEncode())
@@ -48,4 +48,10 @@ public interface MsgHandle { @@ -48,4 +48,10 @@ public interface MsgHandle {
48 * @return 是否应当跳过当前消息(包含了":" 就需要跳过) 48 * @return 是否应当跳过当前消息(包含了":" 就需要跳过)
49 */ 49 */
50 boolean innerCommand(String msg) ; 50 boolean innerCommand(String msg) ;
  51 +
  52 +
  53 + /**
  54 + * 关闭系统
  55 + */
  56 + void shutdown() ;
51 } 57 }
@@ -41,10 +41,13 @@ public interface RouteRequest { @@ -41,10 +41,13 @@ public interface RouteRequest {
41 CIMServerResVO.ServerInfo getCIMServer(LoginReqVO loginReqVO) throws Exception; 41 CIMServerResVO.ServerInfo getCIMServer(LoginReqVO loginReqVO) throws Exception;
42 42
43 /** 43 /**
44 - *  
45 - * @return 获取所有在线用户 44 + * 获取所有在线用户
  45 + * @return
  46 + * @throws Exception
46 */ 47 */
47 List<OnlineUsersResVO.DataBodyBean> onlineUsers()throws Exception ; 48 List<OnlineUsersResVO.DataBodyBean> onlineUsers()throws Exception ;
48 49
49 50
  51 + void offLine() ;
  52 +
50 } 53 }
1 package com.crossoverjie.cim.client.service.impl; 1 package com.crossoverjie.cim.client.service.impl;
2 2
  3 +import com.crossoverjie.cim.client.client.CIMClient;
  4 +import com.crossoverjie.cim.client.config.AppConfiguration;
  5 +import com.crossoverjie.cim.client.service.RouteRequest;
3 import com.crossoverjie.cim.common.kit.HeartBeatHandler; 6 import com.crossoverjie.cim.common.kit.HeartBeatHandler;
  7 +import com.crossoverjie.cim.common.util.NettyAttrUtil;
4 import io.netty.channel.ChannelHandlerContext; 8 import io.netty.channel.ChannelHandlerContext;
  9 +import okhttp3.MediaType;
  10 +import org.slf4j.Logger;
  11 +import org.slf4j.LoggerFactory;
  12 +import org.springframework.beans.factory.annotation.Autowired;
  13 +import org.springframework.stereotype.Service;
5 14
6 /** 15 /**
7 * Function: 16 * Function:
@@ -10,10 +19,39 @@ import io.netty.channel.ChannelHandlerContext; @@ -10,10 +19,39 @@ import io.netty.channel.ChannelHandlerContext;
10 * Date: 2019-01-20 17:16 19 * Date: 2019-01-20 17:16
11 * @since JDK 1.8 20 * @since JDK 1.8
12 */ 21 */
  22 +@Service
13 public class ClientHeartBeatHandlerImpl implements HeartBeatHandler { 23 public class ClientHeartBeatHandlerImpl implements HeartBeatHandler {
14 24
  25 + private final static Logger LOGGER = LoggerFactory.getLogger(ClientHeartBeatHandlerImpl.class);
  26 + private final MediaType mediaType = MediaType.parse("application/json");
  27 +
  28 + @Autowired
  29 + private AppConfiguration appConfiguration ;
  30 +
  31 + @Autowired
  32 + private CIMClient cimClient ;
  33 +
  34 + @Autowired
  35 + private RouteRequest routeRequest;
  36 +
15 @Override 37 @Override
16 public void process(ChannelHandlerContext ctx) throws Exception { 38 public void process(ChannelHandlerContext ctx) throws Exception {
17 39
  40 + long heartBeatTime = appConfiguration.getHeartBeatTime() * 1000;
  41 +
  42 + Long lastReadTime = NettyAttrUtil.getReaderTime(ctx.channel());
  43 + long now = System.currentTimeMillis();
  44 + if (lastReadTime != null && now - lastReadTime > heartBeatTime){
  45 + LOGGER.warn("服务端心跳超时[{}]ms,[{}]需要关闭重新连接!",now - lastReadTime,appConfiguration.getUserName());
  46 +
  47 + //首先清除路由信息,下线
  48 + routeRequest.offLine();
  49 +
  50 + //重连
  51 + cimClient.reconnect();
  52 +
  53 + }
18 } 54 }
  55 +
  56 +
19 } 57 }
@@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory;
16 import org.springframework.beans.factory.annotation.Autowired; 16 import org.springframework.beans.factory.annotation.Autowired;
17 import org.springframework.stereotype.Service; 17 import org.springframework.stereotype.Service;
18 18
  19 +import javax.annotation.Resource;
19 import java.util.List; 20 import java.util.List;
20 import java.util.Map; 21 import java.util.Map;
21 import java.util.concurrent.ThreadPoolExecutor; 22 import java.util.concurrent.ThreadPoolExecutor;
@@ -37,7 +38,7 @@ public class MsgHandler implements MsgHandle { @@ -37,7 +38,7 @@ public class MsgHandler implements MsgHandle {
37 @Autowired 38 @Autowired
38 private AppConfiguration configuration; 39 private AppConfiguration configuration;
39 40
40 - @Autowired 41 + @Resource(name = "callBackThreadPool")
41 private ThreadPoolExecutor executor ; 42 private ThreadPoolExecutor executor ;
42 43
43 @Autowired 44 @Autowired
@@ -221,8 +222,10 @@ public class MsgHandler implements MsgHandle { @@ -221,8 +222,10 @@ public class MsgHandler implements MsgHandle {
221 /** 222 /**
222 * 关闭系统 223 * 关闭系统
223 */ 224 */
224 - private void shutdown() { 225 + @Override
  226 + public void shutdown() {
225 LOGGER.info("系统关闭中。。。。"); 227 LOGGER.info("系统关闭中。。。。");
  228 + routeRequest.offLine();
226 msgLogger.stop(); 229 msgLogger.stop();
227 executor.shutdown(); 230 executor.shutdown();
228 try { 231 try {
@@ -178,4 +178,26 @@ public class RouteRequestImpl implements RouteRequest { @@ -178,4 +178,26 @@ public class RouteRequestImpl implements RouteRequest {
178 178
179 return onlineUsersResVO.getDataBody(); 179 return onlineUsersResVO.getDataBody();
180 } 180 }
  181 +
  182 + @Override
  183 + public void offLine() {
  184 + JSONObject jsonObject = new JSONObject();
  185 + jsonObject.put("userId", appConfiguration.getUserId());
  186 + jsonObject.put("msg", "offLine");
  187 + RequestBody requestBody = RequestBody.create(mediaType, jsonObject.toString());
  188 +
  189 + Request request = new Request.Builder()
  190 + .url(appConfiguration.getClearRouteUrl())
  191 + .post(requestBody)
  192 + .build();
  193 +
  194 + Response response = null;
  195 + try {
  196 + response = okHttpClient.newCall(request).execute();
  197 + } catch (IOException e) {
  198 + LOGGER.error("exception",e);
  199 + } finally {
  200 + response.body().close();
  201 + }
  202 + }
181 } 203 }
  1 +package com.crossoverjie.cim.client.thread;
  2 +
  3 +import com.crossoverjie.cim.client.service.impl.ClientHeartBeatHandlerImpl;
  4 +import com.crossoverjie.cim.client.util.SpringBeanFactory;
  5 +import com.crossoverjie.cim.common.kit.HeartBeatHandler;
  6 +import io.netty.channel.ChannelHandlerContext;
  7 +import org.slf4j.Logger;
  8 +import org.slf4j.LoggerFactory;
  9 +
  10 +/**
  11 + * Function:
  12 + *
  13 + * @author crossoverJie
  14 + * Date: 2019-01-20 21:35
  15 + * @since JDK 1.8
  16 + */
  17 +public class HeartBeatJob implements Runnable {
  18 +
  19 + private final static Logger LOGGER = LoggerFactory.getLogger(HeartBeatJob.class);
  20 +
  21 + private ChannelHandlerContext context ;
  22 +
  23 + private HeartBeatHandler heartBeatHandler ;
  24 +
  25 + public HeartBeatJob(ChannelHandlerContext context) {
  26 + this.context = context;
  27 + this.heartBeatHandler = SpringBeanFactory.getBean(ClientHeartBeatHandlerImpl.class) ;
  28 + }
  29 +
  30 + @Override
  31 + public void run() {
  32 + try {
  33 + heartBeatHandler.process(context);
  34 + } catch (Exception e) {
  35 + e.printStackTrace();
  36 + }
  37 + }
  38 +}
@@ -25,6 +25,8 @@ cim.server.route.request.url=http://45.78.28.220:8083/login @@ -25,6 +25,8 @@ cim.server.route.request.url=http://45.78.28.220:8083/login
25 # 在线用户 25 # 在线用户
26 cim.server.online.user.url=http://45.78.28.220:8083/onlineUser 26 cim.server.online.user.url=http://45.78.28.220:8083/onlineUser
27 27
  28 +# 清除路由信息
  29 +cim.clear.route.request.url=http://45.78.28.220:8083/offLine
28 30
29 ###=======本地模拟======### 31 ###=======本地模拟======###
30 ## 群发消息 32 ## 群发消息
@@ -39,6 +41,9 @@ cim.server.online.user.url=http://45.78.28.220:8083/onlineUser @@ -39,6 +41,9 @@ cim.server.online.user.url=http://45.78.28.220:8083/onlineUser
39 ## 在线用户 41 ## 在线用户
40 #cim.server.online.user.url=http://localhost:8083/onlineUser 42 #cim.server.online.user.url=http://localhost:8083/onlineUser
41 43
  44 +# 清除路由信息
  45 +#cim.clear.route.request.url=http://localhost:8083/offLine
  46 +
42 # 客户端唯一ID 47 # 客户端唯一ID
43 cim.user.id=1545574841528 48 cim.user.id=1545574841528
44 cim.user.userName=zhangsan 49 cim.user.userName=zhangsan
@@ -54,4 +59,7 @@ management.security.enabled=false @@ -54,4 +59,7 @@ management.security.enabled=false
54 spring.boot.admin.url=http://127.0.0.1:8888 59 spring.boot.admin.url=http://127.0.0.1:8888
55 60
56 # 检测多少秒没有收到服务端端心跳后重新登录获取连接 61 # 检测多少秒没有收到服务端端心跳后重新登录获取连接
57 -cim.heartbeat.time = 40  
  62 +cim.heartbeat.time = 60
  63 +
  64 +# 客户端连接失败重连次数
  65 +cim.reconnect.count =3
@@ -12,7 +12,9 @@ import io.netty.channel.ChannelHandlerContext; @@ -12,7 +12,9 @@ import io.netty.channel.ChannelHandlerContext;
12 public interface HeartBeatHandler { 12 public interface HeartBeatHandler {
13 13
14 /** 14 /**
15 - * 处理 15 + * 处理心跳
  16 + * @param ctx
  17 + * @throws Exception
16 */ 18 */
17 void process(ChannelHandlerContext ctx) throws Exception ; 19 void process(ChannelHandlerContext ctx) throws Exception ;
18 } 20 }
@@ -94,7 +94,7 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto @@ -94,7 +94,7 @@ public class CIMServerHandle extends SimpleChannelInboundHandler<CIMRequestProto
94 } 94 }
95 95
96 /** 96 /**
97 - * 清除路由关系 97 + * 下线,清除路由关系
98 * 98 *
99 * @param userInfo 99 * @param userInfo
100 * @throws IOException 100 * @throws IOException