WebSocket实现简单聊天功能案例
创始人
2024-03-31 21:20:34
0

简介

使用WebSocket实现的一个简单的聊天功能业务
使用了SpringBoot的ApplicationEvent事件监听用来与业务解耦

一、Maven的引入

	org.springframework.bootspring-boot-starter-websocket2.4.0org.springframework.bootspring-boot-starter-thymeleafcom.alibabafastjsonorg.springframework.bootspring-boot-starter-weborg.projectlomboklombok

二、后端代码结构图

在这里插入图片描述

二(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 webSockets = new CopyOnWriteArraySet<>();/*** 记录当前登录用户的会话*/private static ConcurrentHashMap sessionPool = new ConcurrentHashMap<>();@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId){this.session = session;this.userId = userId;boolean exists = false;Iterator 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



简单聊天室



无痕聊天室😳

在线|送礼物

【😀】: 

【😄】: 

 

四、成功截图

测试的时候只需要在网页中打开两个链接选择不同角色即可

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

相关内容

热门资讯

Adobe 因使用 SlimP... AIPress.com.cn报道 12月29日消息,作为全球创意软件巨头,Adobe 正面临其首起重...
伟星新材:竞争优势明显 保持积... 12月28日,伟星新材(002372)发布公告,伟星新材(002372)于2025年12月25日召开...
健全数据制度 释放乘数效应——... 来源:经济日报 党的二十届四中全会审议通过的《中共中央关于制定国民经济和社会发展第十五个五年规划的建...
海关出口退税律师张严锋:套用其... 2018年3月2日,B稽查局对A公司涉税事项进行检查。经检查,B稽查局认为A公司涉嫌通过套用他人出口...
哈尔滨权威刑事律师服务推荐:谷... 在哈尔滨,当人们遭遇刑事法律问题时,往往会困惑于刑事律师服务哪家权威刑事律师推荐哪些刑事辩护律师哪个...
原创 《... 2025年12月26日,《晋中市平遥牛肉保护和发展条例》新闻发布会在晋中举行。该条例经山西省人大常委...
权威非法吸收公共存款律师推荐:... 在处理非法吸收公共存款这类复杂刑事案件时,选择一位经验丰富、靠谱且性价比高的律师至关重要。今天,我们...
政策领航聚英才 智农创新兴龙江 □刘凌灼 本报记者 姜斌 刘畅 在广袤的北大荒黑土地上,一片农业科技创新的“新沃土”正破土而出。这里...
上海海关商品归类律师张严锋:自... 自动衬环成型绕圆机如何归类 2023年01月01日至2024年05月30日期间,A公司以一般贸易方...
原创 男... 男子举报自己无证驾驶:进去待两天 朋友们,我最近看到一个很火的帖子,真的让我很感动!“男子自首承认无...