springboot整合webSocket(看完即入门)

1、什么是webSocket?

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在这里插入图片描述

2、webSocket可以用来做什么?

利用双向数据传输的特点可以用来完成很多功能,不需要前端轮询,浪费资源。例如:

  1. 通告功能

  2. 聊天功能 (如下是逻辑图)
    在这里插入图片描述

  3. 实时更新数据功能

  4. 弹幕
    等等。。。。。。

3、webSocket协议

本协议有两部分:握手和数据传输。
握手是基于http协议的。

来自客户端的握手看起来像如下形式:

GET ws://localhost/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: chat,superchat
Sec-WebSocket-Version: 13

来自服务器的握手看起来像如下形式:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

在这里插入图片描述

4、服务端

maven依赖

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

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

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

WebSocket配置类

@Configuration
public class WebSocketConfig {

    /**
     * 	注入ServerEndpointExporter,
     * 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        ServerEndpointExporter exporter = new ServerEndpointExporter();
        return exporter;
    }
}

WebSocket操作类

通过该类WebSocket可以进行群推送以及单点推送


import cn.hutool.json.JSONUtil;
import dto.Msg;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

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


/**
 * WebSocket 服务
 */
@Slf4j
@Getter
@ServerEndpoint("/ws") // 接口路径 ws://localhost:8080/ws
@Component
@Scope("prototype")
public class WebSocketServer {
    private final static String ALL_USER_ID = "ALL";
    // 用来存在线连接用户信息
    private static final Map<String, Session> userSessions = new ConcurrentHashMap<>();

    /**
     * 连接成功
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        userSessions.put(session.getQueryString(), session);
        log.info("连接成功, user:{},sessionId:{}", session.getQueryString(), session.getId());
    }

    /**
     * 接收到消息
     *
     * @param json
     */
    @OnMessage
    public void onMessage(String json, Session fromSession) {
        Msg message = JSONUtil.toBean(json, Msg.class);
        String from = message.getFrom();
        String to = message.getTo();
        String text = message.getText();
        log.info("服务器收到[{}]发送给[{}]的消息[{}]", from, to, text);

        //send
        if (StringUtils.hasText(to)) {
            if (to.equalsIgnoreCase(ALL_USER_ID)) {
                noticeAll(text);
            } else {
                Session toSession = userSessions.get(to);
                sendMessage(fromSession, "我" + ":" + text);
                sendMessage(toSession, from + ":" + text);
            }
        }
    }

    /**
     * 连接关闭
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        userSessions.forEach((key, item) -> {
            if (item.getQueryString().equals(session.getQueryString())) {
                userSessions.remove(key);
            }
        });
        log.info("连接关闭");
    }

    /**
     * 连接错误
     *
     * @param session
     */
    @OnError
    public void onError(Session session, Throwable error) {
        userSessions.forEach((key, item) -> {
            if (item.getQueryString().equals(session.getQueryString())) {
                userSessions.remove(key);
                sessionClose(session);
            }
        });
        log.info("连接出错", error);
    }

    public void noticeAll(String text) {
        userSessions.forEach((key, value) -> {
            sendMessage(value, text);
        });
    }

    private void sendMessage(Session session, String text) {
        if (session == null || !session.isOpen()) {
            return;
        }
        try {
            if (session.isOpen()) {
                session.getBasicRemote().sendText(text);
            } else {
                log.error("session:[{}] is closed!", session.getId());
            }
        } catch (IOException ioException) {
            log.error("send message error", ioException);
            sessionClose(session);
        }
    }

    private void sessionClose(Session session) {
        try {
            session.close();
        } catch (IOException e) {
            log.error("close session error", e);
        }
    }
}

封装的消息对象

/**
 * Ws消息对象
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Msg {
    private String from;
    private String to;
    private String text;
}

注入我们的操作类

@Resource
private WebSocket webSocket;

5、客户端

前端中Thymeleaf 使用WebSocket

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>Websocket Demo</title>
</head>

<body>
<button onclick="connection()">连接</button>
<button onclick="close()">关闭连接</button>
<br/>
<input id="from" type="text"/>
<br/>
<label for="to">发给:</label>
<input id="to" type="text"/>
<br/>
<label for="text">消息:</label>
<input id="text" type="text"/>

<button onclick="send()">发送消息</button>
<br/>

<hr/>
<div id="messages"></div>
<br/>


<script type="text/javascript">
    var websocket = null;


    function connection(){
        var username = document.getElementById('from').value;
        //判断当前浏览器是否支持WebSocket
        if ('WebSocket' in window) {
            websocket = new WebSocket("ws://localhost:8080/ws?"+username);//此处地址在分情况测试的时候需要修改
        } else {
            alert('Not support websocket')
        }

        //发生错误时
        websocket.onerror = function () {
            setMessageInnerHTML("error");
        };

        //成功建立连接时
        websocket.onopen = function (event) {
            setMessageInnerHTML("open");
        }

        //接收到消息时
        websocket.onmessage = function (event) {
            setMessageInnerHTML(event.data);
        }

        //关闭连接时
        websocket.onclose = function () {
            setMessageInnerHTML("close");
        }

        //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
        window.onbeforeunload = function () {
            websocket.close();
        }
    }

    //显示服务器端返回的消息
    function setMessageInnerHTML(text) {
        document.getElementById('messages').innerHTML = document.getElementById('messages').innerHTML + text + '<br/>';
    }

    //关闭连接按钮
    function close() {
        websocket.close();
    }

    //发送消息按钮
    function send() {
        var from = document.getElementById('from').value;
        var to = document.getElementById('to').value;
        var text = document.getElementById('text').value;
        var msg={
            from:from,
            to:to,
            text:text
        };
        console.log(msg);
        websocket.send(JSON.stringify(msg));
    }
</script>
</body>

</html>

在这里插入图片描述
接口调用顺序,进来页面 :

  1. 先建立连接–》调用websocketonopen方法,链接成功调用的方法
  2. websocketonmessage方法为接收后端时处理。
  3. 当我们要发送消息给后端时调用websocketsend。
  4. 当我们要关闭连接时调用websocketclose。
  5. 当发现错误时调用websocketonerror。
    在这里插入图片描述
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值