[TOC] ## 测试工具 推荐测试工具:http://coolaf.com/tool/chattest 项目启动后的websocket地址: ![](https://img.kancloud.cn/a5/72/a572b9280d2a952c325429e0bceb4924_895x552.png) 一直发送消息就不会断开链接,Netty读写空闲的时候,可以通过设置主动断开连接。 ## 对接流程 ![](https://img.kancloud.cn/b9/95/b9955bdf72ff9b2956c9f889d99f00d0_1132x327.png) ### 消息类型 代码中预设了以下几种action: ```java public enum MsgActionEnum { CONNECT(1, "第一次(或重连)初始化连接"), CHAT(2, "聊天消息"), SIGNED(3, "消息签收"), KEEPALIVE(4, "客户端保持心跳"), PULL_FRIEND(5, "拉取好友"); public final Integer type; public final String content; MsgActionEnum(Integer type, String content){ this.type = type; this.content = content; } public Integer getType() { return type; } } ``` ## 发送测试 ### 建立连接 ```json { "action": "1", "chatMsg": { "senderId": "10000", "msg": "这是一条登录消息", "receiverId": null }, "extand": null } ``` 测试结果: ``` 客户端被移除:channel id 为:c999a1cc 005056fffec00008-00005328-0000000b-38c05956b8a7fe83-7e0ea4d9 UserId:10000,ChannelId:005056fffec00008-00005328-0000000b-38c05956b8a7fe83-7e0ea4d9 进入写空闲...... 进入读空闲...... 进入写空闲...... channel 关闭之前:users 的数量为:1 channel 关闭之后:users 的数量为:0 客户端被移除:channel id 为:7e0ea4d9 ``` #### `action = 1`存在的意义是什么? ```java if(MsgActionEnum.CONNECT.type.equals(action)){ //2.1 当websocket 第一次open的时候,初始化channel,把用的channel 和 userid 关联起来 String senderId = dataContent.getChatMsg().getSenderId(); UserChanelRel.put(senderId,channel); //测试 for (Channel c: users) { log.info(c.id().asLongText()); } UserChanelRel.output(); } ``` 把消息的发送者放到一个map中,标记该用户的login状态。 ### 聊天消息 ```json { "action": "2", "chatMsg": { "senderId": "10000", "receiverId": 10086, "msg": "天若有情天亦老" }, "extand": null } ``` ### 签收消息 ```json { "action": "3", "chatMsg": { "senderId": "10000", "receiverId": null, "msg": null }, "extand": 23549256,224244 } ``` 通过接口获取到的消息清单,标记为签收状态,消息编号存放到`extand`属性中。 ### 心跳检测 ```json { "action": "4", "chatMsg": { "senderId": "10000", "receiverId": null, "msg": "心跳保持" }, "extand": null } ``` 需要与服务端保持连接,否则读写均空闲的时候,服务端就会主动断开链接。 ``` /** * 用于检测channel 的心跳handler * 继承ChannelInboundHandlerAdapter,目的是不需要实现ChannelRead0 这个方法 */ public class HeartBeatHandler extends ChannelInboundHandlerAdapter { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if(evt instanceof IdleStateEvent){ IdleStateEvent event = (IdleStateEvent)evt;//强制类型转化 if(event.state()== IdleState.READER_IDLE){ System.out.println("进入读空闲......"); }else if(event.state() == IdleState.WRITER_IDLE) { System.out.println("进入写空闲......"); }else if(event.state()== IdleState.ALL_IDLE){ System.out.println("channel 关闭之前:users 的数量为:"+ChatHandler.users.size()); Channel channel = ctx.channel(); //资源释放 channel.close(); System.out.println("channel 关闭之后:users 的数量为:"+ChatHandler.users.size()); } } } } ```