摘要:實(shí)現(xiàn)聊天,項(xiàng)目介紹。本章完整代碼鏈接。本章主要講的是聊天中關(guān)于聊天功能的實(shí)現(xiàn)。移除方法與處理異常方法的重寫(xiě)在中重寫(xiě)其移除的方法,以及處理異常的方法。用戶(hù)手機(jī)端獲取未簽收的消息列表判斷不能為空查詢(xún)列表測(cè)試
Netty+SpringBoot+FastDFS+Html5實(shí)現(xiàn)聊天App,項(xiàng)目介紹。
Netty+SpringBoot+FastDFS+Html5實(shí)現(xiàn)聊天App,項(xiàng)目github鏈接。
本章完整代碼鏈接。
本章主要講的是聊天App_PigChat中關(guān)于聊天功能的實(shí)現(xiàn)。
移除方法與處理異常方法的重寫(xiě)在ChatHandler中重寫(xiě)其移除channel的方法handlerRemoved,以及處理異常的方法exceptionCaught。
@Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { String channelId = ctx.channel().id().asShortText(); System.out.println("客戶(hù)端被移除,channelId為:" + channelId); // 當(dāng)觸發(fā)handlerRemoved,ChannelGroup會(huì)自動(dòng)移除對(duì)應(yīng)客戶(hù)端的channel users.remove(ctx.channel()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); // 發(fā)生異常之后關(guān)閉連接(關(guān)閉channel),隨后從ChannelGroup中移除 ctx.channel().close(); users.remove(ctx.channel()); }定義消息的實(shí)體類(lèi)
public class ChatMsg implements Serializable { private static final long serialVersionUID = 3611169682695799175L; private String senderId; // 發(fā)送者的用戶(hù)id private String receiverId; // 接受者的用戶(hù)id private String msg; // 聊天內(nèi)容 private String msgId; // 用于消息的簽收 public String getSenderId() { return senderId; } public void setSenderId(String senderId) { this.senderId = senderId; } public String getReceiverId() { return receiverId; } public void setReceiverId(String receiverId) { this.receiverId = receiverId; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public String getMsgId() { return msgId; } public void setMsgId(String msgId) { this.msgId = msgId; } }
對(duì)實(shí)體類(lèi)再做一層包裝
public class DataContent implements Serializable { private static final long serialVersionUID = 8021381444738260454L; private Integer action; // 動(dòng)作類(lèi)型 private ChatMsg chatMsg; // 用戶(hù)的聊天內(nèi)容entity private String extand; // 擴(kuò)展字段 public Integer getAction() { return action; } public void setAction(Integer action) { this.action = action; } public ChatMsg getChatMsg() { return chatMsg; } public void setChatMsg(ChatMsg chatMsg) { this.chatMsg = chatMsg; } public String getExtand() { return extand; } public void setExtand(String extand) { this.extand = extand; } }定義發(fā)送消息的動(dòng)作的枚舉類(lèi)型
public enum MsgActionEnum { CONNECT(1, "第一次(或重連)初始化連接"), CHAT(2, "聊天消息"), SIGNED(3, "消息簽收"), KEEPALIVE(4, "客戶(hù)端保持心跳"), 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; } }
/** * @Description: 用戶(hù)id和channel的關(guān)聯(lián)關(guān)系處理 */ public class UserChannelRel { private static HashMap接受與處理消息方法的重寫(xiě)manager = new HashMap<>(); public static void put(String senderId, Channel channel) { manager.put(senderId, channel); } public static Channel get(String senderId) { return manager.get(senderId); } public static void output() { for (HashMap.Entry entry : manager.entrySet()) { System.out.println("UserId: " + entry.getKey() + ", ChannelId: " + entry.getValue().id().asLongText()); } } }
重寫(xiě)ChatHandler讀取消息的channelRead0方法。
具體步驟如下:
(1)獲取客戶(hù)端發(fā)來(lái)的消息;
(2)判斷消息類(lèi)型,根據(jù)不同的類(lèi)型來(lái)處理不同的業(yè)務(wù);
(2.1)當(dāng)websocket 第一次open的時(shí)候,初始化channel,把用的channel和userid關(guān)聯(lián)起來(lái);
(2.2)聊天類(lèi)型的消息,把聊天記錄保存到數(shù)據(jù)庫(kù),同時(shí)標(biāo)記消息的簽收狀態(tài)[未簽收];
然后實(shí)現(xiàn)消息的發(fā)送,首先從全局用戶(hù)Channel關(guān)系中獲取接受方的channel,然后當(dāng)receiverChannel不為空的時(shí)候,從ChannelGroup去查找對(duì)應(yīng)的channel是否存在,若用戶(hù)在線,則使用writeAndFlush方法向其發(fā)送消息;
(2.3)簽收消息類(lèi)型,針對(duì)具體的消息進(jìn)行簽收,修改數(shù)據(jù)庫(kù)中對(duì)應(yīng)消息的簽收狀態(tài)[已簽收];
(2.4)心跳類(lèi)型的消息
// 用于記錄和管理所有客戶(hù)端的channle public static ChannelGroup users = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("read.........."); // 獲取客戶(hù)端傳輸過(guò)來(lái)的消息 String content = msg.text(); Channel currentChannel = ctx.channel(); // 1. 獲取客戶(hù)端發(fā)來(lái)的消息 DataContent dataContent = JsonUtils.jsonToPojo(content, DataContent.class); Integer action = dataContent.getAction(); // 2. 判斷消息類(lèi)型,根據(jù)不同的類(lèi)型來(lái)處理不同的業(yè)務(wù) if (action == MsgActionEnum.CONNECT.type) { // 2.1 當(dāng)websocket 第一次open的時(shí)候,初始化channel,把用的channel和userid關(guān)聯(lián)起來(lái) String senderId = dataContent.getChatMsg().getSenderId(); UserChannelRel.put(senderId, currentChannel); // 測(cè)試 for (Channel c : users) { System.out.println(c.id().asLongText()); } UserChannelRel.output(); } else if (action == MsgActionEnum.CHAT.type) { // 2.2 聊天類(lèi)型的消息,把聊天記錄保存到數(shù)據(jù)庫(kù),同時(shí)標(biāo)記消息的簽收狀態(tài)[未簽收] ChatMsg chatMsg = dataContent.getChatMsg(); String msgText = chatMsg.getMsg(); String receiverId = chatMsg.getReceiverId(); String senderId = chatMsg.getSenderId(); // 保存消息到數(shù)據(jù)庫(kù),并且標(biāo)記為 未簽收 UserService userService = (UserService)SpringUtil.getBean("userServiceImpl"); String msgId = userService.saveMsg(chatMsg); chatMsg.setMsgId(msgId); DataContent dataContentMsg = new DataContent(); dataContentMsg.setChatMsg(chatMsg); // 發(fā)送消息 // 從全局用戶(hù)Channel關(guān)系中獲取接受方的channel Channel receiverChannel = UserChannelRel.get(receiverId); if (receiverChannel == null) { // TODO channel為空代表用戶(hù)離線,推送消息(JPush,個(gè)推,小米推送) } else { // 當(dāng)receiverChannel不為空的時(shí)候,從ChannelGroup去查找對(duì)應(yīng)的channel是否存在 Channel findChannel = users.find(receiverChannel.id()); if (findChannel != null) { // 用戶(hù)在線 receiverChannel.writeAndFlush( new TextWebSocketFrame( JsonUtils.objectToJson(dataContentMsg))); } else { // 用戶(hù)離線 TODO 推送消息 } } } else if (action == MsgActionEnum.SIGNED.type) { // 2.3 簽收消息類(lèi)型,針對(duì)具體的消息進(jìn)行簽收,修改數(shù)據(jù)庫(kù)中對(duì)應(yīng)消息的簽收狀態(tài)[已簽收] UserService userService = (UserService)SpringUtil.getBean("userServiceImpl"); // 擴(kuò)展字段在signed類(lèi)型的消息中,代表需要去簽收的消息id,逗號(hào)間隔 String msgIdsStr = dataContent.getExtand(); String msgIds[] = msgIdsStr.split(","); List獲取未簽收的消息列表的接口msgIdList = new ArrayList<>(); for (String mid : msgIds) { if (StringUtils.isNotBlank(mid)) { msgIdList.add(mid); } } System.out.println(msgIdList.toString()); if (msgIdList != null && !msgIdList.isEmpty() && msgIdList.size() > 0) { // 批量簽收 userService.updateMsgSigned(msgIdList); } } else if (action == MsgActionEnum.KEEPALIVE.type) { // 2.4 心跳類(lèi)型的消息 System.out.println("收到來(lái)自channel為[" + currentChannel + "]的心跳包..."); } }
在controller中添加獲取未簽收的消息列表的接口getUnReadMsgList。
/** * * @Description: 用戶(hù)手機(jī)端獲取未簽收的消息列表 */ @PostMapping("/getUnReadMsgList") public IMoocJSONResult getUnReadMsgList(String acceptUserId) { // 0. userId 判斷不能為空 if (StringUtils.isBlank(acceptUserId)) { return IMoocJSONResult.errorMsg(""); } // 查詢(xún)列表 List測(cè)試unreadMsgList = userService.getUnReadMsgList(acceptUserId); return IMoocJSONResult.ok(unreadMsgList); }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/73307.html
Netty+SpringBoot+FastDFS+Html5實(shí)現(xiàn)聊天App,項(xiàng)目介紹。Netty+SpringBoot+FastDFS+Html5實(shí)現(xiàn)聊天App,項(xiàng)目github鏈接。本章完整代碼鏈接。 本章內(nèi)容 (1) 查詢(xún)好友列表的接口 (2)通過(guò)或忽略好友請(qǐng)求的接口 (3)添加好友功能展示 查詢(xún)好友列表的接口 /** * @Description: 查詢(xún)我的好友列表 ...
閱讀 2257·2021-11-23 09:51
閱讀 1051·2021-11-18 10:02
閱讀 3444·2021-10-13 09:49
閱讀 1275·2021-09-22 14:57
閱讀 10496·2021-08-18 10:20
閱讀 1189·2019-08-30 15:55
閱讀 2236·2019-08-29 16:06
閱讀 3241·2019-08-29 11:14