WebSocket实现简单聊天功能案例
简介
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实现简单聊天功能案例相关推荐
- WebSocket 实现简单聊天功能
利用WebSocket长连接实现即使通讯,可以实现个人单独聊天,内容不需要存服务器,这里只有前端代码,后端负责传递消息和用户列表. <template><div><div ...
- SpringBoot +WebSocket实现简单聊天室功能实例
SpringBoot +WebSocket实现简单聊天室功能实例) 一.代码来源 二.依赖下载 三.数据库准备(sql) 数据库建表并插入sql 四.resources文件配置 application ...
- 【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.处理请求的 ...
- php聊天功能_php实现简单聊天功能
搜索热词 1.创建聊天消息表,其表的字段有消息内容,发送时间和发送者的名称: CREATE TABLE `guanhui`.`message` ( `id` INT(10) NOT NULL AUTO ...
- JavaWeb--使用Websocket实现在线聊天功能
首先简单介绍下WebSocket,WebSocket是HTML5中内容,是基于TCP的一种新的网络协议,它支持全双工.长连接的通信.在它出现之前,实时消息发送与接收通过轮询实现,但是频繁与服务器建立连 ...
- 微信小程序 | 基于小程序+Java+WebSocket实现实时聊天功能
一.文章前言 此文主要实现在小程序内聊天对话功能,使用Java作为后端语言进行支持,界面友好,开发简单. 二.开发流程及工具准备 2.1.注册微信公众平台账号. 2.2.下载安装IntelliJ ID ...
- Ratchet实现PHP WebSocket多人聊天功能的示例
composer 安装ratchet composer require cboden/ratchet 使用PDO连接数据库,创建mysql命令如下 CREATE TABLE messages (id ...
- 保姆级别 附带源码 Django集成channels(一)实现简单聊天功能
目录 前言 不想看我瞎BB可以直接跳到这里 1.WebSocket 1.1 ajax轮询 1.2 long poll 1.3 Websocket 2.Channels 2.1 WSGI 2.2 ASG ...
- Vue实现简单聊天对话框案例
功能:实现双人简单聊天对话框,见效果图 代码如下: <!DOCTYPE html> <html lang="en"><head><meta ...
最新文章
- 对复杂业务组件在实际开发过程中被调用的反思
- 通过Runtime源码了解关联对象的实现
- 关于linux内核模块的装载过程
- c#操作mysql数据库
- php7 对象转数组,php7中为对象/关联数组进行解构赋值
- Serverless会使 SaaS 商业模式过时,而开源将成为新的王者
- 当 Windows 11 宕机时:从蓝屏死机变成“黑屏死机”!
- Android开发的四大组件
- 基于visual Studio2013解决C语言竞赛题之1054抽牌游戏
- 帆软扩展单元格运算的相关应用
- 今日直播预告 | 漏洞审计介绍
- LM算法+推导+C++代码实践
- anbmcmdn 上下文无关文法_词法分析 | 上下文无关文法和推导
- c语言编程温度转换源,c语言编程,将华氏温度转换成摄氏温度。转换公式为:c=5/9 * (f-32),其中f代表华氏温度...
- EasyGBS国标视频云服务平台可以获取录像却无法播放是什么原因?
- 泡芙噶的计算机网络(3)-扑朔迷离的Cisco Packet Tracer实验
- 从键盘输入一个数,判断是否为质数
- idea 上传项目到码云git仓库提交到gitee(完整操作流程)
- linux系统硬盘设置密码,linux下硬盘加密
- PHP云任务Q助手Tools程序源码+多功能
热门文章
- 2018年6月计算机一级试题答案,2018年计算机一级考试试题及答案.doc
- (xp)不能访问网络位置,有关网络排除故障的信息,请参阅WINDOWS帮助
- VLAN三层交换机配置
- 浪潮林巍:5G应用技术领先才是5G成功的关键
- 【区块链 | EVM】深入理解学习EVM - 深入Solidity数据存储位置
- 去掉桌面快捷方式箭头和快捷方式字样--美化桌面快捷方式
- Json hijacking/Json劫持漏洞
- 乐视网的视频看不了了,说抱歉,你所访问的视频不存在
- 蓝库云:建立智慧零售,零代码技术能起到什么作用
- 57岁天王20场演唱会计划止步第14场!刘德华泪流满面鞠躬致歉