Guiyunweb's blog

使用spring boot 搭建WebSocket

发表于: 2020-04-02 08:43:44标签:WebSocket spring-boot 阅读: 93
使用spring boot 搭建WebSocket

WebSocket

  • 什么是WebSocket:

    WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

    普通http协议有一个严重的缺陷,通信只能有客户端发起.假如服务端有了连续的变化,客户端如果想获知,只能用轮询来获取.但轮询的效率低,非常浪费资源,这时WebSocket就此诞生了.

    WebSocket 于2008年诞生,2011年成为国际标准。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种.

    其他特点包括:

    1. 建立在 TCP 协议之上,服务器端的实现比较容易
    2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议
    3. 数据格式比较轻量,性能开销小,通信高效
    4. 可以发送文本,也可以发送二进制数据
    5. 没有同源限制,客户端可以与任意服务器通信
    6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL


下面简单介绍一下spring boot下如何整合WebSocket

这里使用tio框架进行WebSocket服务的搭建

plugins {
	id 'org.springframework.boot' version '2.2.6.RELEASE'
	id 'io.spring.dependency-management' version '1.0.9.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	implementation 'org.t-io:tio-websocket-spring-boot-starter:3.6.0.v20200315-RELEASE'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

引入tio-websocket-spring-boot-starter依赖

构建服务端

  1. 给SpringBoot Application 主类添加 @EnableTioServerServer 注解

    @SpringBootApplication
    @EnableTioWebSocketServer
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    
    }
    
  2. 修改配置文件

    tio:
      websocket:
        server:
        port: 9998
        # 心跳时间
        heartbeat-timeout: 60000
    
  3. 创建消息处理类

    @Configuration
    public class MyWebSocketMsgHandler implements IWsMsgHandler {
    
    
        /**
        * 握手时走这个方法,业务可以在这里获取cookie,request参数等
        */
        @Override
        public HttpResponse handshake(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
            return httpResponse;
        }
    
        /**
        * 握手成功后触发该方法
        */
        @Override
        public void onAfterHandshaked(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
            System.out.println("握手成功");
        }
    
        /**
        * 当ws是基于BINARY(字节)传输的,会执行此方法
        */
        @Override
        public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
            System.out.println("接收到bytes消息");
            return null;
        }
    
        /**
        * 当客户端发close flag时,会走这个方法
        */
        @Override
        public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
            return null;
        }
    
        /**
        * 当ws是基于TEXT传输的,就会走到这个方法
        */
        @Override
        public Object onText(WsRequest wsRequest, String s, ChannelContext channelContext) throws Exception {
            System.out.println("接收到文本消息:"+s);
            return fakeAi(s);
        }
    
        private String fakeAi(String input){
            if (input == null || "".equals(input)) {
                return "你说什么?没听清︎";
            }
            return input.replace('你', '我')
                    .replace("吗", "")
                    .replace('?', '!')
                    .replace('?', '!');
        }
    }
    
  4. 构建客户端

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>WebSocket客户端</title>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
    <style>
        .jumbotron {
            width: 100%;
        }

        #text {
            height: 3rem;
            font-size: 1rem;
            line-height: 3rem;
            margin: 1rem;
        }

        .btn {
            margin-right: 5px;
        }

        #connect {
            margin-left: 1rem;
        }

        #log {
            margin: 1rem 0 0 1rem;
        }
    </style>
    <div class="container">
        <div class="row">
            <div class="jumbotron">
                <input type="text" placeholder="请输入你想传输的内容" id="text" class="col-lg-12" />
                <input type="button" value="连接" class="btn btn-info" id="connect" onclick="connect()" />
                <input type="button" value="发送" class="btn btn-success" id="sent" disabled="disabled"
                    onclick="sent()" />
                <input type="button" value="断开" class="btn btn-danger" id="disconnect" disabled="disabled"
                    onclick="disconnect()" />

                <div id="log">
                    <p>聊天记录:</p>
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript">
        let text = document.querySelector('#text');
        let connectBtn = document.querySelector("#connect");
        let sentBtn = document.querySelector("#sent");
        let disconnectBtn = document.querySelector("#disconnect");
        let logDiv = document.querySelector("#log");

        let ws = null;

        function connect() {
            ws = new WebSocket("ws://localhost:9998/test");
            ws.onopen = function () {
                setConnected(true);
                log('和服务端连接成功!');
            };
            ws.onmessage = function (event) {
                log('服务端说:' + event.data);
            };
            ws.onclose = function () {
                setConnected(false);
                log('和服务端断开连接!')
            }
        }

        function sent() {
            if (ws != null) {
                ws.send(text.value);
                log('客户端说:' + text.value);
            } else {
                log('请先建立连接!')
            }
        }

        function disconnect() {
            if (ws != null) {
                ws.close();
                ws = null;
            }
            setConnected(false);
        }

        function log(value) {
            let content = document.createElement('p');
            content.innerHTML = value;
            logDiv.appendChild(content);
            text.value = '';
        }

        function setConnected(connected) {
            connectBtn.disabled = connected;
            disconnectBtn.disabled = !connected;
            sentBtn.disabled = !connected;
        }
    </script>
</body>

</html>

运行效果