websocket的聊天室案例

本文展示了如何使用SpringBoot和Vue搭建WebSocket聊天室,包括后端的Maven依赖、WebSocket配置、实体类和工具类的创建,以及如何通过配置注入来获取HttpSession。前端部分详细描述了Vue模板的结构,包括用户输入、消息发送和接收、用户列表等功能。此外,还提到了将聊天记录保存到Redis中的后端实现。
摘要由CSDN通过智能技术生成

websocket的聊天室案例(springBoot+vue)

后端

maven的坐标
<!--websocket依赖-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置文件
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**

  • @author chen
  • @description WebSocketConfig配置类,注入对象ServerEndpointExporter,
  •      这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
    /
    @Configuration
    public class WebSocketConfig {
      @Bean
      public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
     }
    }
    实体类封装
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    //用户间传送的消息
    public class ResultMessage {
      private boolean isSystem;
      private String fromName;
      //private String toName;
      private Object message;
    }
    =====================================================

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    //呃…
    public class Message {
      private String toName;
      private String message;
      private String fromName;
    }
    工具类
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.websocker.pojo.ResultMessage;

    //封装发送的消息内容
    public class MessageUtils {
      public static String getMessage(boolean isSystemMessage,String fromName,Object message){
        try{
          ResultMessage resultMessage = new ResultMessage();
          resultMessage.setSystem(isSystemMessage);
          resultMessage.setMessage(message);
          if(fromName != null){
            resultMessage.setFromName(fromName);
         }
    //     if(toName !=null ){
    //       resultMessage.setToName(toName);
    //     }
          ObjectMapper mapper = new ObjectMapper();
          return mapper.writeValueAsString(resultMessage);
       }catch (JsonProcessingException e){
          e.printStackTrace();
       }
        return null;
     }
    }
    配置注入httpsession
    import javax.servlet.http.HttpSession;
    import javax.websocket.HandshakeResponse;
    import javax.websocket.server.HandshakeRequest;
    import javax.websocket.server.ServerEndpointConfig;

    public class GetHttpSesstionConfig extends ServerEndpointConfig.Configurator {
      @Override
      public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
       HttpSession httpSession= (HttpSession) request.getHttpSession();
       //将httpsession对象存储到配置 ServerEndpointConfig对象中
        if(httpSession!=null){
          sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
       }
     }
    }

    webscoket层的编写
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.websocker.pojo.Message;
    import com.websocker.utile.MessageUtils;
    import org.springframework.stereotype.Component;

    import javax.websocket.;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;

    //设置我们当前是webscoket的类
    @Component
    @ServerEndpoint(value = “/chat”, configurator = GetHttpSesstionConfig.class)
    public class ChatWebSocket {

      // 用来 存储每一个客户端对应的当前类对象
      private static Map<String, ChatWebSocket> onlineUsers = new ConcurrentHashMap<>();

      //声明session 对象 通过该对象可以发给指定的用户
      private Session session;


      @OnOpen
      //连接立时被调用
      public void Onpen(Session session, EndpointConfig config) {
        //给全局变量赋值
        this.session = session;
        //  this.httpSession= (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        //从httpsession获取用户名
        String username = session.getRequestParameterMap().get(“username”).get(0);
        //将当前的这个对象存储到容器当中
        onlineUsers.put(username, this);

        //将当前在线用户的用户名推送到所有的客户端
        String message = MessageUtils.getMessage(true, null, getNames());
        //调用方法进行系统消息的推送
        broadcastAllUsers(message);
     }

      private void broadcastAllUsers(String message) {
        //要将改消息给全部的客户端
        Set<String> strings = onlineUsers.keySet();
        for (String name : strings) {
          // 获取当前集合里面的全部客户端
          ChatWebSocket chatWebSocket = onlineUsers.get(name);
          try {
            //把消息推送给每一个客客户端
            chatWebSocket.session.getBasicRemote().sendText(message);
         } catch (IOException e) {
            e.printStackTrace();
         }
       }
     }

      //获取全部的客户端名
      private Set<String> getNames() {
        return onlineUsers.keySet();
     }

      @OnMessage
      /*
      * 接收到的数据
      */
      public void onMessage(String message, Session session) throws IOException {
        //将数据转成message对象
        ObjectMapper mapper = new ObjectMapper();
        Message mess = mapper.readValue(message, Message.class);
        //获取要将发送给的用户
        String toName = mess.getToName();
        //获取推送给指定用户的信息格式的数据
        String data = mess.getMessage();
        //获取当前登录的用户
        String username = session.getRequestParameterMap().get(“username”).get(0);

        String resultMessage = MessageUtils.getMessage(false, username, data);
        //发送消息
        onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage);
     }

      @OnClose
      //关闭连接
      public void onClose(Session session) {
        //从容器删除指定的用户
        //获取当前登录的用户
        String username = session.getRequestParameterMap().get(“username”).get(0);
        onlineUsers.remove(username);
        //获取推送的信息
        String message = MessageUtils.getMessage(true, null, getNames());
        broadcastAllUsers(message);
     }
    }

    前端


    <template>
     <div>
      <el-row :gutter=“20”>
       <el-col :span=“12” :offset=“6”>
        <div class=“main”>
         <el-row>
          <el-input
           placeholder=“请输入自己的昵称”
           prefix-icon=“el-icon-user-solid”
           v-model=“name”
           style=“width: 50%”
          ></el-input>
          <el-button type=“primary” @click=“conectWebSocket()”
           >建立连接</el-button
          >
          <el-button type=“danger” @click=“colos”>断开连接</el-button>
         </el-row>
         <el-row>
          <el-input
           placeholder=“请输入要发送的消息”
           prefix-icon=“el-icon-s-promotion”
           v-model=“messageValue”
           style=“width: 50%”
          ></el-input>
          <el-button type=“primary” @click=“sendMessage()”>发送</el-button>
         </el-row>
         <div class=“message”>
          <center v-if=“names.length > 0”>和{{ toName }}的聊天</center>
          <div v-for=“(value, index) in messageList” :key=“index”>
           <el-tag
            v-if=“value.fromName == name”
            type=“success”
            style=“float: right”
            >我:{{ value.message }}</el-tag
           >
           <br />
           <el-tag v-if=“value.fromName != name” style=“float: left”
            >{{ value.fromName }}:{{ value.message }}</el-tag
           >
           <br />
          </div>
         </div>
         <br />
         <div>
          <el-card class=“box-card”>
           <center v-if=“names.length > 0”>
            <el-tag type=“success” key=“选择好友” effect=“dark”
             >选择好友</el-tag
            >
           </center>
           <div v-for=“o in names” :key=“o” class=“text item”>
            <el-tag @click=“updateToname” type=“” effect=“dark”>{{
            o
           }}</el-tag>
           </div>
          </el-card>
         </div>
        </div>
       </el-col>
      </el-row>
     </div>
    </template>

<script>
export default {
 data() {
  return {
   name: “”, // 昵称
   websocket: null, // WebSocket对象
   aisle: “”, // 对方频道号
   messageList: [], // 消息列表
   messageValue: “”, // 消息内容
   names: [],
   broadcastList: “”,
   toName: “”,
   charData: null,
 };
},
 methods: {
  updateToname(v) {
   if (this.toName != v.srcElement.innerText) {
    this.messageList = [];
  }
   console.log(v.srcElement.innerText);
   this.toName = v.srcElement.innerText;
   var data = JSON.parse(window.sessionStorage.getItem(this.toName));
   console.log(data);

   if (data != null) {
    //将聊天记录渲染到聊天记录里面去
    this.messageList.push(data);
  }
 },
  colos() {
   // this.websocket.close();
   this.name = “”;
   this.websocket = null;
   this.messageList = [];
  (this.messageValue = “”), (names = []);
 },
  //弹窗提示
  ElementAlert(meassge) {
   this.KaTeX parse error: Expected '}', got '&' at position 18: …tify.success({ &̲nbsp;&nbsp;&nbs…alert(“请输入自己的昵称”, “提示”, {
     confirmButtonText: “确定”,
     callback: (action) => {},
   });
  } else {
    //判断当前浏览器是否支持WebSocket
    if (“WebSocket” in window) {
     this.websocket = new WebSocket(
      “ws://localhost:8089/chat?username=” + this.name
    );
   } else {
     alert(“不支持建立socket连接”);
   }
    //连接发生错误的回调方法
    this.websocket.onerror = function (er) {
     console.log(er);
   };
    //连接成功建立的回调方法
    this.websocket.onopen = function (event) {};
    //接收到消息的回调方法
    var that = this;
    this.websocket.onmessage = function (event) {
     var dataStr = event.data;
     //将dataStr 转换为json对象
     var res = JSON.parse(dataStr);
     //  console.log(res)
     if (res.system) {
      //系统消息
      var names = res.message;
      //好丫列表
      var userListStr = [];
      var broadcastListStr = “”;
      // console.log(that.name);
      for (var i = 0; i < names.length; i++) {
       if (that.name != names[i]) {
        userListStr[userListStr.length] = names[i];
      }
     }
      // console.log(userListStr)
      that.names = userListStr;
      that.broadcastList = broadcastListStr;
      //s console.log(that.names)

      //系统展示
    } else {
      //不是系统消息
      //将服务的信息进行展示s
      // that.messageList.push(res);
      // console.log(““, res);
      if (that.toName == res.fromName) {
       that.messageList.push(res);
     }
      console.log(that.toName, res.fromName);

      that.charData = window.sessionStorage.getItem(res.fromName);
      var ss;
      console.log(that.charData);
      if (that.charData != null) {
       that.charData[that.length] = res;
       ss = that.charData;
     } else {
       ss = res;
     }

      console.log(”
", ss, "*”, res.fromName);
      window.sessionStorage.setItem(res.fromName, JSON.stringify(ss));
    }
     // that.messageList.push(object);
   };
    //连接关闭的回调方法
    this.websocket.onclose = function () {};
    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
     // this.websocket.close();
   };
  }
 },
  // 发送消息
  sendMessage: function () {
   //获取发送给数据
   var messageValue = this.messageValue;
   this.messageValue = “”;
   var json = { toName: this.toName, message: messageValue };

   //发送数据给服务器
   this.websocket.send(JSON.stringify(json));
   var json1 = { fromName: this.name, message: messageValue };
   this.messageList.push(json1);
 },
  showInfo: function (people, aisle) {
   this.$notify({
    title: “当前在线人数:” + people,
    message: “您的频道号:” + aisle,
    duration: 0,
  });
 },
},
};
</script>

<style scoped>
.main {
 position: relative;
 top: 20px;
}
.message {
 position: relative;
 overflow: auto;
 top: 20px;
 width: 100%;
 height: 40%;
 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
 padding: 5px;
}
.text {
 font-size: 14px;
}

.item {
 padding: 18px 0;
}

.box-card {
 width: 480px;
}
</style>

优化后和的前端代码


<template>
 <div>
  <el-row :gutter=“20”>
   <el-col :span=“12” :offset=“6”>
    <div class=“main”>
     <!--   <el-row>
      <el-input
       placeholder=“请输入自己的昵称”
       prefix-icon=“el-icon-user-solid”
       v-model=“name”
       style=“width: 50%”
      ></el-input>
      <el-button type=“primary” @click=“conectWebSocket()”
       >建立连接</el-button
      >
     </el-row> -->
     <el-row>
      <center>
       <el-tag type=“danger” key=“选择好友” effect=“dark”>
        当前用户:{{ user.name }}</el-tag
       >
      </center>
     </el-row>
        <br/>
     <el-row>
      <el-input
       placeholder=“请输入要发送的消息”
       prefix-icon=“el-icon-s-promotion”
       v-model=“messageValue”
       style=“width: 50%”
      ></el-input>
      <el-button type=“primary” @click=“sendMessage()”>发送</el-button>
     </el-row>
     <div class=“message” v-if=“toName”>
      <center>
       <el-tag type=“success” key=“选择好友” effect=“dark”
        >和{{ toName }}的聊天</el-tag
       >
      </center>
      <div v-for=“(value, index) in messageList” :key=“index”>
       <el-tag
        v-if=“value.fromName == name”
        type=“success”
        style=“float: right”
        >我:{{ value.message }}</el-tag
       >
       <br />
       <el-tag v-if=“value.fromName != name” style=“float: left”
        >{{ value.fromName }}:{{ value.message }}</el-tag
       >
       <br />
      </div>
     </div>
     <br />
     <div>
      <el-card class=“box-card”>
       <center v-if=“names.length > 0”>
        <el-tag type=“success” key=“选择好友” effect=“dark”
         >选择好友</el-tag
        >
       </center>
       <div v-for=“o in names” :key=“o” class=“text item”>
        <el-tag @click=“updateToname” type=“” effect=“dark”>{{
         o
       }}</el-tag>
       </div>
      </el-card>
     </div>
    </div>
   </el-col>
  </el-row>
 </div>
</template>

<script>
var that;
import socket from “@/api/websocket”;
import user from “@/api/user”;
export default {
 data() {
  return {
   name: “”, // 昵称
   websocket: null, // WebSocket对象
   aisle: “”, // 对方频道号
   messageList: [], // 消息列表
   messageValue: “”, // 消息内容
   names: [],
   broadcastList: “”,
   toName: “”,
   charData: null,
   user: {},
 };
},
 methods: {
  updateToname(v) {
   this.messageList = [];

   console.log(v.srcElement.innerText);
   this.toName = v.srcElement.innerText;
   socket.getfinByNames(this.name, this.toName).then((res) => {
    this.messageList = res.data.data.data;
    console.log(res.data.data.data);
  });
 },
  colos() {
   // this.websocket.close();
   this.name = “”;
   this.websocket = null;
   this.messageList = [];
  (this.messageValue = “”), (names = []);
 },
  //弹窗提示
  ElementAlert(meassge) {
   this.KaTeX parse error: Expected '}', got '&' at position 18: …tify.success({ &̲nbsp;&nbsp;&nbs…alert(“请输入自己的昵称”, “提示”, {
     confirmButtonText: “确定”,
     callback: (action) => {},
   });
  } else {
    //判断当前浏览器是否支持WebSocket
    if (“WebSocket” in window) {
     this.websocket = new WebSocket(
      “ws://localhost:8989/chat?username=” + this.name
    );
   } else {
     alert(“不支持建立socket连接”);
   }
    //连接发生错误的回调方法
    this.websocket.onerror = function (er) {
     console.log(er);
   };
    //连接成功建立的回调方法
    this.websocket.onopen = function (event) {};
    //接收到消息的回调方法
    this.websocket.onmessage = function (event) {
     var dataStr = event.data;
     //将dataStr 转换为json对象
     var res = JSON.parse(dataStr);
     //  console.log(res)
     if (res.system) {
      //系统消息
      var names = res.message;
      //好丫列表
      var userListStr = [];
      var broadcastListStr = “”;
      // console.log(that.name);
      for (var i = 0; i < names.length; i++) {
       if (that.name != names[i]) {
        userListStr[userListStr.length] = names[i];
      }
     }
      // console.log(userListStr)
      that.names = userListStr;
      that.broadcastList = broadcastListStr;
      //s console.log(that.names)

      //系统展示
    } else {
      //不是系统消息
      //将服务的信息进行展示s
      if (that.toName == res.fromName) {
       if (that.messageList != null) {
        that.messageList.push(res);
      } else {
        that.messageList = res;
      }
     }
      /*          console.log( that.messageList, “==========”); /
      socket.saveList(that.messageList);
    }
   };
    //连接关闭的回调方法
    this.websocket.onclose = function () {};
    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
     // this.websocket.close();
   };
  }
 },
  // 发送消息
  sendMessage: function () {
   //获取发送给数据
   var messageValue = this.messageValue;
   this.messageValue = “”;
   var json = { toName: this.toName, message: messageValue };

   //发送数据给服务器
   this.websocket.send(JSON.stringify(json));
   var json1 = {
    fromName: this.name,
    message: messageValue,
    toName: this.toName,
  };
   /
    console.log(that.messageList,“***”) /
   if (that.messageList != null) {
    that.messageList.push(json1);
  } else {
    that.messageList = [json1];
  }
   /
    console.log(that.messageList,“*”) */
   socket.saveList(that.messageList);
 },
  showInfo: function (people, aisle) {
   this.$notify({
    title: “当前在线人数:” + people,
    message: “您的频道号:” + aisle,
    duration: 0,
  });
 },

  //获取我们用户信息的方法
  getUser() {
   user.getUserToken(window.sessionStorage.getItem(“token”)).then((res) => {
    console.log(res.data);
    this.user = res.data.data;
    this.name = this.user.name;
    this.conectWebSocket()
  });
 },
},

 //页面加载完毕
 created() {
  this.getUser();

},
};
</script>

<style scoped>
.main {
 position: relative;
 top: 20px;
}
.message {
 position: relative;
 overflow: auto;
 top: 20px;
 width: 100%;
 height: 40%;
 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
 padding: 5px;
}
.text {
 font-size: 14px;
}

.item {
 padding: 18px 0;
}

.box-card {
 width: 480px;
}
</style>

后端把聊天记录保存到redis中

import com.shop.shop.entity.Message;
import com.shop.shop.utile.Q;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.;
import java.io.IOException;
import java.util.List;

@RestController
@RequestMapping(“/shop/websocket”)
/**
    对我们的聊天记录进行保存到我们的redis当中去
/
public class WebSocketRedisConreoller {

  @Autowired
  private RedisTemplate<String, Object> redisTemplate;


  @GetMapping(“/findName/{name}/{toName}”)
  /

  
根据name查询聊天记录
  */
  public Q findName(@PathVariable(“toName”) String toName,
           @PathVariable(“name”) String name
 ) throws IOException {
    List<Message> list = (List<Message>) redisTemplate.opsForValue().get(name + “" + toName);
    List<Message> list1 = (List<Message>) redisTemplate.opsForValue().get(toName + "
” + name);
    if (list == null && list1 != null) {
      list = list1;
   }

    return Q.ok().data(“data”, list);
 }


  @PostMapping(“/save”)
  //直接保存
  public Q saveList(@RequestBody List<Message> list) {
    if (list.size() <= 0) {
      return Q.error();
   }
    redisTemplate.opsForValue().set(list.get(0).getFromName() + “" + list.get(0).getToName(), list);
    redisTemplate.opsForValue().set(list.get(0).getToName() + "
” + list.get(0).getFromName(), list);
    return Q.ok();
 }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百度搜索爱敲代码的小庆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值