java SSM项目中用WebSocket 实现聊天功能

前言

当我们开发的javaweb项目中需要实现聊天功能时,可以用HTML5的WebSocket协议来实现。在实现功能之前,先来看一下WebSocket的基本知识,加深对它的认识后面才能更好的理解。

(一) WebSocket

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。(相比于其他推送技术,WebSocket 协议能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。)


为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,附加信息如图所示(看看就行):

(二) 连接过程

浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

当获取 Web Socket 的连接后,就可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

连接语法:var Socket = new WebSocket(url, [protocol] );
url是必须的,指定连接的url。 protocol是可选的,代表可接受的子协议。

(三) WebSocket的属性、事件、方法

1、WebSocket的属性

属性 描述
Socket.readyState 只读属性 readyState 表示连接状态,可以是以下值:
0 - 表示连接尚未建立。
1 - 表示连接已建立,可以进行通信。
2 - 表示连接正在进行关闭。
3 - 表示连接已经关闭或者连接不能打开。

2、WebSocket的事件(这里的Socket.事件名的Socket是js中new WebSocket(url)创建的对象)

事件 事件处理程序(写在js中) 描述
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

3、WebSocket的方法

方法 描述
Socket.send() 使用连接发送数据
Socket.close() 关闭连接

正文

好了,看到这里,对WebSocket也有了一定的了解。接下来,开始用WebSocket实现聊天。

开发环境:Eclipse、java version “10.0.2”
开发语言:java、javascript、html
框架:ssm框架 maven本地仓库
开发目标:实现用户之间的及时通讯(不包含数据库)

开发步骤
1、首先引入WebSocket的库文件

在maven的pom文件中引入依赖(如果没用maven,则需要自己去网上下载这个jar文件):

 <dependency><groupId>org.springframework</groupId><artifactId>spring-websocket</artifactId><version>${spring.version}</version></dependency>

2、设计思想

用户通过账号密码登录,登录之后通过WebSocket的onopen事件自动连接(相当于进入聊天室)。后台通过Map来记录用户连接情况。
进入聊天室之后,选择聊天对象,在input标签中输入文字,然后点击发送按钮,js响应按钮点击事件,把有关的各种JSON信息转换成字符串通过WebSocket的send()方法发送到后台服务器。
服务器接收到用户发送来的信息,解析之后转发给另一个用户,另一个用户通过WebSocket的onmessage事件接收消息。
至此,聊天功能实现。

3、后台代码(本人第一次编写WebSocket代码,所以直接写在Controller层了)

package cn.sx.handler;
/**作者:Ji Pengjie*日期:2021年5月3日*时间:下午4:10:05
**/
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;import javax.json.Json;
import javax.json.JsonObject;
import javax.security.auth.message.callback.PrivateKeyCallback.Request;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.socket.server.standard.SpringConfigurator;import com.alibaba.fastjson.JSONObject;//websocket连接URL地址和可被调用配置 @ServerEndpoint这个注解用来标记一个类是 WebSocket 的处理器。
@ServerEndpoint(value="/websocketDemo/{fromId}",configurator = SpringConfigurator.class)
public class WebsocketDemo {//日志记录      private Logger logger = LoggerFactory.getLogger(WebsocketDemo.class);//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。private static int onlineCount = 0;//记录每个用户下多个终端的连接public static Map<String, Set<WebsocketDemo>> userSocket = new HashMap<>();//需要session来对用户发送数据, 获取连接特征fromIdprivate Session session;private String fromId;private String toId;/*** @Title: onOpen* @Description: websocekt连接建立时的操作* @param @param fromId 用户id* @param @param session websocket连接的session属性* @param @throws IOException*/@OnOpenpublic void onOpen(@PathParam("fromId") String fromId, Session session) throws IOException{this.session = session;this.fromId = fromId;onlineCount++;//根据该用户当前是否已经在别的终端登录进行添加操作if (userSocket.containsKey(this.fromId)) {logger.debug("当前用户id:{}已有其他终端登录",this.fromId);userSocket.get(this.fromId).add(this); //增加该用户set中的连接实例}else {logger.debug("当前用户id:{}第一个终端登录",this.fromId);Set<WebsocketDemo> addUserSet = new HashSet<>();addUserSet.add(this);userSocket.put(this.fromId, addUserSet);}logger.debug("用户{}登录的终端个数是为{}",fromId,userSocket.get(this.fromId).size());logger.debug("当前在线用户数为:{},所有终端个数为:{}",userSocket.size(),onlineCount);}/*** @Title: onClose* @Description: 连接关闭的操作*/@OnClosepublic void onClose(){//移除当前用户终端登录的websocket信息,如果该用户的所有终端都下线了,则删除该用户的记录if (userSocket.get(this.fromId).size() == 0) {userSocket.remove(this.fromId);}else{userSocket.get(this.fromId).remove(this);}logger.debug("用户{}退出连接!",this.fromId);logger.debug("用户{}登录的终端个数是为{}",this.fromId,userSocket.get(this.fromId).size());userSocket.remove(this.fromId);logger.debug("当前在线用户数为:{},所有终端个数为:{}",userSocket.size(),--onlineCount);}/*** @Title: onMessage* @Description: 收到消息后的操作* @param @param message 收到的消息* @param @param session 该连接的session属性*/@OnMessagepublic void onMessage(String message, Session session) {    logger.debug("收到来自用户id为:{}的消息:{}",this.fromId,message);JSONObject jsonObject = JSONObject.parseObject(message);//将前台转来的字符串转为json对象this.toId = jsonObject.getString("toId");if(userSocket.containsKey(toId)) {sendMessageToUser(toId, message);}else {logger.debug("用户{}不在线!", toId);}if(session ==null)  logger.debug("session null");}/*** @Title: onError* @Description: 连接发生错误时候的操作* @param @param session 该连接的session* @param @param error 发生的错误*/@OnErrorpublic void onError(Session session, Throwable error){logger.debug("用户id为:{}的连接发送错误",this.fromId);error.printStackTrace();}/*** @Title: sendMessageToUser* @Description: 发送消息给用户下的所有终端* @param @param fromId 用户id* @param @param message 发送的消息* @param @return 发送成功返回true,反则返回false*/public Boolean sendMessageToUser(String toId,String message){if (userSocket.containsKey(toId)) {logger.debug(" 给用户id为:{}的所有终端发送消息:{}",toId,message);for (WebsocketDemo WS : userSocket.get(toId)) {logger.debug("sessionId为:{}",WS.session.getId());try {WS.session.getBasicRemote().sendText(message);//将消息发送给对方 getBasicRemote().sendText(message) 同步发送} catch (IOException e) {e.printStackTrace();logger.debug(" 给用户id为:{}发送消息失败",toId);return false;}}return true;}logger.debug("发送错误:当前连接不包含id为:{}的用户",toId);return false;}
}

解释:
声明websocket地址类似Spring MVC中的@controller注解类似,websocket使用@ServerEndpoint来进行声明接口:@ServerEndpoint(value="/websocket/{paraName}") ; 其中 “ { } ”用来表示带参数的连接,如果需要获取{}中的参数,在接收参数时使用注解:@PathParam(“paraName”)

4、前端连接代码

<div id="body1" ><div class="page-header" id="tou">webSocket多终端聊天测试</div><div class="well" id="msg"></div><div class="col-lg"><div class="input-group"><textarea class="form-control" placeholder="发送信息..." id="message"></textarea><!--  <input type="text" class="form-control" placeholder="发送信息..." id="message"> --></div><div class="form-control1"><span class="float_r"><button class="span_b" type="button" id="send">发送</button><button class="span_b" type="button" id="close">关闭</button></span></div></div></div><script type="text/javascript">var tid;//全局变量var status = 0;//接收消息提醒标志,初始化为0,当收到消息并查看时设置为1//jquery代码当页面DOM树加载完毕之后触发$(function() {var websocket;//创建对象,用户连接WebSocketif('WebSocket' in window) {console.log("此浏览器支持websocket");websocket = new WebSocket("ws://127.0.0.1:3311/recruitment/websocketDemo/${session.username}");} else if('MozWebSocket' in window) {alert("此浏览器只支持MozWebSocket");} else {alert("此浏览器只支持SockJS");}//连接之后,WebSocket的事件websocket.onopen = function(evnt) {$("#msg_a").css("pointer-events","none");  //阻止点击产生效果 /* var str = location.href;返回当前显示的文档的完整 URL。if(str.split('?')[1]){tid = str.split('?')[1].split('=')[1];//分割字符串,返回字符串数组$("#tou").html("链接通道成功!开始与用户" + tid + "聊天!")} */};//接收消息的WebSocket事件websocket.onmessage = function(evnt) {var message = JSON.parse(evnt.data);//将数据解析成JSON形式var fromId = message.fromId;//对方用户的账号var toId = message.toId;//自己的账号var time = new Date(message.date);//对方发送信息时间var timeStr = time.toLocaleTimeString();if(status == 0){//这个可以不用实现也行alert("收到来自用户 " + fromId + "的消息")var msgg = document.getElementById("img_a");msgg.src = "./imgs/msg2.png";var msg1 = document.getElementById("msg_a");msg1.href = "#?id=" + fromId;$("#msg_a").css("pointer-events","auto");  //阻止a标签点击产生效果 }var text = message.text;//获取对方发送来的文本消息$("#msg").html($("#msg").html() + "<br/>" + timeStr + "   "+ fromId + "   " + "<br/>" +  text);//将信息显示在div页面scroll();};//发生错误时,WebSocket的处理事件websocket.onerror = function(evnt) {};//关闭连接时,WebSocket的处理事件websocket.onclose = function(evnt) {$("#tou").html("与服务器断开了链接!")};$(".imga").click(function (){ status = 1;});//点击发送按钮,响应。$('#send').bind('click', function() {var str = location.href;返回当前显示的文档的完整 URL,里面包含着要发送给谁(对方)的idif(str.split('?')[1]){tid = str.split('?')[1].split('=')[1];//分割字符串,返回字符串数组//把自己的username写进去,然后调用send自定义js函数send('${session.username}');$("#tou").html("链接通道成功!开始与用户" + tid + "聊天!")}});function send(id) {if(websocket != null) {//json字符串var msg = {fromId: id,//自己的idtext: document.getElementById("message").value,//自己要发送的消息toId: tid,//对方的iddate: Date.now()};/*  var message = document.getElementById('message').value; *///调用WebSocket的send()方法,发送数据给服务器websocket.send(JSON.stringify(msg));//把json数组转换成字符串传到后台var div = document.getElementById('msg');//把自己发送的信息显示在div上div.innerHTML = div.innerHTML +  "<br/>" +new Date(msg.date).toLocaleTimeString() + "  " + msg.fromId + "  " + "<br/>" + msg.text;document.getElementById('message').value = "";scroll();} else {alert('未与服务器链接.');}}$("#close").click(function (){ status = 0;var msg = document.getElementById("img_a");msg.src = "./imgs/msg1.png";location.href = location.href.split('#')[0];//设置当前页面的url href地址$("#msg_a").css("pointer-events","none");  //阻止点击产生效果 });//点击退出按钮,退出当前账号,并关闭连接$("#exit").click(function (){ close();});function close() {websocket.close();}});
</script>

解释:
websocket = new WebSocket(“ws://127.0.0.1:3311/recruitment/websocketDemo/${session.username}”);
ws://本机地址:端口号/项目名/Websocket的处理器/{参数}

最后两个参数 要和WebSocket处理器注解@ServerEndpoint中的value值一样,参数直接传值就行,多个参数可以用/分割开

5、实现界面

首先登录

登录之后日志


开始发送消息

另一边收到消息通知


参考:
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications#closing_the_connection
https://www.jianshu.com/p/3398d0230e5f
https://www.runoob.com/html/html5-websocket.html

WebSocket 实现聊天功能相关推荐

  1. 基于java 的websocket的聊天功能,一开始初始化websocket,执行打开连接之后就直接关闭连接了。...

    1 错误描述: java 后台没有报错,但是就是连接不上,一连上又自动关闭. 2 错误根源: 缺少jar包. 对比了报错的tomcat 的jar包和不报错的jar包 发现是tomcat下缺少上图绿色框 ...

  2. 微信小程序WebSocket实现聊天对话功能完整源码

    相关文章: 1.小程序聊天群,发送语音,文字,图片. 2.微信小程序集成腾讯IM,实现实时音视频通话,1V1聊天 3.云开发微信小程序聊天群 4.接入网易云信IM即时通讯的微信小程序聊天室 5.微信小 ...

  3. tornado实现基于websocket的好友一对一聊天功能

    做项目的时候涉及到即时通信了,所以在 gladuo 的建议下看了一篇教程,[转]Tornado 搭建基于 WebSocket 的聊天服务,经过一番修改调试实现了功能,在此总结分享一下. 按思路来聊: ...

  4. JavaWeb--使用Websocket实现在线聊天功能

    首先简单介绍下WebSocket,WebSocket是HTML5中内容,是基于TCP的一种新的网络协议,它支持全双工.长连接的通信.在它出现之前,实时消息发送与接收通过轮询实现,但是频繁与服务器建立连 ...

  5. WebSocket(3)---实现一对一聊天功能

    实现一对一聊天功能 功能介绍:实现A和B单独聊天功能,即A发消息给B只能B接收,同样B向A发消息只能A接收. 本篇博客是在上一遍基础上搭建,上一篇博客地址:[WebSocket]---实现游戏公告功能 ...

  6. 微信小程序 | 基于小程序+Java+WebSocket实现实时聊天功能

    一.文章前言 此文主要实现在小程序内聊天对话功能,使用Java作为后端语言进行支持,界面友好,开发简单. 二.开发流程及工具准备 2.1.注册微信公众平台账号. 2.2.下载安装IntelliJ ID ...

  7. 习惯了微信聊天,利用WebSocket手动实现个聊天功能怎么样?

    1.背景 基于项目需求,最近需要实现一个简单的聊天功能.日常生活中,大家对于聊天也习以为常,微信.QQ等软件也经常用到,其实我们也可以引入一些第三方的sdk包等去实现,也可以利用WebSocket通信 ...

  8. WebSocket消息推送和聊天功能实现

    WebSocket消息推送 SpringBoot集成WebSocket实现消息推送和聊天Demo gradle引入依赖 测试用的Controller 两个测试页面 WebSocket的Endpoint ...

  9. JSP使用Websocket技术实现聊天功能--H5网页前端部分(二)

    [注]:使用的是LayUI作为前端框架,实现网页聊天功能,部分包可能出现版本更迭无法使用 前端页面 <%--Created by IntelliJ IDEA.User: ZhuDiDate: 2 ...

  10. 微信小程序实现websocket及单人聊天功能

    一.什么是websocket: WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议) 它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到 ...

最新文章

  1. php获取ios或android通过文件头(header)传过来的坐标,通过百度接口获取具体城市和地址,并存入到session中...
  2. 在Windows下搭建SVN服务器并且集成到 Eclipse 开发环境中
  3. Repeater 绑定下拉列表
  4. BGP——community团体属性(讲解+配置)@
  5. 4.3.2 用jQuery进行异步加载(2)
  6. java实验 输入输出_实验十三 Java输入输出(一)
  7. java request 封装对象_java request请求参数直接封装model对象
  8. jms 教程_JMS教程–什么是JMS
  9. 数据-第11课-双向链表
  10. 小米网卡驱动_小米是什么?(上)
  11. android pickerview 多行,Android-PickerView系列之介绍与使用篇(一)
  12. 人工智能数学基础--概率与统计8:一个很有意思的下棋输赢概率问题
  13. 安卓桌面软件_Windows启动器v8.34安卓用上win桌面
  14. Fibonacci法与黄金分割法
  15. 使用 IntraWeb (22) - 基本控件之 TIWCalendar
  16. 风影墙纸,一天看N回。
  17. Linux - 可视化菜单界面设计
  18. 逆波兰式的转换与计算(简单)
  19. 联想电脑计算机无法正常启动怎么办,电脑蓝屏无法启动怎么办
  20. 10份可直接套用的华为项目管理模板

热门文章

  1. 计算机中计算器怎么算log,log换算(log计算器在线)
  2. 4-2 jmu-java-m04-Person、Teacher与Student (10分)
  3. 【论文精读】Dual-Feature Warping-based Motion Model Estimation
  4. 全国最大直男论坛的性感女神翻车了,网友:乔碧萝第二
  5. 搭建Hadoop VM集群
  6. linux共享文件夹显示不出来,vmtools设置共享文件夹在linux的mnt/hgfs下却看不到文件夹的问题...
  7. 转型只争朝夕!又一火电企业成立新能源公司
  8. 电脑开机没反应怎么办?
  9. MAC 使用技巧总结
  10. 数据库基础知识(1)--数据库php连接