每当一个用户使用websocket建立连接时,都会存放一个连接对象(在connectMap集合存放,键为sessionId,值为该连接对象),每次当用户发送一条数据时,都会遍历connectMap中所有的连接对象,然后进行广播发送消息,接收消息也是,谁的消息都收,来之不拒,所以此为群聊。

单聊的话,要改变下面几个地方。
1,存放connectMap连接对象时,值要为userId(唯一标识)。
2,使用websocket建立连接时要把自己的userId(发送人)和对方的userId(接收人)传到后台,因为发消息时,根据userId得到连接对象,并使用连接对象发消息到对方的窗口和自己的窗口。
3,在js获取消息的地方要判断该消息的发送人userId是否和初始化时的接收人userId一致,如果一致的话则将消息添加到自己的窗口进行显示,如果不一致则不显示。
有一个地方需要提一下,比如要给A用户发送消息,就要根据A的userId得到连接对象,然后就会把消息发送给A用户。演示:ym.fzapp.top
js代码(html忽略)此为用户点击在线客服按钮进入聊天框页面就执行的js代码。
说明:下面接收人id写死了为超级管理员,我这里省事情了,因为超级管理员的账号(也就是唯一标识)就是超级管理员这五个字,当真正开发时就要动态获取唯一标识了!
js代码- var name="";
- $(function () {
- $.post(
- "/demo/getName",
- function (data) {
- name=data.name;
- },
- "json"
- );
- //得到未读消息并展示到列表中(当点击其中一个未读消息时会得到该消息的账号(前台用户的userId唯一标识))
- $.post(
- "/demo/getweidu",
- function (data) {
- var temp='';
- var count=0;
- var account='';
- for(var i=0;i
- temp+='')">\n' +
- ' '+data[i].account+'\n' +
- ' '" class="weidu">'+data[i].count+'\n' +
- ' ';
- }
- $("#nav").append(temp);
- }
- );
- });
-
- //这是点击对应的未读消息之后将未读消息展示到客服的聊天框
- var sb=null;
- function liaotian(v) { //下面的websocket内容在这个方法里面,执行这个点击事件之后触发websocket连接
- sb=v;
- $("#content").empty();
- $("#"+v).hide();
- $.post(
- "/demo/getxiaoxi",
- {account:v},
- function (data) {
- // alert(JSON.stringify(data));
- var str='';
- for(var i=0;i
- var time=data[i].addtime.slice(0,10);
- str+=' ';
- }
- $("#content").append(str);
- },
- "json"
- );
- //将点击后的未读消息改为已读消息
- $.post(
- "/demo/changeYD",
- {account:v},
- function (data) {
- console.log(data);
- }
- );
- //发送人id(超级管理员写死的,上面说过了)
- var wsUrl="ws://127.0.0.1:8082/charRoomServer";
- var allUrl=wsUrl+"/"+"超级管理员";
- //客户端与服务端建立连接,建立连接以后,它会发出一个ws.open事件
- var ws=new WebSocket(allUrl);
- //接收人userId
- var jsrid=sb;
- //连接成功后,提示浏览器客户端输入名称
- ws.onopen=function(){
- ws.send(jsrid);
- }
- //客户端收到服务端发送的消息
- ws.onmessage=function(message){
- //截取到普通用户的手机号
- var shenfen=JSON.stringify(message.data).toString();
- var shenfens=shenfen.slice(shenfen.lastIndexOf("|")+1,-1);
- console.log("身份:"+shenfens);
- // //判断发送人id是否和页面初始化时的接收人id(jsrid)是否一致,相同则说明是对方回复的消息
- if(jsrid==shenfens||"超级管理员"==shenfens){
- //获取以后,在客户端显示
- content.innerHTML+=message.data;
- }else{
- //不做任何操作
- }
- }
- //获取某个用户输入的聊天内容,并发送到服务端
- function getMessage(){
- var inputMessage=document.getElementById("inputMessage").value;
- //alert(inputMessage);
- //获取消息以后,要发送给服务端,然后广播给所有用户
- if(typeof(inputMessage)=='undefined'){
- alert("请输入您要发送的消息!");
- }else{
- ws.send(inputMessage);
- //输入框消息清空
- inputMessage.value="";
- }
- }
- //当关闭页面或者用户退出时,会执行一个ws.close()方法
- window.onbeforeunload=function(){
- ws.close();
- }
- //按回车发送信息
- document.onkeyup=function(e){
- if(e.keyCode==13){
- getMessage();
- inputMessage.value="";
- }
- }
- }
-
3,最后为服务端的websocket实例
package com.qianlong.controller;-
- import com.qianlong.service.SelectService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
-
- import java.io.IOException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- import javax.websocket.OnClose;
- import javax.websocket.OnMessage;
- import javax.websocket.OnOpen;
- import javax.websocket.Session;
- import javax.websocket.server.PathParam;
- import javax.websocket.server.ServerEndpoint;
- /**
- * 聊天室的服务端程序
- * @author Administrator
- *
- */
- //声明websocket某个服务端的地址
- @ServerEndpoint(value = "/charRoomServer/{param}")
- @Component
- public class ChatRoomServer {
- @Autowired
- public static SelectService selectService;
- private boolean firstFlag=true;
- private Session session;
- private String userName;
-
- //发送人id
- private String userId;
- //key代表此次客户端的userId,value代表此次连接对象
- private static final HashMap
connectMap=new HashMap(); - //保存所有用户昵称信息
- //key是session的id,value是用户昵称
- private static final HashMap
userMap=new HashMap(); - //服务端收到客户端的连接请求,连接成功后会执行此方法
- @OnOpen
- public void start(@PathParam(value = "param") String param, Session session) {
- this.session=session;
- this.userId=param; //接收参数
- connectMap.put(param,this);
- }
-
- //客户端发来的信息,服务端接收
- @OnMessage //接收人userId
- public void chat(String clientMessage,Session session) {
- //firstFlag为true是第一次进入,第二次进入之后设为false
- ChatRoomServer client=null;
- if(firstFlag) {
- this.userName=clientMessage;
- //将新进来的用户保存到用户map
- userMap.put(session.getId(), userName);
-
- try {
- if("超级管理员".equals(userId)){
-
- }else{
- //构造发送给客户端的提示信息
- String message=htmlMessage("大白机器人:","亲爱的"+userId+",您想了解点儿啥?");
- client=(ChatRoomServer) connectMap.get(userId);
- //给对应的web端发送一个文本信息
- client.session.getBasicRemote().sendText(message);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- firstFlag=false;
- }else {
- System.err.println("clientMessage:"+userName);
- //给对方发消息
- String message1=htmlMessage(userId,clientMessage);
- client = (ChatRoomServer) connectMap.get(userName);
- if(client!=null){
- try {
- client.session.getBasicRemote().sendText(message1);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- //给自己窗口发消息
- String message2=htmlMessage(userId,clientMessage);
- client = (ChatRoomServer) connectMap.get(userId);
- try {
- client.session.getBasicRemote().sendText(message2);
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- //这是将前台用户发送的消息存数据库并标记为未读,和上面通信没关系
- if("超级管理员".equals(userId)){
-
- }else{
- Map map=new HashMap();
- map.put("account",userId);
- map.put("message",clientMessage);
- map.put("addtime",new Date());
- int i = selectService.chatInsert(map);
- System.out.println(i);
- }
- }
- }
-
- /**
- * 前台js的ws.close事件,会触发后台的标注onClose的方法
- */
- @OnClose
- public void close() {
- userMap.remove(session.getId());
- connectMap.remove(userId);
- }
- /**
- * 渲染页面,把信息构造好标签再发送
- */
- public String htmlMessage(String userName,String message) {
- StringBuffer stringBuffer=new StringBuffer();
- SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- stringBuffer.append("
" ); - stringBuffer.append(""+sf.format(new Date())+"");
- stringBuffer.append("");
- stringBuffer.append("
"
+userName+""); - stringBuffer.append("");
- stringBuffer.append("");
- stringBuffer.append("");
- stringBuffer.append(""+message+"");
- stringBuffer.append("");
- stringBuffer.append("");
- //这里拼接了消息发送人的userId,在前台进行截取字符串接收发送人的userId
- stringBuffer.append("|"+userName);
- return stringBuffer.toString();
- }
- }
三,依赖注入
在websocket中进行依赖注入service并调用service方法进行数据库存储,如果按常规的方式是走不通的。
解决方式:
在该springboot项目中添加一个WebsocketConfig配置类,对service进行配置。
@Configuration- public class WebSocketConfig {
- /**
- * ServerEndpointExporter 用于扫描和注册所有携带 ServerEndPoint 注解的实例,
- * 若部署到外部容器 则无需提供此类。
- */
- @Bean
- public ServerEndpointExporter serverEndpointExporter() {
- return new ServerEndpointExporter();
- }
-
- /**
- * 因 SpringBoot WebSocket 对每个客户端连接都会创建一个 WebSocketServer(@ServerEndpoint 注解对应的对象,Bean 注入操作会被直接略过,因而手动注入一个全局变量
- */
- @Autowired
- public void setSelectService(SelectService selectService) {
- ChatRoomServer.selectService = selectService;
- }
- }
然后在websocket中注入service。
四,效果展示
进到聊天框中(左下角的用户名忽略,因为上面通过截取字符串得到的发送人userId,所以这里多了一个userId)
登录之后会提示未读消息的信息。
点击客服管理,进入客服聊天平台,并显示消息未读数。
这是给123456用户发送的消息,其他用户也是收不到的。
查看123456用户的聊天框内容,可以收到客服的消息,然后该用户可以和客服实现在线单聊。
本次websocket讲解到此结束,谢谢观赏!
-
相关阅读:
web前端——简单的网页布局案列
JVM判断对象是否存活之引用计数法、可达性分析
用 Go 快速开发一个 RESTful API 服务
java计算机毕业设计文物管理系统源程序+mysql+系统+lw文档+远程调试
神经网络系列---损失函数
matlab使用NCL提供的colormap
初次接触氛围系统架构,聊聊我这三个月的理解
Java系列之:var关键字
CVPR 2024 截稿时间
[JSOI2007] 建筑抢修
-
原文地址:https://blog.csdn.net/q906270629/article/details/126248811