原创WebSocket--使用/教程/实例

Eliauk阅读量 102

原文网址:WebSocket--使用/教程/实例_IT利刃出鞘的博客-CSDN博客

简介

说明

本文介绍长连接技术:WebSocket。

在线WebSocket测试

websocket/ws/wss在线调试测试工具 (显示页面中等,会打印错误码,可清空, 保留输入)
websocket在线测试 (显示页面大,不打印错误码,不可清空,不保留输入)
http://tool.hibbba.com/websocket/ (显示页面大,不打印错误码,不可清空)

WebSocket介绍

说明

WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)。

首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充。可以通过这样一张图理解:

![图片]:(https://img-blog.csdn.net/20171009105859554?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYTc4MjcwNTI4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)

ajax/长轮询/WebSocket/

ajax轮询:浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。

long poll:原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。

WebSocket:通过HTTP/HTTPS协议握手后创建一个TCP连接,服务端与客户端通过此TCP连接进行实时通信。

用法

集成方式

SpringBoot有4种集成WebSocket的方式:原生注解,Spring封装,STOMP,TIO。原生注解比较常用,本文使用原生注解。

端口共用

WebSocket与Tomcat共用一个端口。

内置Tomcat与独立Tomcat区别

使用SpringBoot内置Tomcat与使用独立Tomcat配置有所不同。使用SpringBoot内置Tomcat:需要提供一个ServerEndpointExporter的Bean;独立的Tomcat容器:不要注入ServerEndpointExporter,因为它将由容器自己提供和管理。

单例与多个Bean

虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。

示例

代码

依赖

复制代码
implementation 'org.springframework.boot:spring-boot-starter-websocket'

spring-boot-starter-websocket内部引入了spring-boot-starter-web 。

配置

java 复制代码
package com.example.demo.config;

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
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

发送/接收

java 复制代码
package com.example.demo.common;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Slf4j
@Component
@ServerEndpoint(value = "/ws/{token}")
public class WebSocketServer {
    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //保存客户端所对应的WebSocketServer
    private static Map<String, WebSocketServer> clientMap = new ConcurrentHashMap<>();

    private String token;

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    // 连接建立成功调用的方法
    @OnOpen
    public void onOpen(@PathParam("token") String token, Session session) {
        //TODO 校验token
        this.session = session;
        addOnlineCount();
        clientMap.put(token, this);
        log.info("新连接加入!" + " token:" + token + "; session.getId():" + session.getId() + " 当前连接数:" + onlineCount);
    }

    // 连接关闭
    @OnClose
    public void onClose() {
        subOnlineCount();
        clientMap.remove(token);
        log.info("有一连接关闭,当前连接数为:" + onlineCount);
    }

    // 收到客户端消息
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        log.info("来自客户端的消息:" + message);
        sendMsgToAll(message);
    }

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

    public void sendMessage(String token, String message) throws IOException {
        if (!StringUtils.isEmpty(token) && clientMap.containsKey(token)) {
            clientMap.get(token).send(message);
            log.info("成功发送一条消息:" + message);
        } else {
            log.error("用户:" + token + ",不在线!");
        }
    }

    public void send(String message) throws IOException{
        this.session.getBasicRemote().sendText(message);
    }

    // 给所有客户端群发消息
    public void sendMsgToAll(String message) throws IOException {
        for (WebSocketServer item : clientMap.values()) {
            item.session.getBasicRemote().sendText(message);
        }
        log.info("成功群发一条消息:" + onlineCount);
    }

    public static synchronized int getOnlineCount() {
        return WebSocketServer.onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}

控制器

java 复制代码
package com.example.demo.controller;

import com.example.demo.common.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
@RequestMapping("")
public class HelloController {
    @Autowired
    WebSocketServer webSocketServer;

    @PostMapping("send")
    void send(String token, String message) {
        try {
            webSocketServer.sendMessage(token, message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

测试

前端连接

在线网址:websocket在线测试 输入连接地址:ws://localhost:8080/ws/abc

![图片]:(https://img-blog.csdnimg.cn/20201029185422618.png)

后端结果

复制代码
新连接加入! token:abc; session.getId():0 当前连接数:1

后端发送给前端

postman访问http://localhost:8080/send?message=Hello World&token=abc

后端结果

复制代码
成功发送一条消息:Hello World

在线网址结果

![图片]:(https://img-blog.csdnimg.cn/20201029185442781.png)

前端发送给后端

![图片]:(https://img-blog.csdnimg.cn/20201029185535667.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlaXlpbmcwY2FuZ2xhbmc=,size_16,color_FFFFFF,t_70)

后端结果

复制代码
来自客户端的消息:Welcome
成功群发一条消息:1

前端结果

![图片]:(https://img-blog.csdnimg.cn/20201029185646737.png)

关闭后端程序

后端结果

复制代码
发生错误!

前端结果

![图片]:(https://img-blog.csdnimg.cn/202010291857302.png)

其他网址

SpringBoot2.0集成WebSocket,实现后台向前端推送信息_★【World Of Moshow 郑锴】★-CSDN博客
Spring Boot 使用 WebSocket 实现消息推送 及 WebSocket原理_二一点-CSDN博客


复制代码
     ===========================
    【来源: CSDN】
    【作者: IT利刃出鞘】
    【原文链接】 https://knife.blog.csdn.net/article/details/108103610
    声明:转载此文是出于传递更多信息之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与本网联系,我们将及时更正、删除,谢谢。
0/300
全部评论0
0/300