SpringBoot+Mybatis-Plus使用webSocket实现一对一聊天

说在前面的话:本不应该聊天记录存入数据库,奈何小弟学艺不精,为了实现离线消息的推送才出此下策,只是为了学习,只为了学习!!!!

一、WebSocket

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

二、代码实现
1、 websocket依赖
[Java] 纯文本查看 复制代码<!–websocket依赖–>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2、WebSocketConfig配置类
[Java] 纯文本查看 复制代码import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
*
* @description:WebSocketConfig配置类,
* 注入对象ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
*
* @author 火烛
* @since 2020-9-11
*/

@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}

接下来就是重点了
3、一对一聊天
[Java] 纯文本查看 复制代码import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.medical.health.MyUtils.MapUnite;
import com.medical.health.MyUtils.MyUtils;
import com.medical.health.entity.Message;
import com.medical.health.entity.Users;
import com.medical.health.service.impl.MessageServiceImpl;
import com.medical.health.service.impl.UsersServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* @description:一对一聊天
* 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*
* @author 火烛
* @since 2020-9-17
*/

@RestController
@ServerEndpoint(value = "/webSocketOneToOne/{userId}")
public class WebSocketOneToOne {

// 这里使用静态,让 service 属于类
private static UsersServiceImpl userService;
// 注入的时候,给类的 service 注入
@Autowired
public void setUserService(UsersServiceImpl userService) {
WebSocketOneToOne.userService = userService;
}
private static MessageServiceImpl messageService;
@Autowired
public void setChatMsgService(MessageServiceImpl messageService) {
WebSocketOneToOne.messageService = messageService;
}
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount;
//实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key为用户标识
private static final Map<String,Session> connections = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private String sessionId;
private String sendId;

/**
* 连接建立成功调用的方法
*
* @param session
* 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(@PathParam("userId") String userId, Session session) {
this.sessionId = session.getId();
this.sendId = userId;
connections.put(sendId,session); //添加到map中
addOnlineCount(); //在线数加
System.out.println(userId);
System.out.println("————–连接————-" + this.sessionId);
System.out.println("有新连接加入!新用户:"+sendId+",当前在线人数为" + getOnlineCount());
List<Message> messageList = messageService.queryByType(sendId);
for (Message message : messageList){
downSend(message.getContent(), message.getSendId(), message.getReceiveId(), message.getType(), sessionId);
}
}

/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
Users users = new Users();
users.setId(sendId);
users.setDownTime(String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8))));
userService.updateById(users);
connections.remove(sendId); // 从map中移除
subOnlineCount(); // 在线数减
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());

}

/**
* 收到客户端消息后调用的方法
*
* @param message
* 客户端发送过来的消息
* @param session
* 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("————–接收消息————-" + session.getId());
System.out.println("来自客户端的消息:" + message);
JSONObject json= JSON.parseObject(message);
String msg = (String) json.get("message"); //需要发送的信息
String receiveId = (String) json.get("receiveId"); //发送对象的用户标识(接收者)
String method = (String) json.get("method"); //发送对象的用户标识(接收者)
send(msg, sendId, receiveId, method, session.getId());
}

/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}

//发送给指定角色
public void send(String msg,String sendId,String receiveId,String method, String sessionId){
System.out.println("————–推送消息————-" + sessionId);
Message message = new Message();
message.setId(MyUtils.getRandomString(10));
message.setContent(msg);
message.setCreateTime(LocalDateTime.now());
//时间格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
message.setCreateTimeM(String.valueOf(dateTimeFormatter.format(LocalDateTime.now())));
message.setReceiveId(receiveId);
message.setSendId(sendId);
message.setMethod(method);
try {
Users u = userService.getById(sendId);

//to指定用户
Session con = connections.get(receiveId);
if(con!=null){
System.out.println("con.getId()——–" + con.getId());
if (sessionId.equals(this.sessionId)) {
message.setType("1");
Map map = MapUnite.getMap(message);
map.put("avatar", u.getIcon());
HashMap<String, Map<String, String>> stringMapHashMap = new HashMap<>();
stringMapHashMap.put(sendId, map);
con.getBasicRemote().sendText(JSON.toJSONString(stringMapHashMap));
}
}

//from具体用户
Session confrom = connections.get(sendId);
if(confrom!=null){
if(sessionId.equals(confrom.getId())){
System.out.println("confrom.getId()——-" + confrom.getId());
Map map = MapUnite.getMap(message);
messageService.save(message);
map.put("avatar",u.getIcon());
HashMap<String, Map<String, String>> stringMapHashMap = new HashMap<>();
stringMapHashMap.put(sendId, map);
confrom.getBasicRemote().sendText(JSON.toJSONString(stringMapHashMap));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

//发送离线消息给指定角色
public void downSend(String msg,String sendId,String receiveId,String method, String sessionId){
Message message = new Message();
message.setId(MyUtils.getRandomString(10));
message.setContent(msg);
message.setCreateTime(LocalDateTime.now());
//时间格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
message.setCreateTimeM(String.valueOf(dateTimeFormatter.format(LocalDateTime.now())));
message.setReceiveId(receiveId);
message.setSendId(sendId);
message.setMethod(method);
try {
Users u = userService.getById(sendId);
System.out.println(u);
//to指定用户
Session con = connections.get(receiveId);
if(con!=null){
System.out.println("con.getId()——–" + con.getId());
if (sessionId.equals(this.sessionId)) {
message.setType("1");
Map map = MapUnite.getMap(message);
map.put("avatar", u.getIcon());
HashMap<String, Map<String, String>> stringMapHashMap = new HashMap<>();
stringMapHashMap.put(sendId, map);
con.getBasicRemote().sendText(JSON.toJSONString(stringMapHashMap));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

public static synchronized int getOnlineCount() {
return onlineCount;
}

public static synchronized void addOnlineCount() {
WebSocketOneToOne.onlineCount++;
}

public static synchronized void subOnlineCount() {
WebSocketOneToOne.onlineCount–;
}

}

4、Message表
[SQL] 纯文本查看 复制代码CREATE TABLE `message` (
`id` varchar(36) NOT NULL COMMENT \’id\’,
`send_id` varchar(255) DEFAULT NULL COMMENT \’发送者id\’,
`receive_id` varchar(255) DEFAULT NULL COMMENT \’接收者id\’,
`type` varchar(36) NOT NULL DEFAULT \’0\’ COMMENT \’消息类型\’,
`content` varchar(255) DEFAULT NULL COMMENT \’消息内容\’,
`create_time` datetime DEFAULT NULL COMMENT \’创建时间\’,
`method` varchar(4) DEFAULT NULL COMMENT \’聊天方式\’,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=\’消息列表\’;

最后: 前端页面太丑了 , 刚开始还好一点 ,越改越丑,我自己都不想看了 , 功能实现了就好!

本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。

最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。 若排除这种情况,可在对应资源底部留言,或联络我们。

对于会员专享、整站源码、程序插件、网站模板、网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。

如果您已经成功付款但是网站没有弹出成功提示,请联系站长提供付款信息为您处理

源码素材属于虚拟商品,具有可复制性,可传播性,一旦授予,不接受任何形式的退款、换货要求。请您在购买获取之前确认好 是您所需要的资源