IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> WebSockeret快速入门 -> 正文阅读

[网络协议]WebSockeret快速入门

1、概念

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

2、特点

  • 最大的特点就是服务器可以主动地向客户端发送信息,客户端也还是可以主动向服务端发信息,是真正的平等对话,它属于服务器的一种推送技术
  • 建立在 TCP 协议之上,服务器端的实现比较容易
  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器
  • 握手阶段采用 HTTP 协议
  • 数据格式轻量,性能开销小。客户端与服务端进行数据交换时,服务端到客户端的数据包头只有2到10字节,客户端到服务端需要加上另外4字节的掩码。HTTP每次都需要携带完整头部
  • 更好的二进制支持,可以发送文本,和二进制数据
  • 没有同源限制(不存在跨域),客户端可以与任意服务器通信
  • 协议标识符是ws(如果加密,则是wss),请求的地址就是后端支持websocket的API

2021070313255938

3、WebSocket连接过程

客户端发起HTTP握手,告诉服务端进行WebSocket协议通讯,并告知WebSocket协议版本。服务端确认协议版本,升级为WebSocket协议。之后如果有数据需要推送,会主动推送给客户端

//以下为请求头信息

Accept-Encoding: gzip, deflate, br
Accept-Language: zh, zh-TW; q=0.9, en-US; q=0.8, en; q=0.7,zh-CN; q=0.6
Cache-Control: no-cache
Connection: Upgrade
Host: 127.0.0.1:3000
Origin: http://localhost:3000
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: bwb9SFiJONXhQ/A4pLaXIg==
Sec-WebSocket-Version: 13
Upgrade: websocket

解析

  1. Connection: Upgrade 表示我要升级协议
  2. Upgrade: websocket 要升级协议到websocket协议
  3. Sec-WebSocket-Version 表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号
  4. Sec-WebSocket-Key 对应服务端响应头的Sec-WebSocket-Accept,由于没有同源限制,websocket客户端可任意连接支持websocket的服务。这个就相当于一个钥匙一把锁,避免多余的,无意义的连接。
//以下是服务器的响应头 

Connection: Upgrade
Sec-WebSocket-Accept: 2jrbCWSCPlzPtxarlGTp4Y8XD20=   
Upgrade: websocket

解析

Sec-WebSocket-Accept: 用来告知服务器愿意发起一个websocket连接, 值根据客户端请求头的Sec-WebSocket-Key计算出来

4、Springboot + WebSocket

  1. 新建WebSocketTest项目

  2. 导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
        </dependency>
    </dependencies>
    
  3. application.yml

    server:
      port: 19266
    spring:
      application:
        name: WebSocketTest
    
  4. 主启动类

    @SpringBootApplication
    public class WebSocketMain {
    
        public static void main(String[] args) {
            SpringApplication.run(WebSocketMain.class, args);
        }
    
    }
    
  5. WebSocketConfig.java

    @Configuration
    public class WebSocketConfig {
    
        /**
         * 	注入ServerEndpointExporter,
         * 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter(){
            return new ServerEndpointExporter();
        }
    
    }
    
  6. WebSocket.java

    @Component
    @ServerEndpoint(value = "/websocket/{username}")
    @Slf4j
    public class WebSocket {
    
        // 记录用户名
        private String username;
    
        // 记录Session
        private Session session;
    
        // concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。
        private static final ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();
    
        /**
         * 连接成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session, @PathParam("username") String username) {
            if (webSocketMap.get(username) == null) {
                this.username = username;
                this.session = session;
                sendOneMessage(session, "登录成功!!");
                sendAllMessage("【websocket消息】用户(" + username + ")上线!");
                webSocketMap.put(username, this);
                log.info("【websocket消息】用户({})上线!,session={}", username, session);
            } else {
                try {
                    sendOneMessage(session, "该用户名已被占用!!");
                    session.close();
                    log.info("【websocket消息】用户({})已被登录!,session={}", username, session);
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
    
        }
    
        /**
         * 链接关闭调用的方法
         */
        @OnClose
        public void onClone() {
            // 删除当前WebSocket
            if (this.username != null) {
                webSocketMap.remove(this.username);
                sendAllMessage("【websocket消息】用户(" + username + ")下线!");
                log.info("【websocket消息】用户({})下线!", this.username);
            }
        }
    
        /**
         * 收到客户端消息后调用的方法
         */
        @OnMessage
        public void onMessage(String message) {
            sendAllMessage(this.username + "说:" + message);
        }
    
        /**
         * 发送错误时的处理
         */
        @OnError
        public void onError(Session session, Throwable error) {
            log.error("【websocket消息】发生错误!,错误信息:{}", error.getMessage());
        }
    
        /**
         * 广播消息
         * @param message 消息内容
         */
        public void sendAllMessage(String message) {
            for (String username : webSocketMap.keySet()) {
                if (webSocketMap.get(username).session.isOpen()) {
                    webSocketMap.get(username).session.getAsyncRemote().sendText(message);
                }
            }
        }
    
        /**
         * 此为单点消息(多人)
         * @param formUsernames 收消息的用户名数组
         * @param message 消息内容
         */
        public void sendMoreMessage(String[] formUsernames, String message) {
            String[] distinctUsernames = ArrayUtil.distinct(formUsernames);
            for (String username : distinctUsernames) {
                webSocketMap.get(username).session.getAsyncRemote().sendText(message);
            }
        }
    
        /**
         * 此为单点消息
         * @param session 收消息的用户session
         * @param message 消息内容
         */
        public void sendOneMessage(Session session, String message) {
            session.getAsyncRemote().sendText(message);
        }
    
    }
    
  7. 前端页面代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>群聊WebSocket测试</title>
    </head>
    <body>
    
    <table>
        <tr>
            <th id="username-prompt">请输入用户名:</th>
            <td><label for="username"></label><input onkeydown="if(event.keyCode===13) {wsConnection()}" type="text" id="username"> </td>
            <td><button  onclick="wsConnection()" id="login">登录</button></td>
        </tr>
        <tr>
            <th>请输入发送的消息:</th>
            <td><label for="text"></label><input onkeydown="if(event.keyCode===13) {wsSendMessage()}" type="text" id="text" disabled /></td>
            <td><button id="send" onclick="wsSendMessage()" disabled>发送</button></td>
        </tr>
    </table>
    <div id="message"></div>
    
    <script type="application/javascript">
        let usernamePrompt = document.getElementById("username-prompt")
        let username = document.getElementById("username")
        let login = document.getElementById("login")
        let text = document.getElementById("text")
        let send = document.getElementById("send");
        let message = document.getElementById("message")
        let webSocket;
    
        function wsConnection() {
            webSocket = new WebSocket("ws://localhost:19266/websocket/" + username.value);
    
            webSocket.onopen = function () {
                username.disabled = true
                login.disabled = true
                usernamePrompt.textContent = "用户名:"
                text.disabled = false
                send.disabled = false
            }
    
            webSocket.onmessage = (ent) => {
                message.insertAdjacentHTML(
                    'afterend',
                    "<p><strong>" +
                    ent.data.split(":")[0] +
                    (ent.data.split(":")[1] !== undefined ? ":</strong>" + ent.data.split(":")[1] : "</strong>") +
                    "</p>"
                )
            }
    
            webSocket.onerror = (ev) => {
                alert("连接失败!")
                console.log(ev)
            }
    
            webSocket.onclose = () => {
                text.disabled = true
                send.disabled = true
                username.value = ""
                text.value = ""
                usernamePrompt.textContent = "请输入用户名:"
                username.disabled = false
                login.disabled = false
                message.insertAdjacentHTML("afterbegin","<p><strong>已掉线,请重新登录!!</strong></p>")
            }
        }
    
        function wsSendMessage() {
            webSocket.send(text.value)
        }
    
    </script>
    
    </body>
    </html>
    
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-09-15 02:20:34  更:2022-09-15 02:22:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:50:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码