SpringBoot+webSocket项目私信聊天功能实现

webSocket

        什么是ws?

         WS(WebSocket)是一种网络通信协议,它提供了在客户端和服务器之间进行双向、实时通信的能力。相比于传统的HTTP协议,WebSocket具有更低的延迟和更高的效率。

传统的HTTP协议是一种无状态的协议,每次通信都需要客户端发起请求,服务器响应后关闭连接,因此无法实现实时的双向通信。而WebSocket协议则在初始握手阶段首先通过HTTP协议建立连接,然后升级为双向通信的WebSocket连接。一旦建立了WebSocket连接,客户端和服务器就可以通过该连接进行实时的数据交换,而无需每次都重新建立连接。

WebSocket协议使用基于帧的消息传递机制,允许客户端和服务器以消息的形式进行通信。客户端可以发送消息给服务器,服务器也可以主动推送消息给客户端,实现了真正的双向通信。这种实时通信的特性使得WebSocket在许多场景下非常有用,例如在线聊天应用、实时数据监控和游戏等。

SpringBoot集成WebSocket

        在Java项目中的pom.xml文件中导入依赖wzbsocket依赖

   <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>

     导入websocket配置文件注册成为一个工具类,交给ioc管理。加载websocket


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * 开启WebSocket支持
 * @author wzk
 */
@Configuration  
public class WebSocketConfig {  
	
    @Bean  
    public ServerEndpointExporter serverEndpointExporter() {  
        return new ServerEndpointExporter();  
    }  
  
} 

        加载业务逻辑

websocket方法:

      @ServerEndpoint(value = "/chat/{username}"):用于建立连接路径,username表示当前连接的用户(前端的连接 路径为:ws://localhost:端口号/chat/当前连接用户)

     @Component:当前类注入ioc管理对方

     @OnOpen:用户发送请求触发函数方法

     @PathParam("name"):将用户请求路径中的 参数注入到该方法的参数上面

     @OnError : 用户请求连接异常触发函数

     @OnMessage:用户客户端*(前端)项目服务端(后端)发送信息触发函数

     @OnClose : 用户关闭连接触发函数

      发送消息方法sendMessage :toSession.getBasicRemote().sendText(msg):通过toSessiongetBasicRemote()方法获取到与目标会话关联的RemoteEndpoint.Basic对象,通过调用sendText(msg)方法发送文本消息。sendText方法是WebSocket API提供的方法,用于发送文本消息至远程端点。


import com.alibaba.fastjson.JSON;
import com.hqyj.project.pojo.Message;
import com.hqyj.project.utils.ApplicationContextUtil;
import lombok.extern.slf4j.Slf4j;

import org.springframework.data.redis.core.RedisTemplate;
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.List;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/chat/{username}")
@Component
@Slf4j
public class SocketServer {


    // 保存链接的session,key为用户名,value为对应的session名
    private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 创建连接
     * 用于监听建立连接,当有客户端与该服务端点建立连接时,将会自回调该注解标注的方法
     * @param session
     * @param username
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "username") String username) {
        log.info("用户{}已创建连接", username);
        String key = "webSocket:"+username;
        sessionMap.put(username,session);
        //获取redis对象
        RedisTemplate redisTemplate =(RedisTemplate)ApplicationContextUtil.getBean("redis");
        //判断是否为空
        List<String> members = redisTemplate.opsForList().range(key,0,-1);
        if (null != members && !members.isEmpty()){
            for (String message : members) {
                Session toSession = sessionMap.get(username);
                  //发送缓存信息
                sendMessage(toSession,message);
            }
            //删除redis中的键
            redisTemplate.delete(key);
        }
    }


    /**
     * 用于监听客户端向服务端发送消息,当客户端与服务端发送消息时,将会回调该注解标注的方法
     * @param msg
     * @param username
     */
    @OnMessage
    public void onMessage(String msg,@PathParam(value = "username") String username){
        log.info("用户{}发来消息:{}",username,msg);
        //解析用户发送的信息,使用系列化,映射到Message类
        Message message = JSON.parseObject(msg, Message.class);
        //使用工具类获取redis缓存数据库
        RedisTemplate redisTemplate =(RedisTemplate)ApplicationContextUtil.getBean("redis");
        //根据message中的to属性获取接收消息的用户的session,利用其session将消息转发过
        if (null == sessionMap.get(message.getTo())){   //发送给的用户为空
            //存入redis
            redisTemplate.opsForList().rightPush("webSocket:"+message.getTo(),message.getMsg());
            return;
        }
        Session toSession = sessionMap.get(message.getTo());
        //发送信息给用户
        sendMessage(toSession, message.getMsg());
    }


    /**
     * 用于监听连接关闭,当客户端与该服务端点断开连接时,将会回调该注解标注的方法
     * @param session
     * @param username
     */
    @OnClose
    public void onClose(Session session, @PathParam(value = "username") String username){
        log.info("用户{}已关闭连接", username);
        sessionMap.remove(username);
    }


    /**
     * 用于监听该连接上的任何错误,当客户端与该服务端点的连接发生任何异常,都将回调该注解标注的方法
     * 注意该方法的参数必选Throwable,可选Sessiion以及0-n个String参数,且String参数需要使用@PathParam注解标注
     * @param throwable
     * @param username
     */
    @OnError
    public void onError(Throwable throwable,@PathParam(value = "username") String username){
        log.error("用户{}连接发生异常", username);
    }


    /**
     * 用来发送消息的方法,参数分别为接收消息的用户的session,和对应的消息
     */
    private void sendMessage(Session toSession,String msg){
        try {
            toSession.getBasicRemote().sendText(msg);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


}

前端Vue代码

        代码解析注释里面已经存在,就不过多阐述

<template>
  <div class="chat-container">
    <div class="chat-header">
      <span class="chat-header-text">{{ userFriend.userFriendNickname }}</span>
    </div>
    <div class="chat-messages">
      <div v-for="message in messages" :key="message.id" class="chat-message">
        <div v-if="message.email === user.email " class="chat-message-right">
          <div class="chat-message-author">{{ message.nickName }}</div>
          <div class="chat-message-avatar" :style="{ backgroundImage: `url(${message.cover})` }"></div>
          <div class="chat-message-text">{{ message.text }}</div>
        </div>
        <div v-else class="chat-message-left">
          <div class="chat-message-author">{{ message.nickName }}</div>
          <div class="chat-message-avatar" :style="{ backgroundImage: `url(${message.cover})` }"></div>
          <div class="chat-message-text">{{ message.text }}</div>
        </div>
      </div>
      <div v-if="messages.length === 0" class="no-message-tip">暂无消息</div>
    </div>
    <div class="chat-input-container">
      <input v-model="inputText" placeholder="输入消息" @keydown.enter="send" />
      <button @click="send">发送</button>
    </div>
  </div>
</template>
<script>
  // 创建websocket
  let webSocket = null;

  export default {
    name: "Chat",
    data() {
      return {
        // toUser: "",
        messages: [],
        // userName: "",
        inputText: "",
        messageId: 1,
        user:{
          cover:"",
          email:"",
          nickName:""
        },
        userFriend:{
          userEmail:"",
          userFriendEmail:"",
          userFriendNickname:"",
          userFriendCover:""
        }
      };
    },
    methods: {
      sendMessage(message) {         //当前账号发送信息
        if (message !== "") {
          this.messages.push({
            id: this.messageId++,
            email:this.user.email,           //当前账号
            nickName: this.user.nickName,    //当前账号昵称
            text: message,                   //当前发送的信息
            cover: this.user.cover          //当前头像的url
          });
          // console.log(this.messages)
          this.inputText = "";
        }
      },
      responseMessage(message) {         //响应信息
        if (message !== "") {
          this.messages.push({
            id: this.messageId++,
            email:this.userFriend.userFriendEmail,
            nickName: this.userFriend.userFriendNickname,    //对方昵称
            text: message,                   //对方发送的信息
            cover: this.userFriend.userFriendCover          //对方头像的url
          });
          // console.log(this.messages)
          this.inputText = "";
        }
      },
      connectWebSocket() {       //获取链接
        const userName = this.user.email;      //当前账号
        console.log(this.userFriend)
        const target = "ws://localhost:8081/chat/" + userName;
        if ("WebSocket" in window) {         //判断浏览器是否支持,创建websocket对象
          webSocket = new WebSocket(target);
        } else {
          alert("浏览器不支持websocket");
        }

        webSocket.onerror = function () {
          alert("发生错误连接失败");
        };

        webSocket.onopen = function () {
          console.log("连接成功");
        };

        webSocket.onmessage = (res) => {
          console.log(res)
          this.responseMessage(res.data);     //res.data对方响应的信息
        };

        webSocket.onclose = function () {
          // this.sendMessage("Loc MSG:关闭连接");
          console.log("关闭连接");
        };

        window.onbeforeunload = function () {
          webSocket.close();
        };
      },
      closeWebSocket() {
        webSocket.close();
      },
      send() {
        const msg = this.inputText.trim();
        if (msg !== "") {
          const toUser = this.userFriend.userFriendEmail;
          const chatMsg = {
            msg: msg,
            to: toUser
          };
          webSocket.send(JSON.stringify(chatMsg));
          this.sendMessage(msg);
        }
      },
      sendToUser() {
        //获取当前用户账号和发送信息用户账号
        this.user = JSON.parse(sessionStorage.getItem("user"));
        // console.log(this.user)
        const userFriend = JSON.parse(sessionStorage.getItem("userFriendEmail"));
        // console.log(userFriend)
        this.userFriend.userFriendEmail = userFriend.userFriendEmail;
        this.userFriend.userFriendNickname = userFriend.userFriendNickname;
        this.userFriend.userFriendCover = userFriend.userFriendCover;
        // console.log(this.userFriend)
      },
    },
    mounted() {
      this.sendToUser();
      this.connectWebSocket();
    },
  };
</script>

<style scoped>
  .chat-container {
    width: 400px;
    height: 500px;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
    display: flex;
    flex-direction: column;
  }

  .chat-header {
    padding: 10px;
    background-color: #1890ff;
    color: #fff;
    font-size: 18px;
    font-weight: bold;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .chat-header-text {
    width: 100%;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .chat-messages {
    flex-grow: 1;
    padding: 10px;
    overflow-y: auto;
    background-color: #f5f5f5;
    border-top: 1px solid #ccc;
    border-bottom: 1px solid #ccc;

  }

  .chat-message {
    margin-bottom: 10px;
  }

  .chat-message-left {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
  }

  .chat-message-avatar {
    /*变为行元素*/
    display: inline;
    width: 40px;
    height: 40px;
    background-size: cover;
    background-position: center;
    border-radius: 50%;
    margin-right: 10px;
  }

  .chat-message-author {
    font-weight: bold;
    margin-bottom: 5px;
  }

  .chat-message-right .chat-message-text {
    display: inline;
    position:relative;
    top: -40px;
    right: 55px;
    word-wrap: break-word;
    max-width: 200px;
    padding: 8px;
    background-color: #fff;
    border-radius: 5px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
  }
  .chat-message-left .chat-message-text {
    display: inline;
    position:relative;
    top: -40px;
    right: -45px;
    word-wrap: break-word;
    max-width: 200px;
    padding: 8px;
    background-color: #fff;
    border-radius: 5px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
  }

  .chat-message-right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
  }

  .chat-input-container {
    display: flex;
    align-items: center;
    padding: 10px;
    background-color: #f5f5f5;
    border-top: 1px solid #ccc;
  }

  .chat-input-container input {
    flex-grow: 1;
    height: 30px;
    margin-right: 10px;
    padding: 5px;
    border-radius: 5px;
    border: 1px solid #ccc;
    outline: none;
    font-size: 14px; /* 添加字体大小样式 */
  }

  .chat-input-container button {
    padding: 5px 10px;
    background-color: #1890ff;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    outline: none;
    font-size: 14px; /* 添加字体大小样式 */
  }

</style>

成品图

        

完成全双关双向通信

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Spring Boot的WebSocket实现支持简单的即时聊天应用程序的构建,可以在服务器和客户端之间双向传输消息。主要步骤如下:1. 添加WebSocket依赖 2. 实现WebSocketConfigurer接口,并重写registerWebSocketHandlers()方法 3. 编写WebSocket消息处理器 4. 测试WebSocket应用程序 ### 回答2: 实现Spring Boot中的WebSocket聊天功能代码可以按照以下步骤进行: 1. 首先,确保你的项目中已添加了相关的依赖,包括`spring-boot-starter-websocket`和`spring-boot-starter-thymeleaf`。可以在`pom.xml`中添加如下依赖: ```xml <dependencies> <!-- Spring Boot WebSocket --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <!-- Spring Boot Thymeleaf --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> </dependencies> ``` 2. 创建一个`WebSocketConfig`类,用于配置WebSocket: ```java import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { private final WebSocketHandler webSocketHandler; public WebSocketConfig(WebSocketHandler webSocketHandler) { this.webSocketHandler = webSocketHandler; } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketHandler, "/chat").setAllowedOrigins("*"); } } ``` 3. 创建一个`WebSocketHandler`类,实现WebSocket的处理逻辑: ```java import org.springframework.stereotype.Component; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketMessage; import org.springframework.web.socket.WebSocketSession; import java.io.IOException; import java.util.ArrayList; import java.util.List; @Component public class ChatWebSocketHandler implements WebSocketHandler { private List<WebSocketSession> sessions = new ArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { for (WebSocketSession s : sessions) { s.sendMessage(new TextMessage(message.getPayload().toString())); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { sessions.remove(session); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { session.close(); } @Override public boolean supportsPartialMessages() { return false; } } ``` 4. 创建一个`ChatController`类,用于处理聊天页面的请求: ```java import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class ChatController { @GetMapping("/chat") public String chat() { return "chat"; } } ``` 5. 创建一个`chat.html`文件,作为聊天页面的视图: ```html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Chat</title> </head> <body> <div id="messageContainer"></div> <script> var socket = new WebSocket("ws://" + location.host + "/chat"); socket.onmessage = function (event) { var messageContainer = document.getElementById('messageContainer'); var message = document.createElement('p'); message.innerHTML = event.data; messageContainer.appendChild(message); }; document.onkeydown = function (event) { if (event.keyCode === 13) { var input = document.querySelector('input[name="message"]'); socket.send(input.value); input.value = ''; } }; </script> </body> </html> ``` 6. 启动应用,并在浏览器中访问`http://localhost:8080/chat`,即可看到聊天页面。在多个浏览器窗口中打开该页面,它们之间可以实时互相发送消息并显示在页面上。 这是一个简单的Spring Boot中实现WebSocket聊天功能的代码示例,你可以根据实际需求进行调整和扩展。 ### 回答3: Spring Boot通过Spring WebSocket提供了实现WebSocket功能。下面是实现基本聊天功能的Spring Boot WebSocket的代码。 1. 创建一个Spring Boot项目,并添加以下Maven依赖到pom.xml文件中: ```xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> </dependencies> ``` 2. 创建一个WebSocket配置类,用于配置WebSocket相关的Bean。 ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new ChatHandler(), "/chat").setAllowedOrigins("*"); } } ``` 3. 创建一个WebSocket处理程序,用于处理来自客户端的WebSocket消息。 ```java public class ChatHandler extends TextWebSocketHandler { private List<WebSocketSession> sessions = new ArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { for (WebSocketSession s : sessions) { s.sendMessage(message); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); } } ``` 4. 创建一个Controller类,用于处理聊天页面的请求。 ```java @Controller public class ChatController { @GetMapping("/chat") public String chat() { return "chat"; } } ``` 5. 在templates目录下创建一个chat.html文件,用于展示聊天页面。 ```html <!DOCTYPE html> <html> <head> <title>Chat</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script> var socket = new WebSocket("ws://localhost:8080/chat"); socket.onmessage = function(event) { var message = event.data; $("#chat").append('<p>' + message + '</p>'); } function sendMessage() { var message = $("#message").val(); socket.send(message); $("#message").val(""); } </script> </head> <body> <input type="text" id="message"><button onclick="sendMessage()">Send</button> <div id="chat"></div> </body> </html> ``` 以上代码实现了一个简单的聊天功能,客户端向服务端发送消息并显示在页面中,同时服务端将消息广播给所有客户端。通过访问http://localhost:8080/chat可以打开聊天页面。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值