吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3702|回复: 12
收起左侧

[Java 原创] 【Java】SpringBoot+Mybatis-Plus使用webSocket实现一对一聊天 后台

[复制链接]
战之都 发表于 2020-9-21 11:26
本帖最后由 战之都 于 2020-9-21 11:56 编辑

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='消息列表';




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

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

yiwanyiwan 发表于 2020-9-21 14:30
分享快乐
QingYi. 发表于 2020-9-21 21:25
战之都 发表于 2020-9-21 12:05
小弟不才,5+1+1  5天测试通过, 2天修改才到这个地步

不对 5+1+1  还有一个群聊没拿出 ...

可以了 可惜我最近在学软件测试 不是有很多时间深入学习这些东西了。
红尘多可笑 发表于 2020-9-22 11:02
 楼主| 战之都 发表于 2020-9-21 15:21
夏橙M兮 发表于 2020-9-21 14:48
快进到用Netty服务器。

等群聊的各种问题解决了就会用Netty重新写一份出来
    一点一点来
夏橙M兮 发表于 2020-9-21 14:48
快进到用Netty服务器。
 楼主| 战之都 发表于 2020-9-21 14:08
dreamrise 发表于 2020-9-21 14:03
前端呢?没发出来?搞个能运行的打包版本来看看

前端太丑了 也不是专业写前端的,就不放出来的  我晚一点 更新主要业务逻辑出来
dreamrise 发表于 2020-9-21 14:03
前端呢?没发出来?搞个能运行的打包版本来看看
lxyadobe 发表于 2020-9-21 13:47
学过一点点Java。
Willis_Wang 发表于 2020-9-21 12:48
最近在学java 厉害了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-26 02:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表