使用WebSocket实现的一个简单的聊天功能业务
使用了SpringBoot的ApplicationEvent事件监听用来与业务解耦
org.springframework.boot spring-boot-starter-websocket 2.4.0 org.springframework.boot spring-boot-starter-thymeleaf com.alibaba fastjson org.springframework.boot spring-boot-starter-web org.projectlombok lombok

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());}}
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();}
}
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;
}
简单聊天室
无痕聊天室😳
在线|送礼物
【😀】:
【😄】:



