使用WebSocket实现一个简易的聊天室

我这里的框架是SpringBoot

首先,我们要有一个前端页面

html 复制代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
      layout:decorate="layout">
<head>
  <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js" defer></script>
  <meta charset="UTF-8"/>
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
  <title>websocket</title>
</head>
<body>
<input type="text" placeholder="请输入您想显示的昵称" name="username" id="username" />
<button onclick="connection()">链接服务器</button>
<!--<button onclick="send()">发送数据到服务器</button>-->
<button onclick="closeSocket()">关闭连接</button>
<p style="border: 1px solid black;width: 680px;height: 500px" id="talkMsg"></p>
<input id="message"/><button id="sendBtn" onclick="send()">发送</button>
</body>
<script>
  let sock=""
  let username=""
  function connection(){
    username = $("#username").val()
    //ws是WebSocket协议
    sock = new WebSocket('ws://localhost:8080/v1/point/' + username);

    //WebSocket事件
    sock.onopen = () => {
      console.log("已经与服务器建立连接.")
    }
    sock.onmessage = (e) => {
      console.log("\n已获取服务器响应的数据.")
      console.log(e)
      document.getElementById("talkMsg").innerHTML = e.data
    }
    sock.onclose = () => {
      console.log("已关闭与服务器的连接.")
    }
    sock.onerror = (e) => {
      console.log("连接发生异常.")
      console.log(e)
    }
  }


  /**
   * 发送数据到服务端
   */
  function send() {
    // sock.send(JSON.stringify({'message': ' hello world! '}))
    // sock.send(JSON.stringify({'message': document.getElementById("message").value }))
    if(document.getElementById("message").value===""){
      alert("抱歉,消息不能为空^_^")
    }else{
      var message = username + ":" +document.getElementById("message").value
      sock.send(message)
      //发送完信息之后输入框变为空
      document.getElementById("message").value=""
      document.getElementById("talkMsg").innerHTML=""
    }
  }

  /**
   * 关闭当前用户与WebSocket的连接
   */
  function closeSocket() {
    //代码只能是 1000,或者[3000, 4999]之间
    let code = 3000;
    let reason = "我想关闭连接!";
    sock.close(code, reason)
  }
</script>
</html>

其次,要有相关配置类以及Controller

配置类

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration   //标明该类是配置类
public class WebSocketConfig {
    /**
     * @Bean 注解会把该方法的返回值当做一个JavaBean,存放在Spring上下文中,以供使用
     * ServerEndpointExporter类的作用是,会扫描所有的服务器端点,
     * 把带有  @ServerEndpoint 注解的所有类都添加进来
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

Controller

java 复制代码
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.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 该类是用于ws的
 * value:当前 WebSocket 服务的访问(监听)地址
 */
@Component  //生产对象,声明为类
@ServerEndpoint(value = "/v1/point/{username}")     //表明监听地址
public class EndPointController {

    //存储用户,通过一个Map来完成存储
    private static final Map<String,EndPointController> online = new ConcurrentHashMap<>();

    //发送数据是通过session对象实现的,那么就要给每个用户一个session对象
    private Session session;

    //记录所发出的信息
    private static StringBuffer stringBuffer = new StringBuffer();
    //以下方法都是被触发,而不是访问
    /**
     * 连接建立时被调用。
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session, EndpointConfig config) throws IOException {
//        System.out.println("连接已经建立.");
        this.session = session;     //等号之前的session指的是第24行的,后面的是该方法的
        online.put(username,this);
        //给前端的控制台返回数据
//        session.getBasicRemote().sendText(username+"已加入群聊");
        String message = username + "已加入群聊";
        stringBuffer.append(message + "<br/>");
//        broadcastAllUsers(stringBuffer.toString());
    }

    /**
     * 收到消息时被调用。
     * @param message 前端传递过来的消息。
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
//        System.out.println("收到了消息:" + message);
        //将消息推送到前端
//        session.getBasicRemote().sendText("谢谢,我收到了你的消息:" + message);

        stringBuffer.append(message);
        broadcastAllUsers(stringBuffer.toString());
    }

    /**
     * 连接关闭时被调用.
     *
     * @param session
     * @param reason  关闭的理由
     */
    @OnClose
    public void onClose(Session session, CloseReason reason) {
        System.out.println("连接已关闭,关闭理由:" + reason);
    }

    /**
     * 当连接发生异常时被调用
     *
     * @param session
     * @param e
     */
    @OnError
    public void onError(Session session, Throwable e) {
        System.out.println("连接发生异常:" + e.getMessage());
        e.printStackTrace();
    }

    //遍历map将数据发送给每个用户
    private void broadcastAllUsers(String message) throws IOException {
        Set<String> names = online.keySet();    //keySet返回所有key值列表
        for (String name : names) {
            EndPointController endPoint = online.get(name);
            //将数据发送给每一个人
            stringBuffer.setLength(0);
            stringBuffer.append(message+ "<br/>");
            endPoint.session.getBasicRemote().sendText(stringBuffer.toString());
//            System.out.println(stringBuffer.toString());
        }
    }
}
相关推荐
速盾cdn35 分钟前
速盾:vue的cdn是干嘛的?
服务器·前端·网络
叫我龙翔1 小时前
【计网】实现reactor反应堆模型 --- 框架搭建
linux·运维·网络
不爱学习的YY酱2 小时前
【计网不挂科】计算机网络期末考试——【选择题&填空题&判断题&简述题】试卷(4)
网络·计算机网络
装睡的小5郎2 小时前
家庭宽带如何开启公网ipv4和ipv6
网络
yfs10242 小时前
压缩Minio桶中的文件为ZIP,并通过 HTTP 响应输出
网络·网络协议·http
有谁看见我的剑了?2 小时前
Ubuntu 22.04.5 配置vlan子接口和网桥
服务器·网络·ubuntu
hgdlip2 小时前
有什么办法换网络ip动态
网络·tcp/ip·智能路由器
超栈2 小时前
HCIP(11)-期中综合实验(BGP、Peer、OSPF、VLAN、IP、Route-Policy)
运维·网络·网络协议·计算机网络·web安全·网络安全·信息与通信
დ旧言~2 小时前
【网络】应用层——HTTP协议
开发语言·网络·网络协议·http·php
不爱学习的YY酱2 小时前
【计网不挂科】计算机网络期末考试——【选择题&填空题&判断题&简述题】试卷(1)
网络·计算机网络