WebSocket的基本使用

什么是WebSocket?

简介

WebSocket是一种在单个TCP连接上进行全双工通信的协议,已被W3C定为标准。

使用WebSocket可以使得客户端和服务器之间的数据交换变得更加简单,它允许服务端主动向客户端推送数据。

在WebSocket协议中,浏览器和服务器只需要完成一次握手,两者之间就可以直接创建持久性的连接,并进行双向数据传输。

WebSocket使用了HTTP/1.1的协议升级特性,一个WebSocket请求首先使用非正常的HTTP请求以特定的模式访问一个URL,这个URL有两种模式,分别是ws和wss,对应HTTP协议中的HTTP和HTTPS,在请求头中有一个Connection:Upgrade字段,表示客户端想要对协议进行升级,另外还有一个Upgrade:websocket字段,表示客户端想要将请求协议升级为WebSocket协议。这两个字段共同告诉服务器要将连接升级为WebSocket这样一种全双工协议,如果服务端同意协议升级,那么在握手完成之后,文本消息或者其他二进制消息就可以同时在两个方向上进行发送,而不需要关闭和重建连接。此时的客户端和服务端关系是对等的,它们可以互相向对方主动发送消息。

特点

  • WebSocket使用时需要先创建连接,这使得WebSocket成为一种有状态的协议,在之后的通信过程中可以省略部分状态信息(例如身份认证等)。
  • WebSocket连接在端口80(ws)或者443(wss)上创建,与HTTP使用的端口相同,这样,基本上所有的防火墙都不会阻止WebSocket连接。
  • WebSocket使用HTTP协议进行握手,因此它可以自然而然地集成到网络浏览器和HTTP服务器中,而不需要额外的成本。
  • 使用该协议,当消息启动或者到达的时候,服务端和客户端都可以知道。
  • WebSocket连接关闭时将发送一个特殊的关闭消息。
  • WebSocket支持跨域,可以避免Ajax的限制。
  • HTTP规范要求浏览器将并发连接数限制为每个主机名两个连接,但是当我们使用WebSocket的时候,当握手完成之后,该限制就不存在了,因为此时的连接已经不再是HTTP连接了。
  • WebSocket既然具有这么多优势,使用场景当然也是非常广泛的,例如:
    在线股票网站、 即时聊天、多人在线游戏、应用集群通信等等。

原理

  • websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信
  • 在websocket出现之前,web交互一般是基于http协议的短连接或者长连接
  • websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"
  • 按照传统的定义,WebSocket是一种双工协议,主要用于客户端-服务器通信通道。它本质上是双向的,这意味着通信在客户端与服务器之间来回发生。
  • 使用 WebSocket 开发的连接只要任何参与方中断连接就会持续存在。一旦一方断开连接,另一方将无法进行通信,因为连接会在其前面自动断开。
  • WebSocket需要HTTP的支持来发起连接。说到它的实用性,当涉及到数据的无缝流和各种不同步流量时,它是现代 Web 应用程序开发的支柱。

WebSocket 运用场景

WebSocket 是一种重要的客户端-服务器通信工具,人们需要充分了解其实用性并避免使用其最大潜力的场景。

在以下情况下使用 WebSocket:

1.开发实时网络应用程序

WebSocket 最常见的用途是实时应用程序开发,其中它有助于在客户端连续显示数据。当后端服务器不断发回这些数据时,WebSocket 允许在已经打开的连接中不间断地推送或传输这些数据。WebSocket 的使用使此类数据传输变得快速并充分利用了应用程序的性能。

此类 WebSocket 实用程序的一个现实示例是比特币交易网站。在这里,WebSocket 协助部署的后端服务器向客户端发送数据处理。

‍ 2.创建聊天应用程序

聊天应用程序开发人员在一次性交换和发布/广播消息等操作中向 WebSocket 寻求帮助。由于使用相同的 WebSocket 连接来发送/接收消息,因此通信变得简单快捷。

‍ 3.正在开发游戏应用程序

在游戏应用程序开发过程中,服务器必须不间断地接收数据,而不要求 UI 刷新。WebSocket 可以在不影响游戏应用程序 UI 的情况下实现这一目标。

既然已经清楚了应该在哪里使用 WebSocket,请不要忘记了解应该避免使用 WebSocket 的情况,让自己远离大量的操作麻烦。

当需要获取旧数据或仅需要一次性处理数据时,不应该使用 WebSocket。在这些情况下,使用 HTTP 协议是明智的选择。

WebSocket 与 HTTP

  • 由于 HTTP 和 WebSocket 都用于应用程序通信,因此人们经常感到困惑,并且很难从这两者中选择一个。看一下下面提到的文本,可以更清楚地了解 HTTP 和 WebSocket。

  • 如前所述,WebSocket 是一种框架式双向协议。相反,HTTP 是一个在 TCP 协议之上运行的单向协议。

  • 由于WebSocket协议能够支持连续的数据传输,因此主要用于实时应用程序开发。HTTP 是无状态的,用于开发RESTful和 SOAP 应用程序。Soap仍然可以使用HTTP来实现,但是REST被广泛传播和使用。

  • 在 WebSocket 中,通信发生在两端,这使其成为更快的协议。在 HTTP 中,连接是在一端建立的,这使得它比 WebSocket 有点慢。

  • WebSocket使用统一的TCP连接,需要一方终止连接。在发生这种情况之前,连接将保持活动状态。HTTP 需要为单独的请求构建不同的连接。请求完成后,连接会自动断开。

总结:

  1. 首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等
  2. 然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据
  3. 最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信

WebSocket入门案例

1、基于servlet进行服务端通讯

导入依赖:

        <!-- websocket依赖 -->
        <dependency>
            <groupId>javax.websocket</groupId>
            <artifactId>javax.websocket-api</artifactId>
            <version>1.1</version>
            <scope>provided</scope>
        </dependency>

创建一个WebSocket的服务端:

/**
 * WebSocket的服务端
 *
 * @ServerEndpoint注解表示当前类是一个websocket
 * 的服务端,value属性指定一个连接服务端的url地址
 */
@ServerEndpoint("/connect")
@Slf4j
public class WebSocketServer {

    //用户列表
    private static final List<Session> users  = new ArrayList<>();

    /**
     * 打开链接的方法,只有在客户端连接服务端的时候
     * 这个方法会调用一次
     *
     * session表示一个websocket客户端的连接会话,
     * 每一个客户端连接就会创建一个Session会话
     */
    @OnOpen
    public void onOpen(Session session) {
        log.info("客户端已连接");
        //将session添加到用户列表中
        users.add(session);
    }

    /**
     * 接收客户端发送的消息方法
     */
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        log.info("消息:" + message);
        //向当前客户端发送一个消息
        //session.getBasicRemote().sendText("Hello client");
        //群发消息
        for(Session s : users) {
            s.getBasicRemote().sendText(message);
        }
    }

    /**
     * 当客户端断开连接后调用此方法
     */
    @OnClose
    public void onClose(Session session) {
        log.info("客户端已断开连接");
        //用户离线
        users.remove(session);
    }
}

这里服务端创建好后,使用html页面进行测试

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>客户端</title>
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <h1>websocket客户端</h1>
    请输入消息:<input v-model="sendMsg" type="text">
    <button @click="onSend">发送</button>

    <div>
        <h2>消息</h2>
        <p v-for="msg in messages">{{msg}}</p>
    </div>
</div>
</body>
<script>
    new Vue({
        el:"#app",
        data:{
            messages:[],
            sendMsg:"",
            ws: null
        },
        methods:{
            onSend() {
                this.ws.send(this.sendMsg)
            },
            initWS(){
                console.log("服务端初始化")
                let that = this
                this.ws = new WebSocket("ws://localhost:8080/connect")
                this.ws.onmessage = function (event) {
                    console.log("这是消息初始化")
                    console.log(that.messages.push(event.data))
                    console.log(event.data)
                }
            },
        },
        mounted(){
            this.initWS()
        }
    })
</script>
</html>

这里可以看到,当我们前端创建了WebSocket对象会跟WebSocket的服务端进行连接,只需要在输入框中输入需要发送的信息,服务端就会接收到前端发送过来的信息。

2、基于servlet进行群发布

首先,创建一个html页面 login.html 进行登录,将用户保存到HttpSession会话中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<div>
    <form action="/login" method="post">
        账号:<input type="text" name="userName"><br>
        <input type="submit" value="登录">
    </form>
</div>
</body>
</html>

创建Servlet,将用户信息保存到会话作用域中,然后进行重定向到发送信息页面

@WebServlet("/login")
@Slf4j
public class LoginServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String userName = req.getParameter("userName");
        log.info("servlet用户名:{}",userName);
        HttpSession session = req.getSession();
        session.setAttribute("userName",userName);
        resp.sendRedirect("chat.html");
    }
}

创建发送信息的页面 chat.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>
    <script src="js/jquery.min.js"></script>
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="msg">
    <input type="button" value="发送" @click="send()">
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            ws:null,
            msg:""
        },
        methods:{
            initWS(){
                let that = this;
                this.ws = new WebSocket("ws://localhost:8080/connect")
                this.ws.onmessage = function (ev) {
                    let dataMsg = ev.data;
                    dataMsg = JSON.parse(dataMsg)
                    console.log(dataMsg)
                }
            },
            send(){
                this.ws.send(this.msg)
            }
        },
        mounted(){
            this.initWS()
        }
    })
</script>
</body>
</html>

创建握手处理类,因为在用户的信息保存在HttpSession中,websocket在交互前的第一次请求是基于http协议进行握手,因此可以在这个类中得到握手请求对象,从而得到HttpSession的信息

public class WebSocketHandshake extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        //获取HttpSession对象
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        //获取用户名
        String userName = (String) httpSession.getAttribute("userName");
        //将用户名保持到当前用户连接websocket的Session中
        sec.getUserProperties().put("userName",userName);
    }
}

创建WebSocket服务端

/**
 * configurator属性指定自定义的握手连接处理类
 */
@ServerEndpoint(value = "/connect",
        configurator = WebSocketHandshake.class)
public class ChatServer {

    // 用户列表
    // key为用户的id或者是name,
    // value则是每一个客户端的Session
    private static final Map<String, Session> users = new HashMap<>();

    @OnOpen
    public void onOpen(Session session) {
        //获取Session中的用户名
        String userName = (String) session.getUserProperties().get("user");
        //添加到用户列表中
        users.put(userName, session);
    }

    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
        //获取发送人
        String formUser = (String) session.getUserProperties().get("user");
        //创建发送时间
        String sendTime = new SimpleDateFormat("hh:mm").format(new Date());
        //封装消息对象并序列为json
        Message msg = new Message(formUser, sendTime, message);
        String jsonMessage = new ObjectMapper().writeValueAsString(msg);
        //群发给所有人
        for(String userName : users.keySet()) {
            Session s = users.get(userName);
            s.getBasicRemote().sendText(jsonMessage);
        }
    }

    @OnClose
    public void onClose(Session session) {
        //将用户移除在线列表
        String userName = (String) session.getUserProperties().get("user");
        users.remove(userName);
    }
}

这就实现了一个群发布的功能,只要是其中一个人发送了信息,那么所有登录的后建立连接的人都能收到信息。

3、基于SpringMVC进行群发布

创建一个dto,用来保存信息

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
    private String fromUser;
    private String sendTime;
    private String content;
}

创建WebSocket服务端,Spring对WebSocket服务端进行了封装

/**
 * spring封装的websocket服务端,可以继承一个TextWebSocketHandler,
 * 表示这个服务端用于处理文本数据的消息
 */
public class ChatServer extends TextWebSocketHandler {

    //用户列表
    //每一个用户连接时都会创建一个WebSocketSession对象
    private static final Map<String, WebSocketSession>
             users = new HashMap<>();


    /**
     * 客户端建立连接后执行的方法,等效于onOpen方法
     * @param session
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
         //获取登录的用户信息
         String userName = (String) session.getAttributes().get("user");
         //保存到用户列表
         users.put(userName, session);
    }

    /**
     * 接收客户端的消息,等效于onMessage方法
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //获取消息载体,也就是客户端发送的文本内容
        String msgContent = message.getPayload();
        //获取发送人
        String fromUser = (String) session.getAttributes()
                .get("user");
        //发送时间
        String sendTime = new SimpleDateFormat("hh:mm")
                .format(new Date());
        //封装消息对象
        Message msg = new Message(fromUser, sendTime, msgContent);
        //序列化为json字符串
        String json = new ObjectMapper().writeValueAsString(msg);
        //群发给所有人
        for(String userName : users.keySet()) {
            WebSocketSession s = users.get(userName);
            //发送消息,必须是TextMessage的对象
            s.sendMessage(new TextMessage(json));
        }
    }

    /**
     * 连接关闭后执行的方法,等效于onClose方法
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        String userName = (String) session.getAttributes().get("user");
        //将用户移除在线列表
        users.remove(userName);
    }
}

创建WebSocket配置类,将服务端类纳入到容器中管理

@Configuration
//启用websocket支持
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    /**
     * 装配服务端
     * @return
     */
    @Bean
    public WebSocketHandler webSocketHandler() {
        return new ChatServer();
    }

    /**
     * 装配HttpSession的拦截器,这样就可以在握手阶段
     * 获取HttpSession的内容,在使用WebSocketSession时
     * 就能直接得到HttpSession的数据
     * @return
     */
    @Bean
    public HandshakeInterceptor handshakeInterceptor() {
        return new HttpSessionHandshakeInterceptor();
    }

    /**
     * 给服务端注册请求的端点(映射连接地址)
     * @param registry
     */
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
                //给ChatServer设置连接的url
        registry.addHandler(webSocketHandler(), "/connect")
                //设置握手拦截器
                .addInterceptors(handshakeInterceptor());
    }
}

创建MvcConfig

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

创建主配置类SpringConfig

@Configuration
@ComponentScan(basePackages = "edu.nf.ch03")
@Import({MvcConfig.class, WebSocketConfig.class})
public class AppConfig {
}

创建WebConfig配置类

public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{AppConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

最后创建控制层和页面进行测试

@Controller
public class UserController {

    /**
     * 简单的用户登录
     * @param userName
     * @param session
     * @return
     */
    @PostMapping("/user/login")
    public String login(String userName, HttpSession session) {
        //将用户信息保存到会话作用域
        session.setAttribute("user", userName);
        //重定向到聊天的首页
        return "redirect:/static/chat.html";
    }
}
}

登录页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户登录</h1>
<form name="f1" method="post" action="../user/login">
Name:<input type="text" name="userName"/>
<input type="submit" value="登录"/>
</form>
</body>
</html>

发送信息页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>
    <script src="js/jquery.min.js"></script>
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <input type="text" v-model="msg">
    <input type="button" value="发送" @click="send()">
</div>

<script>
    new Vue({
        el:"#app",
        data:{
            ws:null,
            msg:""
        },
        methods:{
            initWS(){
                let that = this;
                this.ws = new WebSocket("ws://localhost:8080/connect")
                this.ws.onmessage = function (ev) {
                    let dataMsg = ev.data;
                    dataMsg = JSON.parse(dataMsg)
                    console.log(dataMsg)
                }
            },
            send(){
                this.ws.send(this.msg)
            }
        },
        mounted(){
            this.initWS()
        }
    })
</script>
</body>
</html>

最后的结果和Servlet实现的效果一样,都是能够实现群发功能。

STOMP

STOMP简单介绍

协议上节中我们在上面简单介绍了Spring中对于WebSocket的封装,并实现一个简单的服务端,这节我们将会结合STOMP子协议实现WebSocket通信。

WebSocket协议定义了两种消息类型(文本类型和二进制类型),但是消息内容却是未定义的。

STOMP (Simple Text Oriented Messaging Protocol) 起源于脚本语言,比如Ruby、Python和Perl,用于连接企业消息代理,它可以用于任何可靠的双向网络协议中,如TCP和WebSocket。尽管STOMP是一个面向文本的协议,但消息负载可以是文本或者二进制。

STOMP基于WebSocket在客户端和服务端之间定义了一种机制,协商通过子协议(更高级的消息协议)来定义可以发送何种消息,每条消息的内容是什么,等等。

STOMP协议内容

STOMP是一个基于帧的协议,帧的结构如下:

COMMAND
header1:value1
header2:value2

Body

客户端可以用SEND或者SUBSCRIBE命令去发送和订阅消息,destination头部用来描述消息发送到哪里以及谁应该接收消息,下面的消息结构是客户端订阅股票行情的例子,如下:

SUBSCRIBE
id:sub-1
destination:/topic/price.stock.*

STOMP服务端可以使用MESSAGE命令广播消息给所有的订阅者,下面的例子为广播股票行情消息给所有消息订阅者

MESSAGE
message-id:nxahklf6-1
subscription:sub-1
destination:/topic/price.stock.MMM

{"ticker":"MMM","price":129.45}

使用STOMP的好处

在Spring中使用STOMP与原生WebSockets相比提供了更加丰富的编程模型,下面是使用STOMP的优点:

  • 不需要发明自定义消息协议和消息格式。
  • 在Spring中,STOMP客户端,包括Java客户端都可用。
  • 可使用其它消息代理管理消息订阅和广播,如RabbitMQ,ActiveMQ等支持STOMP协议的中间件。
  • 应用逻辑处理入口可以像Spring MVC一样统一在@Controller实例内,同时消息可以基于STOMP头部消息进行路由,而不是直接用WebSocketHandler处理原生WebSocket消息。
  • 可以基于STOMP目的地和消息类型使用Spring Security对消息进行安全传输。

代码示例

目前使用的是spring提供的消息代理
添加依赖:

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
            <version>5.3.3</version>
</dependency>

创建MVC配置类

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

创建WebSocket配置类

@Configuration
//启用websocket消息代理中间件
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    /**
     * 注册一个连接消息中间件的端点(路径url)
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("broker");
    }

    /**
     * 配置消息代理,主要是设置相关的主题
     * 消息代理是服务中心的核心,spring-websocket内置了
     * 一个简单的消息代理,但也只是能够满足基本要求,如果
     * 需要强大的消息中心的功能,通常都会集成第三方的消息队列
     * 例如:RabbitMQ等
     * @param registry
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //启用spring内置简单的消息代理并设置一个主题(topic)的前缀,
        //用于消息的发布和订阅
        registry.enableSimpleBroker("/news", "/video");
        //如果需要集成外部其他的消息代理,使用下面的方法
        //registry.enableStompBrokerRelay();
    }
}

创建主配置类

@Configuration
@ComponentScan(basePackages = "edu.nf.ch04")
@Import({MvcConfig.class, WebSocketConfig.class})
public class AppConfig {
}

创建Web配置类

public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{AppConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

创建消息对象:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
    private String content;
    private String sendTime;
}

创建控制层:

@RestController
@RequiredArgsConstructor
public class PublishController {

    /**
     * 消息处理模版,用于发布消息
     */
    private final SimpMessagingTemplate template;

    /**
     * 后台发布消息
     */
    @PostMapping("/publish/{topic}/{sub}")
    public void publish(@PathVariable("topic") String topic,
                        @PathVariable("sub") String sub,
                        String message) {
        String sendTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
        Message msg = new Message(message, sendTime);
        //将消息发布到消息代理指定的主题中
        template.convertAndSend("/" +topic +"/" + sub, msg);
    }
}

现在模拟一个消息发布于订阅功能,比如说现在发布者需要发布消息,创建一个页面代表发布者:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>后台发布</title>
    <script src="js/vue.js"></script>
    <script src="js/jquery.min.js"></script>
</head>
<body>
<div id="app">
    <select v-model="pathName">
        <option value="video">影视</option>
        <option value="news">娱乐</option>
    </select>
    <input type="text" name="message" v-model="msg">
    <button @click="OnSend">发送</button>
</div>
</body>
<script>
    new Vue({
        el:"#app",
        data:{
            msg:"",
            pathName:"video",
        },
        methods:{
            OnSend(){
                $.ajax({
                    url:"publish/"+this.pathName,
                    type:"post",
                    data:{
                        message:this.msg
                    },
                    success:() => {

                    },
                    error:() => {

                }
                })
            }
        },
    })
</script>
</html>

创建两个客户端对,比如说者两个用户都订阅了,那发布者可以对两个用户分别发布信息:

client.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>客户端</title>
    <script src="js/stomp.min.js"></script>
    <script src="js/jquery.min.js"></script>
    <script src="js/vue.js"></script>
</head>
<body>
<div id="app">
    <h1>消息:{{msg.content}}</h1>
</div>
</body>
<script>
    new Vue({
        el: "#app",
        data: {
            msg: {}
        },
        methods: {
            initWS() {
                let that = this
                //创建WebSocket对象
                let ws = new WebSocket("ws://localhost:8080/broker");
                //将WebSocket包装成stomp客户端
                let stompClient = Stomp.over(ws);
                //连接服务器并订阅消息
                stompClient.connect({}, function () {
                    //执行订阅
                    stompClient.subscribe("/news/sport", function (data) {
                        //接收发布的通知内容
                        let message = JSON.parse(data.body);
                        that.msg = message
                        console.log(message)
                    })
                }, {})
            }
        },
        mounted() {
            this.initWS()
        }
    })
</script>
</html>

client2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>客户端</title>
    <script src="js/stomp.min.js"></script>
    <script src="js/jquery.min.js"></script>
    <script src="js/vue.js"></script>
</head>
<body>
  <div id="app">
      <h1>消息:{{msg}}</h1>
  </div>
</body>
<script>
  new Vue({
    el:"#app",
    data:{
        msg:""
    },
    methods:{
      initWS(){
        //创建WebSocket对象
        let ws = new WebSocket("ws://localhost:8080/broker");
        //将WebSocket包装成stomp客户端
        let stompClient = Stomp.over(ws);
        //连接服务器并订阅消息
        stompClient.connect({},function () {
          //执行订阅
          stompClient.subscribe("/video/sport",function (data) {
            //接收发布的通知内容
              let message = JSON.parse(data.body);
              console.log(message)
          })
        },{})
      }
    },
    mounted(){
        this.initWS()
    }
  })
</script>
</html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值