基于SpringBoot搭建聊天室服务端【一对多】
引入依赖
<!-- WebSocket依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
首先肯定是引入我们需要的依赖,将【spring-boot-starter-websocket】依赖先引入pom.xml文件中
编写配置类
创建一个类,名字随意,这里起名WebSocketConfig.java
,在该类中编写我们的wenSocket配置信息。
@Configuration // 声明这是一个配置类
public class WebSocketConfig {
// 为什么要配置这个信息呢?
// 因为:这个Bean会自动注册使用@ServerEndpoint注解声明的websocket
// 后续前后端之间的收发消息都能直接对应的到指定的方法,不需要自己配置地址
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
这点不理解的可以先放一放,看到后面回来重新看即可明白
编写具体webSocket类
创建一个类,名字随意,这里起名:WebSocket
首先,编写该类,有一些需要注意的点:【下面内容可结合代码】
- 类
- 类的注解
- 方法
- 方法的注解
- 方法的参数
- 变量
@Component // 声明这是一个需要被扫描的类
@ServerEndpoint("/webSocket/{uuid}") // 这个注解需要上面配置类的存在才能实现
public class WebSocket {
Logger logger = LoggerFactory.getLogger("WebSocket");
// 统计在线人数的【采用原子性的AtomicInteger,避免多用户同时写入出现线程安全问题】
private static AtomicInteger onlineCount = new AtomicInteger(0);
private static Map<String, WebSocket> loginMap = new ConcurrentHashMap<String, WebSocket>(); // 存储在线人数,与用户的信息
private String uuid; // 用户id
public Session session;
/**
* 打开连接
* @param
* @param session
* @throws IOException
*/
@OnOpen // 在前端申请连接访问 .../webSocket/{uuid}时,就会自动调用该注解的方法
public void onOpen(@PathParam("uuid") String uuid,Session session) throws IOException {
//在这里初始化数据,登记连接人员等操作
this.uuid = uuid;
this.session = session;
logger.info("{} 加入了群聊 ",uuid);
loginMap.put(uuid, this);
addOnlineCount();//本来用统计在线人数的
logger.info("有新的用户加入群聊,目前群聊人数为:{}",onlineCount);
}
/**
* 删除
* @throws IOException
*/
@OnClose // 在前端断开连接后,会自动调用有该注解的方法
public void onClose() throws IOException {
// 用户处理断开连接人员的删除
if (!uuid.equals("")) {
loginMap.remove(uuid);
subOnlineCount(); //本来用统计在线人数的
logger.info(uuid+"退出群聊,目前群聊人数为:"+onlineCount);
}
}
/**
* 错误信息
*/
@OnError //当连接或者webSocket出现问题时,就会调用该注解的方法
public void onError(Session session, Throwable error) {
// 可以在这里进行错误信息打印与错误处理
logger.error("错误信息:"+session);
error.printStackTrace();
}
/**
* 接收数据
* @throws IOException
*/
@OnMessage //当前端调用发送消息方法时,后端该注解的方法就会被调用,并且消息会以参数的方式传入
public void onMessage(String message) throws IOException {
//注意!!!参数为String!!是固定的,如果需要转JSON,就需要进行处理
// 这里通常用于处理心跳或者处理信息
if (message.equals("心一跳")){
logger.info("【{}】发来心跳:{}",uuid,message);
}else {
logger.info("【{}】发来信息:{}",uuid,message);
// 将接收的消息发送给所有人
sendMessageLoginTo(message);
}
}
/**
* 发给所有人
* @param message
* @throws IOException
*/
// 这里是一个自定义的方法,用于发送消息给其他人,注意!!没有注解!!!
public void sendMessageLoginTo(String message) throws IOException {
//session.getBasicRemote().sendText(message); // 同步发送,会阻塞
//session.getAsyncRemote().sendText(message); // 异步发送,不会阻塞
//这里为 将信息发送给!!所有人!!
if (loginMap != null) {
// 遍历目前还连接着的所有人员
for(String key:loginMap.keySet()){
// 拿到他们的WebSocket
WebSocket item = loginMap.get(key);
this.logger.info("发送消息给{}",key);
// 采用异步的方法发送消息 注意!sendText的参数是String类型的!!
item.session.getAsyncRemote().sendText(message);
}
}
}
/**
* 发给指定一个人
* @param message
* @throws IOException
*/
// 这里是一个自定义的方法,用于发送消息给其他人,注意!!没有注解!!!
public void sendMessageToOne(String message,String uid) throws IOException {
//这里为 将信息发送给!!指定一个人!!
if (loginMap != null) {
// 遍历目前还连接着的所有人员
for(String key:loginMap.keySet()){
if(key==uid){
// 这里循环到了指定用户
WebSocket item = loginMap.get(key);
this.logger.info("发送消息给{}",key);
// 采用异步发送
item.session.getAsyncRemote().sendText(message);
}
// 没有指定对象,可进行另外的逻辑编写
}
}
}
// 增加在线人数,因为涉及到多线程问题,这里采用原子性的AtomicInteger进行记录
public static void addOnlineCount() {
WebSocket.onlineCount.getAndIncrement();
}
// 减少在线人数
public static void subOnlineCount() {
WebSocket.onlineCount.getAndDecrement();
}
// 返回目前在线人数的所有信息
public static synchronized Map<String, WebSocket> getLoginMap() {
return loginMap;
}
}
后续将会更新前端部分的代码解析,如有需要webSocket理论的解读,可在评论区提出,我会尽快提上日程,满足各位大伙的知识需求
到此,聊天室的服务端基本功能就搭建完毕了,如果有疑问的地方,可以在评论进行提问。