2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > websocket实现仿微信聊天功能

websocket实现仿微信聊天功能

时间:2022-02-23 06:39:38

相关推荐

websocket实现仿微信聊天功能

后端使用的技术是springboot前端是H5+和mui框架。

具体步骤很多,我主要概况一下大概,如果有朋友想要源码欢迎加我qq997355706。

前端最主要的地方有4个:

分别是登录创建与后端websocket的连接,关闭连接和发生错误,和接收消息。

其中open和message又最为重要,里面包括了发送和接收消息。

wsopen: function() {console.log("websocket连接已建立...");var me = app.getUserGlobalInfo();//构建ChatMsgvar chatMsg = new app.ChatMsg(me.id,null,null,null);// 构建DateContentvar dateContent = new app.DataContent(app.CONNECT,chatMsg,null);//发送WebSocketCHAT.chat(JSON.stringify(dateContent));// //每次连接过后,获取用户的未读未签收消息列表fetchUnReadMsg();// //定时发送心跳,要小于后端关闭的时间setInterval("CHAT.keepalive()",10000); },

| | |wsmessage: function(e) {

console.log(“接受消息:”+e.data);

// //转换为DataContent对象var dataContent = JSON.parse(e.data);// var chatMsg = JSON.parse(e.data);//获取action,判断是否为重新拉取好友/var action = dataContent.action;if (action === app.PULL_FRIEND) {fetchContactList();return false;}// //如果不是重新拉取好友列表,则获取聊天消息模型,渲染接收到的聊天记录var chatMsg = dataContent.chatMsg;var msg = chatMsg.msg;var friendUserId = chatMsg.senderId;var myId = chatMsg.receiverId;// // console.log(msg);// // console.log(myId);// // console.log(friendUserId);//调用聊天页面的webview的reciveMsg方法// var chatWebview = plus.webview.getWebviewById("chatting-190810CHXGPYWNHH");var chatWebview = plus.webview.getWebviewById("chatting-" + friendUserId);var isRead = true; //设置消息的默认状态为已读//当要接受的webview不为空的时候才做渲染if (chatWebview != null) {//运用其他webview页面的js脚本//接受消息chatWebview.evalJS("receiveMsg('" + msg + "')");// chatWebview.evalJS("receiveMsg('" + e.data + "')");//重新调整滚动条chatWebview.evalJS("resizeScreen()");} else {isRead = false; //chatWebview 聊天页面没有打开,标记消息为未读}//接受到消息之后,对消息记录进行签收// console.log(chatMsg.msgId);var test = new app.DataContent(app.SIGNED,null,chatMsg.msgId);CHAT.chat(JSON.stringify(test)); //保存聊天历史记录到本地缓存,二代表的是朋友的记录app.saveUserChatHistory(myId,friendUserId,msg,2); // 保存聊天的快照app.saveUserChatSnapshot(myId,friendUserId,msg,isRead);// //渲染快照列表进行展示loadingChatSnapshot();},

这是里面的代码,还有其他代码太多就不贴在上面。

里面注意的是保存聊天记录和快照这两部分,这两个部分非常的重要。

然后就是后端chathandler,主要分为了5个步骤:

1.获取客户端发来的消息,将json转化为对象模型

2.判断消息类型,根据不同的类型来处理不同的业务

2.1 当websocket第一次open的时候,初始化channel,把用的channel和userid关联起来

2.2 聊天类型的消息,把聊天记录保存到数据库,同时标记消息的签收状态[未签收]

2.3 签收消息类型,针对具体的消息进行签收,修改数据库中对应消息的签收状态[已签收]

2.4 心跳类型的消息

里面需要注意的是2.1步骤,我们想要聊天是两个人,所以我们需要将各自的channel和userID关联起来,利用channel来判断对方时在线和离线的状态,在通过好友请求的时候我们也可以通过对方的channel获取对方的信息,好实时更新好友的信息。

这是chathandler的代码:

/*

处理消息的handler

TextWebSocketFrame:在netty中,是用于为websocket专门处理文本的对象 ,frame是消息的载体

*/

public class ChatHandler extends SimpleChannelInboundHandler{

//用户记录和管理所有客户端的channel

public static ChannelGroup users = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

@Override

protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {

// //获取客户端传输过来的消息

// String content = msg.text();

// System.out.println(“接收到的数据:”+content);

//

// for(Channel channel : users){

// channel.writeAndFlush(new TextWebSocketFrame("[服务器在:]"+LocalDateTime.now()+“接收到消息,消息为:”+content));

// }

//下面这个方法,和上面的for循环,一致

// clients.writeAndFlush(new TextWebSocketFrame("[服务器在:]"+LocalDateTime.now()+“接收到消息,消息为:”+content));

// ------------------------------------------------------------------------------------------------------

//获取客户端传输过来的消息String content = msg.text(); //获取channelChannel currentChannel = ctx.channel(); //1.获取客户端发来的消息,将json转化为对象模型 DataContent dataContent = JsonUtils.jsonToPojo(content, DataContent.class);

// System.out.println(dataContent.getAction());

// System.out.println(dataContent.getExtend());

// System.out.println("----------------------");

Integer action = dataContent.getAction(); //2.判断消息类型,根据不同的类型来处理不同的业务if (action == MsgActionEnum.CONNECT.type) {//2.1 当websocket第一次open的时候,初始化channel,把用的channel和userid关联起来String senderId = dataContent.getChatMsg().getSenderId();UserChannelRel.put(senderId, currentChannel); //测试for (Channel c:users) {System.out.println(c.id().asLongText());}//测试UserChannelRel.output();}else if(action == MsgActionEnum.CHAT.type) {//2.2 聊天类型的消息,把聊天记录保存到数据库,同时标记消息的签收状态[未签收]ChatMsg chatMsg = dataContent.getChatMsg();String msgText = chatMsg.getMsg();String receiverId = chatMsg.getReceiverId();String senderId = chatMsg.getSenderId();//保存到数据库,并且标记为未签收//获取userserviceUserService userService = (UserService) SpringUtil.getBean("userServiceImpl");//返回一个msgId,是未签收的状态String msgId = userService.saveMsg(chatMsg); //设置MsgIdchatMsg.setMsgId(msgId); //将chatMsg设置在DataContent中,因为后端接收是Datacontent,所以发送也应该是datacontentDataContent dataContentMsg = new DataContent();dataContentMsg.setChatMsg(chatMsg);//发送消息Channel receiverChannel = UserChannelRel.get(receiverId); if (receiverChannel == null ) {//channel为空代表用户离线,推送消息(JPush,个推,小米推送)}else {//当receiverChannel不为空的时候,从ChannelGroup去查找对应的channel是否存在Channel findChannel = users.find(receiverChannel.id());if (findChannel != null) {//用户在线receiverChannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.objectToJson(dataContentMsg)));

// receiverChannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.objectToJson(chatMsg)));

}else {

//用户离线,推送消息

}}}else if(action == MsgActionEnum.SIGNED.type) {//2.3 签收消息类型,针对具体的消息进行签收,修改数据库中对应消息的签收状态[已签收]//获取userserviceUserService userService = (UserService) SpringUtil.getBean("userServiceImpl");//扩展字段在signed类型的消息中,代表需要去签收的消息id,逗号间隔。String msgIdsStr = dataContent.getExtend();String msgIds[] = msgIdsStr.split(",");

// System.out.println(msgIdsStr + " 0000000");

// System.out.println(msgIds[0] + " 000000000 ");

List<String> msgIdList = new ArrayList<>();for (String mid : msgIds) {if(StringUtils.isNoneBlank(mid)) {msgIdList.add(mid);}}//输出msgIDlistSystem.out.println(msgIdList.toString());//批量签收if (msgIdList != null && !msgIdList.isEmpty() && msgIdList.size() > 0) {userService.updateMsgSigned(msgIdList); }}else if(action == MsgActionEnum.KEEPALIVE.type) {//2.4 心跳类型的消息System.out.println("收到来自channel为[" + currentChannel + "]的心跳包...");}}/** 当客户端连接服务端之后(打开连接)* 获取客户端的channel,并且放到ChannelGroup总进行管理*/@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {users.add(ctx.channel());}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {//当触发handlerRemoved,ChannelGroup会自动移除对应客户端的channel

// clients.remove(ctx.channel());

// System.out.println(“客户端断开,channel对应的长id为:”+ ctx.channel().id().asLongText());

// System.out.println(“客户端断开,channel对应的短id为:”+ ctx.channel().id().asShortText());

String channelId = ctx.channel().id().asShortText();System.out.println("客户端被移除,channelId为:" + channelId);users.remove(ctx.channel());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();//发生异常之后关闭连接(关闭channel),随后从ChannelGroup中移除 ctx.channel().close();users.remove(ctx.channel());}

}

然后就是前后端的数据交互,我们在后端创建了dataContent对象和chatmsg对象,datacontent有动作的类型和聊天记录和扩展字段,扩展字段是用于标记后面未读的消息。然后chatmsg主要保存聊天记录。

后端同样创建了这两个对象。

用js使用的构造函数。将对象利用发送消息传到后端。

总结的可能不是太好如果想要更深一步了解,可以加我qq997355706。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。