简介

1、使用WebSocket实现的一个简单的聊天功能业务
2、使用了SpringBoot的ApplicationEvent事件监听用来与业务解耦
3、需要注意的是websocket的长连接默认会在1分钟后自动关闭,状态码为:1001,正常关闭状态码为:1000
因此客户端要定义一个定时器反复向服务端发送心跳,保持处于活跃状态(本文并未去实现)
4、当前版本使用的是JDK的WebSocket,后面可以改成SpringBoot的WebSocketHandler

一、Maven的引入

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>2.4.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>

二、后端代码结构图

二(1)ApplicationEvent及监听

package com.chat.simplechat.event;import com.chat.simplechat.entity.GiftBean;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;/*** 送礼物事件*/
@Getter
public class GiftEvent extends ApplicationEvent {private GiftBean giftBean;public GiftEvent(Object source,GiftBean giftBean) {super(source);this.giftBean = giftBean;}
}
package com.chat.simplechat.event;import com.alibaba.fastjson.JSONObject;
import com.chat.simplechat.entity.GiftBean;
import com.chat.simplechat.websocket.WebSocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Random;/*** 送礼物事件监听*/
@Slf4j
@Component
public class GiftEventListener {@Resourceprivate WebSocket webSocket;@EventListenerpublic void givingGifts(GiftEvent giftEvent){GiftBean giftBean = giftEvent.getGiftBean();Random random = new Random();String[] str = new String[]{"烟花","跑车","皇冠","凤冠","穿云箭"};int i = random.nextInt(str.length);JSONObject jsonObject = new JSONObject();jsonObject.put("fromUserId",giftBean.getFromUserId());jsonObject.put("toUserId",giftBean.getToUserId());jsonObject.put("contentText",str[i]);webSocket.sendOneMessage(giftBean.getToUserId(),jsonObject.toJSONString());}}

二(2)WebSocket及配置

package com.chat.simplechat.websocket;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;/*** websocket通讯*/
@Slf4j
@Component
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {private Session session;private String userId;/**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/private static int onlineCount = 0;/*** 记录*/private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();/*** 记录当前登录用户的会话*/private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<>();@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId){this.session = session;this.userId = userId;boolean exists = false;Iterator<WebSocket> iterator = webSockets.iterator();log.info("iterator:{}",iterator.hasNext());while (iterator.hasNext()){WebSocket webSocket = iterator.next();if(webSocket.userId.equals(userId)){exists = true;break;}}if(exists){//先删除之前的,在加入现在的this.remove();}webSockets.add(this);sessionPool.put(userId,session);log.info("【WebSocket】用户["+this.userId+"]已上线,当前在线用户数量:"+webSockets.size());this.addOnlineCount();}@OnClosepublic void onClose(){try {this.remove();log.info("【WebSocket】用户["+this.userId+"]已下线,当前在线用户数量:"+webSockets.size());} catch (Exception e) {throw new RuntimeException(e);}}@OnMessagepublic void onMessage(String message){log.info("【WebSocket】收到客户端消息:"+message);if(StringUtils.hasText(message)){//解析发送的报文JSONObject jsonObject = JSON.parseObject(message);//追加发送人(防止串改)jsonObject.put("fromUserId",this.userId);String toUserId = jsonObject.getString("toUserId");Session session = sessionPool.get(toUserId);//传送给对应toUserId用户的websocketif(StringUtils.hasText(toUserId) && null != session){session.getAsyncRemote().sendText(jsonObject.toJSONString());}else{log.error("请求的userid:{}不在该服务器上",toUserId);//否则不在这个服务器上,发送到mysql或者redis}}}@OnErrorpublic void onError(Session session,Throwable throwable){log.error("消息发送错误,原因:{}",throwable.getMessage());}/*** 单点消息单用户发送* @param userId* @param message*/public void sendOneMessage(String userId,String message){try {Session session = sessionPool.get(userId);if(null != session && session.isOpen()){session.getAsyncRemote().sendText(message);log.info("消息发送成功!");}} catch (Exception e) {throw new RuntimeException("消息发送失败:",e);}}/*** 单点消息多用户发送* @param userIds* @param message*/public void sendMoreMessage(String[] userIds,String message){for (String userId : userIds) {try {Session session = sessionPool.get(userId);if(null != session && session.isOpen()){session.getAsyncRemote().sendText(message);}} catch (Exception e) {throw new RuntimeException(e);}}}/*** 广播轮询* @param message*/public void radioBroadcast(String message){for (WebSocket webSocket : webSockets) {try {if(webSocket.session.isOpen()){webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {throw new RuntimeException(e);}}}/*** 消息发送* @param message*/public void sendMessage(String message){this.session.getAsyncRemote().sendText(message);}/*** 移除用户*/private void remove() {webSockets.remove(this);sessionPool.remove(userId);this.subOnlineCount();}public static synchronized int getOnlineCount(){return onlineCount;}public static synchronized void addOnlineCount(){WebSocket.onlineCount++;}public static synchronized void subOnlineCount(){WebSocket.onlineCount--;}
}
package com.chat.simplechat.websocket;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** 启用websocket功能*/
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter(){return new ServerEndpointExporter();}
}

二(3)控制器及实体类

package com.chat.simplechat.controller;import com.chat.simplechat.entity.GiftBean;
import com.chat.simplechat.event.GiftEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** 控制器*/
@Controller
@RequestMapping("/gift")
public class GiftController {@Resourceprivate ApplicationContext applicationContext;@ResponseBody@GetMapping("/randomGift")public String randomGift(GiftBean giftBean){applicationContext.publishEvent(new GiftEvent(this,giftBean));return "成功";}@GetMapping("/page")public String page(){return "websocket/SimpleChat";}
}
package com.chat.simplechat.entity;import lombok.Data;/*** 礼物bean* @author wangyabin* @date 2021/8/24 10:52*/
@Data
public class GiftBean {/*** 发送者ID*/private String fromUserId;/*** 接收者ID*/private String toUserId;
}

三、建立HTML

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><title>简单聊天室</title>
</head>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script><script>$(function(){$("#div").hide();$(".on-line").hide();})var socket;function openSocket() {if(typeof(WebSocket) == "undefined") {console.log("您的浏览器不支持WebSocket");}else{console.log("您的浏览器支持WebSocket");var socketUrl="http://localhost:7523/websocket/"+$("#userId").val();socketUrl=socketUrl.replace("https","ws").replace("http","ws");console.log(socketUrl);if(socket!=null){socket.close();socket=null;}socket = new WebSocket(socketUrl);//打开事件socket.onopen = function() {console.log("websocket已打开");$("#div").show();$(".on-line").show();$("#login").hide();//socket.send("这是来自客户端的消息" + location.href + new Date());};//获得消息事件socket.onmessage = function(msg) {console.log(msg);if(msg.data == "连接成功"){$("#sendMessage").css("color","green");}else{var user = JSON.parse(msg.data);console.log(user);$("#user").text("【"+user.fromUserId+"】的消息:");$("#msg").empty();$("#msg").text(user.contentText);}console.log(msg.data);//发现消息进入    开始处理前端触发逻辑};//关闭事件socket.onclose = function() {console.log("websocket已关闭");};//发生了错误事件socket.onerror = function(e) {console.log(e);console.log("websocket发生了错误");}}}function sendMessage() {var msg = $("#contentText").val();$("#contentText").val("");console.log(msg);if(typeof(WebSocket) == "undefined") {console.log("您的浏览器不支持WebSocket");$("#msg").val("您的浏览器不支持WebSocket");}else {console.log("您的浏览器支持WebSocket");console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+msg+'"}');socket.send('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+msg+'"}');}}//送礼物function givingGifts(){$.ajax({url:'/gift/randomGift',post:'get',data:{"fromUserId":$("#userId").val(),"toUserId":$("#toUserId").val()},success:function(data){console.log(data);},error:function(e){console.log(e);}})}
</script>
<body style="margin: 0;width: 100%;margin: auto">
<div style="margin: 10% 50% 10% 25%;border: 1px solid #cefff4;padding:1% 2% 1% 2%;background-color: #cefff4"><div><p><h1>无痕聊天室												

WebSocket实现简单聊天功能案例相关推荐

  1. WebSocket 实现简单聊天功能

    利用WebSocket长连接实现即使通讯,可以实现个人单独聊天,内容不需要存服务器,这里只有前端代码,后端负责传递消息和用户列表. <template><div><div ...

  2. SpringBoot +WebSocket实现简单聊天室功能实例

    SpringBoot +WebSocket实现简单聊天室功能实例) 一.代码来源 二.依赖下载 三.数据库准备(sql) 数据库建表并插入sql 四.resources文件配置 application ...

  3. 【SpringBoot框架篇】18.使用Netty加websocket实现在线聊天功能

    文章目录 1.简介 2.最终功能实现的效果图 2.1.pc端 2.2.移动端 3.实战应用 3.1.引入依赖 3.2.配置文件 3.3.测试demo 3.3.1.消息内容实体类 3.3.2.处理请求的 ...

  4. php聊天功能_php实现简单聊天功能

    搜索热词 1.创建聊天消息表,其表的字段有消息内容,发送时间和发送者的名称: CREATE TABLE `guanhui`.`message` ( `id` INT(10) NOT NULL AUTO ...

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

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

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

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

  7. Ratchet实现PHP WebSocket多人聊天功能的示例

    composer 安装ratchet composer require cboden/ratchet 使用PDO连接数据库,创建mysql命令如下 CREATE TABLE messages (id ...

  8. 保姆级别 附带源码 Django集成channels(一)实现简单聊天功能

    目录 前言 不想看我瞎BB可以直接跳到这里 1.WebSocket 1.1 ajax轮询 1.2 long poll 1.3 Websocket 2.Channels 2.1 WSGI 2.2 ASG ...

  9. Vue实现简单聊天对话框案例

    功能:实现双人简单聊天对话框,见效果图 代码如下: <!DOCTYPE html> <html lang="en"><head><meta ...

最新文章

  1. 对复杂业务组件在实际开发过程中被调用的反思
  2. 通过Runtime源码了解关联对象的实现
  3. 关于linux内核模块的装载过程
  4. c#操作mysql数据库
  5. php7 对象转数组,php7中为对象/关联数组进行解构赋值
  6. Serverless会使 SaaS 商业模式过时,而开源将成为新的王者
  7. 当 Windows 11 宕机时:从蓝屏死机变成“黑屏死机”!
  8. Android开发的四大组件
  9. 基于visual Studio2013解决C语言竞赛题之1054抽牌游戏
  10. 帆软扩展单元格运算的相关应用
  11. 今日直播预告 | 漏洞审计介绍
  12. LM算法+推导+C++代码实践
  13. anbmcmdn 上下文无关文法_词法分析 | 上下文无关文法和推导
  14. c语言编程温度转换源,c语言编程,将华氏温度转换成摄氏温度。转换公式为:c=5/9 * (f-32),其中f代表华氏温度...
  15. EasyGBS国标视频云服务平台可以获取录像却无法播放是什么原因?
  16. 泡芙噶的计算机网络(3)-扑朔迷离的Cisco Packet Tracer实验
  17. 从键盘输入一个数,判断是否为质数
  18. idea 上传项目到码云git仓库提交到gitee(完整操作流程)
  19. linux系统硬盘设置密码,linux下硬盘加密
  20. PHP云任务Q助手Tools程序源码+多功能

热门文章

  1. 2018年6月计算机一级试题答案,2018年计算机一级考试试题及答案.doc
  2. (xp)不能访问网络位置,有关网络排除故障的信息,请参阅WINDOWS帮助
  3. VLAN三层交换机配置
  4. 浪潮林巍:5G应用技术领先才是5G成功的关键
  5. 【区块链 | EVM】深入理解学习EVM - 深入Solidity数据存储位置
  6. 去掉桌面快捷方式箭头和快捷方式字样--美化桌面快捷方式
  7. Json hijacking/Json劫持漏洞
  8. 乐视网的视频看不了了,说抱歉,你所访问的视频不存在
  9. 蓝库云:建立智慧零售,零代码技术能起到什么作用
  10. 57岁天王20场演唱会计划止步第14场!刘德华泪流满面鞠躬致歉