基于socket ,Spring boot的实时聊天系统

1,前言

        不管是在线商城还是聊天软件,都需要一个实时的聊天系统,我们应该如何实现实时的聊天系统,可以方便我们实时聊天,对商品的一些了解等。

2,技术

        java + spring boot + WebSocket +vue

3,效果展示

 4,实现步骤

         1,前端实现

                

<template>
  <div>
    <van-nav-bar
      :title="toname"
      left-arrow
      @click-left="onClickLeft"
      @click-right="onClickRight"
    >
      <template #right>
        <van-icon name="ellipsis" size="18" />
      </template>
    </van-nav-bar>

    <van-form @submit="websocketsend">
      <van-notice-bar
        :scrollable="true"
        left-icon="volume-o"
        text="提示:歡迎來到聊天內側版,同步最近历史消息可点击···,請愉快的玩耍吧!。"
      />

      <van-grid :column-num="1" :border="false">
        <van-grid-item>
          <van-image
            width="6rem"
            height="6rem"
            fit="contain"
            src="https://www.pngdirs.com/thumb/350/orange/orange-9864.png"
          />

          <h3>CHENGO FRUIT</h3>
        </van-grid-item>
      </van-grid>
      <div class="chat-content">
        <!-- recordContent 聊天记录数组-->
        <div v-for="(itemc, indexc) in recordContent" :key="indexc">
          <!-- 对方 -->
          <div class="word" v-if="!itemc.mineMsg">
            <van-row>
              <van-col span="3" style="padding-top: 16px; text-align: right">
                <van-image
                  round
                  fit="contain"
                  :src="imgUrl"
                  width="3rem"
                  height="3rem"
                />
              </van-col>
              <van-col span="21">
                <div class="yinfo">
                  <p class="time">{{ itemc.nickName }} {{ itemc.timestamp }}</p>
                  <div class="yinfo-content">
                    <span v-if="itemc.type == 'video'">
                      {{ itemc.contactText }}
                    </span>
                    <span
                      v-else-if="itemc.type == 'img'"
                      @click="preViewImg(itemc.contactText)"
                    >
                      <van-image
                        fit="cover"
                        :src="itemc.contactText"
                        width="8rem"
                        height="100%"
                      />
                    </span>
                    <span v-else>
                      {{ itemc.contactText }}
                    </span>
                  </div>
                </div>
              </van-col>
            </van-row>
          </div>
          <!-- 我的 -->
          <div class="word-my" v-else>
            <van-row>
              <van-col span="21">
                <div class="info">
                  <p class="time">{{ itemc.nickName }} {{ itemc.timestamp }}</p>
                  <van-popover
                    v-model="itemc.cancellMessage"
                    trigger="click"
                    placement="top"
                  >
                    <van-grid
                      square
                      clickable
                      :border="false"
                      column-num="3"
                      style="width: 163px"
                    >
                      <van-grid-item
                        text="撤回"
                        icon="revoke"
                        @click="cancellMsg(itemc.id)"
                      />
                      <van-grid-item
                        text="取消"
                        icon="cross"
                        @click="cancell(itemc)"
                      />
                      <van-grid-item
                        text="引用"
                        icon="replay"
                        @click="cancell(itemc)"
                      />
                    </van-grid>
                    <template #reference>
                      <div
                        class="info-content"
                        @touchstart="getTouchStart(itemc)"
                        @getTouchEnd="getTouchEnd(itemc)"
                      >
                        <span v-if="itemc.type == 'video'">
                          {{ itemc.contactText }}
                        </span>
                        <span
                          v-else-if="itemc.type == 'img'"
                          @click="preViewImg(itemc.contactText)"
                        >
                          <van-image
                            fit="cover"
                            :src="itemc.contactText"
                            width="8rem"
                            height="100%"
                          />
                        </span>
                        <span v-else>
                          {{ itemc.contactText }}
                        </span>
                      </div>
                    </template>
                  </van-popover>
                </div>
              </van-col>
              <van-col span="3" style="padding-top: 16px">
                <van-image
                  round
                  fit="contain"
                  :src="userInfo.imgUrl"
                  width="3rem"
                  height="3rem"
              /></van-col>
            </van-row>
            <van-row>
              <van-col span="21">
                <div class="status">{{ itemc.status }}</div>
              </van-col>
              <van-col span="3"> </van-col>
            </van-row>
          </div>
        </div>
      </div>
      <div style="margin: 0px 0px">
        <div style="margin: 5px">
          <!-- <van-button round block type="primary" @click="getEmo()">
          选择表情
        </van-button> -->
          <van-field
            v-model="chatContent"
            center
            clearable
            placeholder="输入新消息"
          >
            <template #button>
              <van-button size="small" type="primary" round color="#25d4d0">发送</van-button>
            </template>
          </van-field>
          <van-grid
            square
            clickable
            :border="false"
            column-num="5"
            style="margin-top: 5px"
          >
            <van-grid-item key="1" text="照片" icon="photo-o">
              <van-uploader
                v-model="uploader"
                :after-read="afterRead"
                :max-count="1"
              >
                <van-icon name="photo-o" size="1.5rem"> </van-icon>
                <div style="font-size: 12px; text-align: center">照片</div>
              </van-uploader>
            </van-grid-item>
            <van-grid-item key="3" text="位置" icon="location-o">
              <van-icon name="location-o" size="1.5rem"> </van-icon>
              <div style="font-size: 12px; text-align: center">位置</div>
            </van-grid-item>
            <van-grid-item key="4" text="语言输入" icon="volume-o">
              <van-icon name="volume-o" size="1.5rem"> </van-icon>
              <div style="font-size: 12px; text-align: center">语言输入</div>
            </van-grid-item>
            <van-grid-item key="6" text="文件" icon="desktop-o">
              <van-icon name="desktop-o" size="1.5rem"> </van-icon>
              <div style="font-size: 12px; text-align: center">
                文件
              </div></van-grid-item
            >
            <van-grid-item key="8" text="音乐" icon="music-o">
              <van-icon name="music-o" size="1.5rem"> </van-icon>
              <div style="font-size: 12px; text-align: center">
                音乐
              </div></van-grid-item
            >
          </van-grid>
          <div class="emotion-box-line" v-for="(line, i) in list" :key="i">
            <emotion
              class="emotion-item"
              v-for="(item, i) in line"
              :key="i"
              @click="checkHandle(item)"
              >{{ item }}</emotion
            >
          </div>
        </div>
      </div>
    </van-form>
  </div>
</template>

<script>
import { Form, Field, CellGroup, Toast, Notify } from "vant";
import { fetchGet, fetchPost, fetchPostFormData } from "../../http";
import { ref } from "vue";
import Vue from "vue";
import { Tab, Tabs } from "vant";
import { NavBar } from "vant";
import { Grid, GridItem } from "vant";
import { Col, Row } from "vant";
import user from "../../common/user";
import { Popover } from "vant";
import { ImagePreview } from "vant";
import { Sticky } from "vant";
import { Popup } from "vant";

export default {
  name: "Message",
  components: {
    [Form.name]: Form,
    [Field.name]: Field,
    [CellGroup.name]: CellGroup,
    Toast,
    Notify,
    Tabs,
    Tab,
    NavBar,
    Grid,
    GridItem,
    Col,
    Row,
    Popover,
    ImagePreview,
    Sticky,
    Popup,
  },
  data() {
    return {
      key: "",
      varCodeUrl: "",
      rememberMe: true,
      isLoading: false,
      isError: false,
      chatContent: "",
      password: "",
      code: "",
      websock: null,
      recordContent: [],
      list: [],
      toUserId: null,
      type: null,
      toname: null,
      imgUrl: null,
      userInfo: {},
      wsHasClose: false,
      showPopover: false,
      uploader: [],
      uploadUrl: null,
      plus: false,
    };
  },
  created() {
    this.getUserInfo();
    this.initWebSocket();
    this.getHisMessage();
  },
  destroyed() {
    this.websock.close(); //离开路由之后断开websocket连接
  },
  methods: {
    sendPic() {},
    showPopup() {
      this.plus = !this.plus;
    },
    preViewImg(url) {
      ImagePreview([url]);
    },
    afterRead(file) {
      file.status = "uploading";
      file.message = "上传中...";
      fetchPostFormData("/sys/file/upload", file.file)
        .then((data) => {
          this.uploadUrl = data.data.data.url;
          file.status = "success";
          file.message = "上传成功";
        })
        .catch(() => {
          file.status = "failed";
          file.message = "上传失败";
        });
    },
    getTouchStart(itemc) {
      itemc.cancellMessage = true;
      this.showPopover = true;
    },
    getTouchEnd(itemc) {
      itemc.cancellMessage = false;
    },
    cancellMsg(id) {
      let data = {
        msgId: id,
      };
      fetchPost("/sys/message/withdrawMessage", data)
        .then((data) => {
          if (data.data.data) {
            Toast({
              message: "撤回成功",
              position: "top",
            });
          } else {
            Toast({
              message: data.data.msg,
              position: "top",
            });
          }
          this.$nextTick(() => {
            this.getHisMessage();
          });
        })
        .catch(() => {});
    },
    cancell(itemc) {
      itemc.cancellMessage = false;
    },
    getUserInfo() {
      this.userInfo = Vue.ls.get("userInfo");
    },
    getHisMessage() {
      // 异步更新数据
      fetchGet("/sys/message/listMyMessage?" + "username=" + this.toUserId)
        .then((data) => {
          let record = data.data.data;
          let records = [];
          for (let i = 0; i < record.length; i++) {
            let flag = record[i].toUser == this.toUserId;
            let content = JSON.parse(record[i].msgContent);
            let param = {
              mineMsg: flag,
              timestamp: content.cuTime,
              nickName: flag ? this.userInfo.name : this.toname,
              contactText: content.data,
              status: "1" == record[i].messageStatus ? "已读" : "送达",
              cancellMessage: false,
              id: record[i].id,
              type: record[i].type,
            };
            records.push(param);
          }
          this.recordContent = records;
          this.scrollToBottom();
        })
        .catch(() => {});
    },
    onClickLeft() {
      this.$router.push("/sys/message");
    },
    onClickRight() {
      this.getHisMessage();
    },
    scrollToBottom() {
      this.$nextTick(() => {
        console.log(document.documentElement.scrollHeight);
        scrollTo(0, document.documentElement.scrollHeight);
      });
    },
    checkHandle(item) {
      let index = this.list.indexOf(item);
      let imgHTML = `<img src="https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/${index}.gif">`;
      this.$nextTick(() => {
        this.$el.innerHTML = imgHTML;
      });
    },
    getEmo() {
      this.list = [
        "微笑",
        "撇嘴",
        "色",
        "发呆",
        "得意",
        "流泪",
        "害羞",
        "闭嘴",
        "睡",
        "大哭",
        "尴尬",
        "发怒",
        "调皮",
        "呲牙",
        "惊讶",
        "难过",
        "酷",
        "冷汗",
        "抓狂",
        "吐",
        "偷笑",
        "可爱",
        "白眼",
        "傲慢",
        "饥饿",
        "困",
        "惊恐",
        "流汗",
        "憨笑",
        "大兵",
        "奋斗",
        "咒骂",
        "疑问",
        "嘘",
        "晕",
        "折磨",
        "衰",
        "骷髅",
        "敲打",
        "再见",
        "擦汗",
        "抠鼻",
        "鼓掌",
        "糗大了",
        "坏笑",
        "左哼哼",
        "右哼哼",
        "哈欠",
        "鄙视",
        "委屈",
        "快哭了",
        "阴险",
        "亲亲",
        "吓",
        "可怜",
        "菜刀",
        "西瓜",
        "啤酒",
        "篮球",
        "乒乓",
        "咖啡",
        "饭",
        "猪头",
        "玫瑰",
        "凋谢",
        "示爱",
        "爱心",
        "心碎",
        "蛋糕",
        "闪电",
        "炸弹",
        "刀",
        "足球",
        "瓢虫",
        "便便",
        "月亮",
        "太阳",
        "礼物",
        "拥抱",
        "强",
        "弱",
        "握手",
        "胜利",
        "抱拳",
        "勾引",
        "拳头",
        "差劲",
        "爱你",
        "NO",
        "OK",
        "爱情",
        "飞吻",
        "跳跳",
        "发抖",
        "怄火",
        "转圈",
        "磕头",
        "回头",
        "跳绳",
        "挥手",
        "激动",
        "街舞",
        "献吻",
        "左太极",
        "右太极",
      ];
    },
    initWebSocket() {
      let asscess_token = Vue.ls.get("asscess_token");
      this.toUserId = this.$route.query.username;
      this.toname = this.$route.query.name;
      this.imgUrl = this.$route.query.imgUrl;
      //初始化weosocket
      if (window.WebSocket) {
        let wsBaseUrl = this.$API_BASE_URL;
        if (wsBaseUrl.indexOf("https") != -1) {
          wsBaseUrl = wsBaseUrl.replace("https", "wss");
        } else {
          if (wsBaseUrl.indexOf("http") != -1) {
            wsBaseUrl = wsBaseUrl.replace("http", "ws");
          }
        }
        const wsuri =
          wsBaseUrl +
          "/message_websocket/" +
          asscess_token +
          "/" +
          this.toUserId;
        this.websock = new WebSocket(wsuri);
        this.websock.onmessage = this.websocketonmessage;
        this.websock.onopen = this.websocketonopen;
        this.websock.onerror = this.websocketonerror;
        this.websock.onclose = this.websocketclose;
      } else {
        alert("你的浏览器不支持WebSocket。请不要使用低版本的IE浏览器。");
      }
      this.wsHasClose = false;
    },
    websocketonopen() {
      //连接建立之后执行send方法发送数据
      let actions = {
        userId: "workOrderSendId",
        provinceCode: "01",
        cityCode: "02",
        areaCode: "03",
        gridCode: "04",
        roleIds: "01,02,03",
      };
      this.websocketsend(JSON.stringify(actions));
    },
    websocketonerror() {},
    websocketonmessage(e) {
      //数据接收
      console.log(e.data);
      let retData = JSON.parse(e.data);
      if (retData != "" && retData.withdrawMessage == "true") {
        let msgId = retData.msgId;

        for (let i = 0; i < this.recordContent.length; i++) {
          if (this.recordContent[i].id == msgId) {
            this.recordContent.splice(i, 1);
          }
        }
        Notify({ type: "primary", message: "对方撤回了一条消息!" });

        return;
      }
      if (retData != "" && retData.readFlag == "true") {
        for (let i = 0; i < this.recordContent.length; i++) {
          if (this.recordContent[i].status == "送达") {
            this.recordContent[i].status = "已读";
          }
        }
        return;
      }
      if ("" != retData.data) {
        Notify({ type: "primary", message: "新消息:" + retData.data });
        var param = {
          mineMsg: false,
          //   headUrl: "https://www.pngdirs.com/thumb/350/orange/orange-9864.png",
          timestamp: retData.cuTime,
          nickName: this.toname,
          contactText: retData.data,
          cancellMessage: false,
          id: retData.id,
          type: retData.type,
        };

        this.recordContent = this.recordContent.concat(param);

        //如果用户再看历史聊天记录,不能直接调最底层
        //整个高度
        let height = document.documentElement.scrollHeight;
        this.$nextTick(() => {
          this.scrollToBottom();
        });
      }
    },
    websocketsend() {
      if (this.wsHasClose) {
        this.initWebSocket();
      }
      let type = "text";
      //判断发送的消息类型
      if (this.uploader.length > 0) {
        type = "img";
        this.chatContent = this.uploadUrl;
      }

      let yy = new Date().getFullYear();
      let mm = new Date().getMonth() + 1;
      let dd = new Date().getDate();
      let hh = new Date().getHours();
      let mf =
        new Date().getMinutes() < 10
          ? "0" + new Date().getMinutes()
          : new Date().getMinutes();
      let ss =
        new Date().getSeconds() < 10
          ? "0" + new Date().getSeconds()
          : new Date().getSeconds();
      let cuTime = yy + "/" + mm + "/" + dd + " " + hh + ":" + mf + ":" + ss;
      let msgId = user.uuid(32, 15);
      let requestParam = {
        toUserId: this.toUserId,
        data: this.chatContent,
        cuTime: cuTime,
        id: msgId,
        type: type,
      };

      //数据发送
      this.websock.send(JSON.stringify(requestParam));

      var param = {
        mineMsg: true,
        //   headUrl: "https://www.pngdirs.com/thumb/350/orange/orange-9864.png",
        timestamp: cuTime,
        nickName: this.userInfo.name,
        contactText: this.chatContent,
        status: "送达",
        cancellMessage: false,
        id: msgId,
        type: type,
      };

      if (this.chatContent != null && this.chatContent != "") {
        this.recordContent = this.recordContent.concat(param);
        this.chatContent = null;

        Toast.success("发送成功");
      }
      this.$nextTick(() => {
        this.uploadUrl = null;
        this.uploader = [];
        this.scrollToBottom();
      });
    },
    websocketclose(e) {
      //关闭
      console.log("断开连接", e);
      this.wsHasClose = true;
    },
  },
};
</script>
<style scoped>
.reg {
  color: rgb(123, 143, 143);
}
.chat-content {
  width: 98%;
}
.word {
  margin-bottom: 10px;
}
.yinfo {
  margin-left: 10px;
  text-align: left;
}
.yinfo-content {
  max-width: 80%;
  padding: 10px;
  font-size: 14px;
  float: left;
  position: relative;
  margin-top: 8px;
  background: #ddeae7;
  text-align: left;
  border-radius: 5px;
  word-break: break-all;
  word-wrap: break-word;
}

.yinfo-content::before {
  position: absolute;
  left: -8px;
  top: 8px;
  content: "";
  border-right: 10px solid #ddeae7;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
}
.word-my {
  margin-bottom: 10px;
}
.info {
  margin-left: 15px;
  text-align: right;
}
.time {
  font-size: 12px;
  color: rgba(51, 51, 51, 0.8);
  margin: 0;
  height: 20px;
  line-height: 20px;
  margin-top: -5px;
  /* margin-right: 10px; */
}
.info-content {
  max-width: 80%;
  padding: 10px;
  font-size: 14px;
  float: right;
  margin-right: 10px;
  position: relative;
  margin-top: 8px;
  background: #a3c3f6;
  text-align: left;
  border-radius: 5px;
  word-break: break-all;
  word-wrap: break-word;
}
.info-content::after {
  position: absolute;
  right: -8px;
  top: 8px;
  content: "";
  border-left: 10px solid #a3c3f6;
  border-top: 8px solid transparent;
  border-bottom: 8px solid transparent;
}
.status {
  text-align: right;
  font-size: 8px;
  color: #757272;
}
</style>

5,后端实现

      1, 增加WebSocketConfig 配置

import com.chengo.finance.orangefruitsystemservice.server.WebSocketServer;
import com.chengo.finance.orangefruitsystemservice.sys.service.ISysMessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

    /**
     * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    @Autowired
    public void setSenderService(ISysMessageService senderService){
        WebSocketServer.sysMessageService = senderService;
    }
}

2,增加WebSocketServer,类似于一个controller,用于消息的接受与发送。

package com.chengo.finance.orangefruitsystemservice.server;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.chengo.finance.orangefruitcore.constant.CommonConstant;
import com.chengo.finance.orangefruitcore.pojo.LoginUser;
import com.chengo.finance.orangefruitcore.util.AESUtil;
import com.chengo.finance.orangefruitcore.util.IDGenerate;
import com.chengo.finance.orangefruitcore.util.SecurityUtil;
import com.chengo.finance.orangefruitsystemservice.sys.entity.SysMessage;
import com.chengo.finance.orangefruitsystemservice.sys.service.ISysMessageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@ServerEndpoint(value = "/message_websocket/{token}/{toUser}")
@Component
@Slf4j
public class WebSocketServer {

    private String token;

    private String username;

    private Session session;

    public static ISysMessageService sysMessageService;
    /**
     * 记录当前在线连接数
     */
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    /**
     * 存放所有在线的客户端
     */
    private static ConcurrentHashMap<String, WebSocketServer> clients = new ConcurrentHashMap<>();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token, @PathParam("toUser") String toUser) {

        LoginUser loginUser = SecurityUtil.getLoginUserByToken(token);
        this.token = token;
        this.session = session;
        this.username = loginUser.getUsername();
        onlineCount.incrementAndGet(); // 在线数加1
        clients.put(username, this);
        log.info("有新连接加入:{},当前在线人数为:{}", username, onlineCount.get());

        //判断是否是存在消息的人上线 初始化消息已经发送,不需要重发了注释
//        LambdaQueryWrapper<SysMessage> query = Wrappers.lambdaQuery();
//        query.eq(SysMessage::getToUser, username).eq(SysMessage::getSendStatus, "1")
//                .eq(SysMessage::getFromUser, toUser).orderByAsc(SysMessage::getCreateTime);
//        List<SysMessage> list = sysMessageService.list(query);
//        if (null != list && !list.isEmpty()) {
//            for (SysMessage sysMessage : list) {
//                try {
//                    String decrypt = AESUtil.decrypt(sysMessage.getMsgContent(), AESUtil.aesKey);
//                    this.sendInfo(null == decrypt ? sysMessage.getMsgContent() : decrypt, username);
//                    sysMessage.setSendStatus(CommonConstant.TWO);
//                    sysMessage.setMessageStatus(CommonConstant.ONE);
//                    sysMessageService.updateById(sysMessage);
//                } catch (IOException e) {
//                    log.error("推送消息失败,稍后重试!");
//                }
//            }
//        }


    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session) {
        onlineCount.decrementAndGet(); // 在线数减1
        if (clients.get(username) != null) {
            clients.remove(username);
            log.info("有一连接关闭:{},当前在线人数为:{}", username, onlineCount.get());
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服务端收到客户端[{}]的消息:{}", username, message);
        if (StringUtils.isNotBlank(message)) {
            try {
                // 解析发送的报文
                JSONObject jsonObject = JSONUtil.parseObj(message);
                // 追加发送人(防窜改)
                jsonObject.set("fromUserId", this.username);
                String toUserId = jsonObject.getStr("toUserId");
                // 传送给对应 toUserId 用户的 WebSocket
                if (StringUtils.isNotBlank(toUserId) && clients.containsKey(toUserId)) {
                    clients.get(toUserId).sendMessage(JSONUtil.toJsonStr(jsonObject));
                    saveMessage(jsonObject, toUserId, CommonConstant.TWO);

                    try {
                        JSONObject object = new JSONObject();
                        object.set("readFlag","true");
                        this.sendInfo(object.toString(),username);
                    } catch (IOException e) {
                        log.error("发送已读消息回执失败!");
                    }

                } else {
                    log.info("请求的userId:" + toUserId + "不在该服务器上,记录在数据库,上线推送"); // 否则不在这个服务器上,发送到 MySQL 或者 Redis
                    saveMessage(jsonObject, toUserId, CommonConstant.ONE);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 保存数据
     * @param jsonObject
     * @param toUserId
     * @param status
     */
    private void saveMessage(JSONObject jsonObject, String toUserId, String status) {
        if (StringUtils.isNotBlank(jsonObject.getStr("data"))) {
            SysMessage sysMessage = new SysMessage();
            String encrypt = AESUtil.encrypt(JSONUtil.toJsonStr(jsonObject), AESUtil.aesKey);
            if (StringUtils.isNotEmpty(jsonObject.getStr("id"))) {
                sysMessage.setId(jsonObject.getStr("id"));
            } else {
                sysMessage.setId(IDGenerate.generateId());
            }
            sysMessage.setType(jsonObject.getStr("type"));
            sysMessage.setMsgContent(null == encrypt ? JSONUtil.toJsonStr(jsonObject) : encrypt)
                    .setSendStatus(status).setToUser(toUserId)
                    .setMessageStatus(CommonConstant.ONE.equals(status) ? CommonConstant.ZERO : CommonConstant.ONE)
                    .setFromUser(username);
            sysMessageService.save(sysMessage);
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 群发消息
     *
     * @param message 消息内容
     */
    private void sendMessage(String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 群发自定义消息
     *
     * @param message
     * @param username
     * @throws IOException
     */
    public void sendInfo(String message, String username) throws IOException {

        // 遍历集合,可设置为推送给指定sid,为 null 时发送给所有人
        Iterator entrys = clients.entrySet().iterator();
        while (entrys.hasNext()) {
            Map.Entry entry = (Map.Entry) entrys.next();

            if (username == null) {
                clients.get(entry.getKey()).sendMessage(message);
                log.info("发送消息到:" + entry.getKey() + ",消息:" + message);
            } else if (entry.getKey().equals(username)) {
                clients.get(entry.getKey()).sendMessage(message);
                log.info("发送消息到:" + entry.getKey() + ",消息:" + message);
            }

        }


    }
}

以上就是简单的实现消息的发送与接受,实时的聊天系统就完成了

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值