精华内容
下载资源
问答
  • 2022-01-08 22:27:57

    WebSocket协议

        WebSocket协议是一种网络通信协议,是HTML5之后提出的一种在单个TCP上进行全双工通讯的协议。可以使服务端主动的向客户端发送消息,常用于推送服务。其特点如下:

    • HTTP协议(1.0)是单向的没有连接状态的协议,客户端浏览器为了获取服务器的动态变化信息,需要配合JavaScript和AJAX进行不断的异步轮询请求,需要不断的建立连接,非常耗费资源。

    • WebSocket能够使任一方主动的建立起连接,并且连接只要建立一次就一直保持连接状态。

        WebSocket并不是全新的协议,而是利用HTTP协议来建立连接的,其创建过程如下:

    1. 浏览器发送HTTP请求
     GET ws://localhost:3000/hello HTTP/1.1
     Host: localhost
     Upgrade: websocket
     Connection: Upgrade
     Origin: http://localhost:3000
     Sec-WebSocket-Key: client-random-string
     Sec-WebSocket-Version: 13
    

    说明:

    • GET请求的地址不是类似/path/,而是以ws://开头的地址;

    • 请求头Upgrade: websocketConnection: Upgrade表示这个连接将要被转换为WebSocket连接;

    • Sec-WebSocket-Key是用于标识这个连接,并非用于加密数据;

    • Sec-WebSocket-Version指定了WebSocket的协议版本。

    1. 服务器接受该请求,就会返回下面的响应:
     HTTP/1.1 101 Switching Protocols
     Upgrade: websocket
     Connection: Upgrade
     Sec-WebSocket-Accept: server-random-string
    

        该响应代码101表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket指定的WebSocket协议。

    1. WebSocket连接建立,服务器端可以主动的发送消息给浏览器,可以是二进制数据和文本数据。

        
    下面是如何在前端分离的项目中使用WebSocket协议,前端Vue,后端Spring Boot。

    WebSocket客户端

    适用于支持html5的浏览器,其开放的API(不需要引入)为:

    1. 创建Web Socket连接
     var Socket = new WebSocket(url, [protocol] );
    

    url表示指定连接的URL,protocol是子协议,可选。

    对象属性:

     Socket.readyState: 连接状态,0表示连接未建立;1表示连接建立,可以进行通信;2表示连接正在关闭;3表示连接已经关闭,并且不能打开。
     Socket.bufferAmount:等待传输的队列。
    

    对象事件:

    事件事件处理程序描述
    openSocket.onopen连接建立时触发
    messageSocket.onmessage浏览器客户端接收服务器的信息时触发
    errorSocket.onerror通信发生错误时触发
    closeSocket.onclose连接关闭时触发

    对象方法:

    • Socket.send():使用连接发送数据。

    • Socket.close():关闭连接。

     // 初始化一个 WebSocket 对象
     var socket = new WebSocket('ws://localhost:9998/echo');// 建立 web socket 连接成功触发事件
     socket.onopen = function() {
       // 使用 send() 方法发送数据
       socket.send('发送数据');
       alert('数据发送中...');
     };// 接收服务端数据时触发事件
     socket.onmessage = function(evt) {
       var received_msg = evt.data;
       alert('数据已接收...');
     };// 断开 web socket 连接成功触发事件
     socket.onclose = function() {
       alert('连接已关闭...');
     };
    

        

    Vue使用web socket

     // 建立web socket连接
     webSocket() {
         if ('WebSocket' in window) {
         this.websocket = new WebSocket("ws://localhost:8081/websocket/hello");
         } else {
         console.log("你的浏览器还不支持web socket");
     }
     console.log(this.websocket);
     this.websocket.onopen = this.webSocketOnOpen;
     this.websocket.onclose = this.webSocketOnClose;
     this.websocket.onmessage = this.webSocketOnMessage;
     this.websocket.onerror = this.webSocketOnError;
     },// 连接建立之后
     webSocketOnOpen() {
         console.log("与后端建立连接");
         var data = "这里是端口8001建立连接";
         this.websocket.send(data);
     },// 连接关闭之后
     webSocketOnClose() {
         console.log("与后端关闭连接");
     },// 发送消息
     webSocketOnMessage(res) {
         console.log("接收到后端发送回来的消息");
         console.log(res.data)
     },// 发生错误
     webSocketOnError() {
         console.log("发生错误~");
     },
    

    使用原生的web socket就行了,也有封装了web socket的工具可以使用。

        

    WebSocket服务端

    如果是使用Tomcat服务器的话,要版本7以上才支持,使用Spring Boot整合WebSocket的过程如下:

    1. 引入依赖
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-websocket</artifactId>
     </dependency>
    
    1. 创建核心配置类
     import org.springframework.context.annotation.Bean;
     import org.springframework.context.annotation.Configuration;
     import org.springframework.messaging.simp.config.MessageBrokerRegistry;
     import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
     import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
     import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
     import org.springframework.web.socket.server.standard.ServerEndpointExporter;/**
      * spring boot web socket 核心配置类
      */
     @Configuration
     public class WebSocketConfig {
         /**
          * 配置服务端点导出器
          * @return
          */
         @Bean
         public ServerEndpointExporter serverEndpointExporter() {
             return new ServerEndpointExporter();
         }}
    
    1. 创建服务端点
     import lombok.extern.slf4j.Slf4j;
     import org.springframework.stereotype.Component;
     import org.springframework.web.bind.annotation.RequestParam;import javax.websocket.*;
     import javax.websocket.server.PathParam;
     import javax.websocket.server.ServerEndpoint;
     import java.io.IOException;
     import java.util.Map;
     import java.util.concurrent.CopyOnWriteArraySet;/**
      * 后端实现web socket
      */
     @Component
     @ServerEndpoint("/hello")
     @Slf4j
     public class WebSocketServer {
         /**
          * 静态变量,用来记录当前在线连接数
          * 注意线程安全问题
          */
         private static int onlineCount = 0;
         
         /**
          * concurrent包的线程安全集合,存放每个客户端对应的web socket对象
          */
         private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();/**
          * 与某个客户端的连接会话,需要听过它来给客户端发送数据
          * 每个前端的WebSocket对象建立起来的有一个session对象
          */
         private Session session;/**
          * 连接建立成功调用的方法
          */
         @OnOpen
         public void onOpen(Session session) {
             this.session = session;
             System.out.println("session = " + session);
             // 添加当前web socket对象
             webSocketSet.add(this);
             // 累加在线人数
             addOnlineCount();
             log.info("有新连接加入!当前在线人数为" + getOnlineCount());
             try {
                 sendMessage("有新连接加入!当前在线人数为" + getOnlineCount());
             } catch (IOException e) {
                 log.error("websocket IO异常");
             }
         }/**
          * 连接关闭调用的方法
          */
         @OnClose
         public void onClose() {
             // 将连接的web socket对象从set中移除
             webSocketSet.remove(this);
             // 在线人数-1
             subOnlineCount();
             log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
         }/**
          * 收到客户端消息后调用的方法
          * @param message 客户端发送过来的消息
          */
         @OnMessage
         public void onMessage(String message, Session session) {
             log.info("来自客户端的消息:" + message);//群发消息
             for (WebSocketServer item : webSocketSet) {
                 try {
                     item.sendMessage(message);
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }/**
          * 发生错误
          * @param session
          * @param error
          */
         @OnError
         public void onError(Session session, Throwable error) {
             log.error("发生错误");
             error.printStackTrace();
         }/**
          * 给客户端浏览器发送消息
          * @param message
          * @throws IOException
          */
         public void sendMessage(String message) throws IOException {
             this.session.getBasicRemote().sendText(message);
         }
         /**
          * 群发自定义消息
          * */
         public static void sendInfo(String message) throws IOException {
             log.info(message);
             for (WebSocketServer item : webSocketSet) {
                 try {
                     item.sendMessage(message);
                 } catch (IOException e) {
                     continue;
                 }
             }
         }
         public static synchronized int getOnlineCount() {
             return onlineCount;
         }
         public static synchronized void addOnlineCount() {
             WebSocketServer.onlineCount++;
         }
         public static synchronized void subOnlineCount() {
             WebSocketServer.onlineCount--;
         }
     }
    

    如果要获取前端发送的消息可用:

     @Component
     @ServerEndPoint("/hello/{name}/{age}")
     public class WebSocketServer {
         @OnOpen
         public void onOpen(@PathParam("name")String name, @PathParam("age")int age) {
             
         }
     }
    

        
    【参考】廖雪峰的官方网站https://www.liaoxuefeng.com/wiki/1022910821149312/1103303693824096

        
        

    更多相关内容
  • WebSocket协议中文版.pdf

    2021-10-19 09:35:45
    WebSocket协议实现在受控环境中运行不受信任代码的一个客户端到一个从该代码已经选择加入通信的远程主机之间的全双工通信。用于这个的安全模型是通常由web浏览器使用的基于来源的安全模型。该协议包括一个打开阶段...
  • WebSocket API是下一代客户端-...WebSocket协议主要分为两部分,第一部分是连接许可验证和验证后的数据交互.连接许可验证比较简单,由Client发送一个类似于HTTP的请求,服务端获取请求后根据请求的KEY生成对应的值并返回.
  • websocket协议 RFC6455 中文版,完整版pdf文档下载,带完整中文目录,文件名:WebSocket协议中文版.pdf。必定物有所值。你会满意的。
  • WebSocket协议的目的是为了工作于现有的网络基础设施。作为这一设计原则的一部分,WebSocket连接的协议规范定义了一个HTTP连接作为其开始生命周期,进而保证其与pre-WebSocket世界的完全向后兼容。通常来说从HTTP协议...
  • 我们需要准备Jmeter的WebSocket协议的支持插件: JMeterWebSocketSampler-1.0.2-SNAPSHOT.jar 要安装此插件,请将jar文件放入\ JMeter \ lib \ ext文件夹中。 请记住下载依赖项并将其放入同一文件夹。 您将在...
  • HTML+JS基于webSocket协议的视频插件,比起video.js+HLS协议或者RTMP协议都要稳定,延迟要小,且不卡顿,一个页面中可分为多个视频播放窗口
  • rfc6455的中文翻译版下载,资源来源自:http://www.52im.net/thread-3172-1-1.html
  • websocket协议开源

    2022-05-27 13:15:15
    WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。...最新版本的WebSocket协议于2011-12其标准规范已经明确下来,所以现在
  • 用于处理WebSocket协议的通用Ruby库。 它着重于通过提供抽象层,而不是提供服务器或客户端功能。 高速公路测试:, 当前,WebSocket Ruby支持所有现有的WebSocket草案,其中包括: 安装 WebSocket Ruby没有外部...
  • 该jar包为websocket协议的插件,直接放在jmeter的D:\software\apache-jmeter-4.0\lib\ext目录下,重启jmeter即可使用,可以进行websocket压力测试
  • websocket协议简介

    千次阅读 2022-04-16 09:15:53
    概念介绍 单工通信:数据传输只允许在一个方向上传输,只能一方发送数据,另一方接收...1、websocket是双向通信协议,模拟socket协议,可以双向发送或接受信息。HTTP是单向的。 2、都是需要握手进行建立连接的,但H

    概念介绍

    • 单工通信:数据传输只允许在一个方向上传输,只能一方发送数据,另一方接收数据并发送。
    • 半双工:数据传输允许两个方向上的传输,但在同一时间内,只可以有一方发送或接收数据。
    • 全双工:同时可进行双向数据传输。

    websocket介绍

    • WebSocket协议在2008年诞生,2011年成为国际标准。
    • WebSocket是一种在单个TCP连接上进行全双工通信的协议,位于 OSI 模型的应用层。
    • WebSocket的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。

    出现背景

    • HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。
    • 这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。很多网站为了实现推送技术,所用的技术都是轮询。即在特定得时间间隔,由浏览器对服务器发出http请求。

    websocket与HTTP比较

    http不同版本简介

    相同点

    • 都是一样基于TCP的,都是可靠性传输协议。
    • 都是应用层协议。

    不同点

    • websocket 是持久连接,http 是短连接;
    • websocket 的协议是以 ws/wss 开头,http 对应的是 http/https;
    • websocket 是有状态的,http 是无状态的;
    • websocket 连接之后服务器和客户端可以双向发送数据,http 只能是客户端发起一次请求之后,服务器才能返回数据;
    • websocket 连接建立之后,不需要再发送request请求,数据直接从TCP通道传输。

    联系

    • WebSocket在建立握手时,数据是通过HTTP传输的。

    在这里插入图片描述

    websocket握手过程

    • 1、浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。
    • 2、TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)
    • 3、服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。
    • 4、当收到了连接成功的消息后,通过TCP通道进行传输通信。

    HTTP协议头

    http协议头详解

    请求

    • Accept: text/html,application/xhtml+xml,application/xml
    • Accept-Encoding: gzip, deflate, br
    • Accept-Language: zh-CN,zh;q=0.9
    • Connection: keep-alive
    • Host: www.baidu.com
    • User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36

    响应

    • Connection: keep-alive
    • Content-Encoding: gzip
    • Content-Type: text/html;charset=utf-8
    • Date: Sat, 16 Apr 2022 10:43:46 GMT
    • Server: BWS/1.1

    websocket协议头

    请求

    • *Accept-Encoding: gzip, deflate
    • *Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
    • Connection: Upgrade
    • Host: 192.168.1.2:8080
    • Sec-WebSocket-Key: 821VqJT7EjnceB8m7mbwWA==
    • Sec-WebSocket-Version: 13
    • Upgrade: websocket
    • User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.39

    响应

    • Connection: Upgrade
    • Date: Sat, 16 Apr 2022 10:49:05 GMT
    • Sec-WebSocket-Accept: paFykwJusIMnfpohWxA5HVpjD1Q=
    • Server: Server
    • Upgrade: websocket

    websocket头详解

    • 请求头详解
    • Upgrade: 向服务器指定协议类型,告诉web服务器当前使用的是websocket协议
    • Sec-WebSocket-Key:是一个 Base64 encode 的值,这个是浏览器随机生成的
    • Sec-WebSocket-Version:websocket协议版本
    • 响应头详解(web服务返回状态码101表示协议切换成功)
    • Sec-WebSocket-Accept: 是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。用来证明客户端和服务器之间能进行通信了。

    代码展示

    服务端

    服务端程序是通过QT实现

    • websocketservice.h
    #ifndef WEBSOCKETSERVER_H
    #define WEBSOCKETSERVER_H
    
    #include <QWidget>
    #include <QWebSocketServer>
    #include <QWebSocket>
    #include <QHostInfo>
    #include <QNetworkInterface>
    
    namespace Ui {
    class WebSocketServer;
    }
    
    class WebSocketServer : public QWidget
    {
        Q_OBJECT
    
    public:
        explicit WebSocketServer(QWidget *parent = 0);
        ~WebSocketServer();
    
    private slots:
        void on_pushButton_startListen_clicked();
        void onNewConnection();
        void onTextMessageReceived(QString msg);
        void onDisconnected();
    
    
        void on_pushButton_send_clicked();
    
    private:
        Ui::WebSocketServer *ui;
        QWebSocketServer *server;
        QWebSocket *socket;
        QList<QWebSocket*> clientList;
    
        QString mAddr;
        int mPort;
    };
    
    #endif // WEBSOCKETSERVER_H
    
    • websocketservice.cpp
    #include "websocketserver.h"
    #include "ui_websocketserver.h"
    
    WebSocketServer::WebSocketServer(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::WebSocketServer)
    {
        ui->setupUi(this);
        ui->pushButton_send->setEnabled(false);
    
    
        //获取本机IP和端口
        QString hostName = QHostInfo::localHostName();   //获取主机名
        QHostInfo hostInfo = QHostInfo::fromName(hostName); //获取主机信息
        QList<QHostAddress> addList = hostInfo.addresses(); //获取IP地址列表
    
        QString localIP;
        if(!addList.isEmpty())
        {
            for(int i = 0; i < addList.count();i++)
            {
                QHostAddress aHost = addList.at(i);
                if(QAbstractSocket::IPv4Protocol == aHost.protocol())
                {
                    localIP = aHost.toString();
                    break;
                }
            }
        }
    
        ui->lineEdit_url->setText(localIP);
        ui->lineEdit_port->setText("8080");
    
    
        //构造:QWebSocketServer(const QString& serverName,QWebSocketServer::SslMode secureMode,QObject *parent=nullptr)
        //使用给定的serverName构造一个新的QWebSocketServer。
        //该服务器名称将在HTTP握手阶段被用来识别服务器。它可以为空,此时不会将服务器名称发送给客户端。
        //SslMode指示服务器是通过wss(SecureMode)还是ws(NonSecureMode)运行
        //QWebSocketServer::SecureMode服务器以安全模式运行(通过wss)
        //QWebSocketServer::NonSecureMode服务器以非安全模式运行(通过ws)
    
        server=new QWebSocketServer("Server",QWebSocketServer::NonSecureMode,this);
    
        //有新的连接
        connect(server,&QWebSocketServer::newConnection,this,&WebSocketServer::onNewConnection);
    }
    
    WebSocketServer::~WebSocketServer()
    {
        delete ui;
    }
    
    void WebSocketServer::on_pushButton_startListen_clicked()
    {
        QHostAddress address = QHostAddress(ui->lineEdit_url->text());
        if(server->listen(address, ui->lineEdit_port->text().toInt())){
            ui->pushButton_startListen->setEnabled(false);
            ui->pushButton_startListen->setText("disListen");
        }
    }
    
    void WebSocketServer::onNewConnection()
    {
        socket=server->nextPendingConnection();
        mAddr = socket->peerAddress().toString();
        mPort = socket->peerPort();
    
        ui->plainTextEdit_clientStatus->appendPlainText("[" + mAddr + ":" + QString::number(mPort) + "]" + " connected...");
    
        ui->pushButton_send->setEnabled(true);
    
        connect(socket,&QWebSocket::textMessageReceived, this, &WebSocketServer::onTextMessageReceived);
    
        ui->plainTextEdit_sendMsg->clear();
        ui->plainTextEdit_sendMsg->appendPlainText("welcome to connect Server!");
    
        //断开连接时
        connect(socket,&QWebSocket::disconnected, this, &WebSocketServer::onDisconnected);
    }
    
    void WebSocketServer::onTextMessageReceived(QString msg)
    {
        ui->plainTextEdit_recvMsg->appendPlainText(msg);
    }
    
    void WebSocketServer::onDisconnected()
    {
        ui->plainTextEdit_clientStatus->appendPlainText("[" + mAddr + ":" + QString::number(mPort) + "]" + " disConnected!");
    }
    
    void WebSocketServer::on_pushButton_send_clicked()
    {
        socket->sendTextMessage(ui->plainTextEdit_sendMsg->toPlainText());
    }
    
    

    客户端

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Qt WebSocket Demo</title>
    </head>
    
    <body>
        <input type="text" id="edit_url" value="ws://192.168.1.2:8080" />
        <input type="button" id="btn_open" value="open" onclick="doOpen()" />
        <br />
        <p>Recv:</p>
        <br />
        <textarea id="edit_recv" cols="50" rows="10"></textarea>
        <br />
        <p>Send:</p>
        <br />
        <textarea id="edit_send" cols="50" rows="10">Hello, I am websocket Client!</textarea>
        <br />
        <input type="button" value="Send" onclick="doSend()" />
        <script>
            var edit_url = document.getElementById("edit_url");
            var btn_open = document.getElementById("btn_open");
            var edit_recv = document.getElementById("edit_recv");
            var edit_send = document.getElementById("edit_send");
    
            var client = null;
    
            function doOpen() {
                console.log("open")
                if (!("WebSocket" in window)) {
                    //不支持WebSocket
    				console.log("no websocket")
                    return;
                }
                if (client === null) {
                    client = new WebSocket(edit_url.value);
    
                    client.onopen = function () {
                        btn_open.value = "Close";
                    }
                    //收到数据后追加到尾巴上
                    client.onmessage = function (event) {
                        edit_recv.value += String(event.data);
                    }
                    client.onclose = function () {
                        client = null;
                        btn_open.value = "Open";
                    }
                } else {
                    client.close();
                    client = null;
                }
            }
    
            function doSend() {
                console.log("send")
                if (client === null)
                    return;
                client.send(edit_send.value);
            }
        </script>
    </body>
    
    </html>
    

    演示

    • 可以看到只需发一次request请求,后续就不需要再发request请求,可以直接进行数据的传输,并且服务端也能主动发送消息给客户端
      在这里插入图片描述
      在这里插入图片描述
    展开全文
  • WebSocket协议

    2020-12-28 22:18:04
    WebSocket协议简介WebSocket是html5规范新引入的功能,用于解决浏览器与后台服务器双向通讯的问题,使用WebSocket技术,后台可以随时向前端推送消息,以保证前后台状态统一,在传统的无状态HTTP协议中,这是“无法...

    WebSocket协议简介

    WebSocket是html5规范新引入的功能,用于解决浏览器与后台服务器双向通讯的问题,使用WebSocket技术,后台可以随时向前端推送消息,以保证前后台状态统一,在传统的无状态HTTP协议中,这是“无法做到”的。

    在WebSocket出现之前,传统的服务端向浏览器推送消息的技术包括:ajax、flash、comet、java applet等。无一例外,这些技术使用的都是长轮循,即每隔一段时间去请求后台,以获取最新状态。长轮询方式容易实现,但效果也差,频繁盲目的调用后台,带来不必要的开销,且实时性无法保障,后台出现更新,前端需要在下一次轮询时才知道。

    WebSocket协议支持服务端与浏览器建立长连接,双方可以随时发送数据给对方,不再是由客户端控制的一问一答的方式。在实现推送功能的时候,主要是由服务端给客户端发送数据。

    基于长轮循(polling)和websocket推送的浏览器(browser)和服务端(Server)的交互对比图如下所示:

    由于WebSocket协议建立在http协议的基础之上,因此二者有很多的类似之处。事实上,在使用websocket协议时,浏览器与服务端最开始建立的还是http连接,之后再将协议从http转换成websocket,协议转换的过程称之为握手(handshake),表示服务端与客户端都同意建立websocket协议。

    需要注意的是,由于websocket是新的协议,需要浏览器和web服务端都支持的情况下,才能建立连接成功。

    正常情况下,连接在建立的时候,浏览器向服务端发送一个HTTP请求,通过包含一些额外信息,表明其希望将协议从HTTP转换成WebSocket。这个额外信息实际上就是增加了一个请求头Update,如下所示:

    GET ws://echo.websocket.org/?encoding=text HTTP/1.1

    Origin: http://websocket.org

    Cookie: __utma=99as

    Connection: Upgrade

    Host: echo.websocket.org

    Sec-WebSocket-Key: uRovscZjNol/umbTt5uKmw==

    Upgrade: websocket

    Sec-WebSocket-Version: 13

    可以看到Update请求头的值为websocket,表示希望将Http协议转换成websocket协议。

    Origin表示的是,连接开始时发送http请求的地址。

    注意GET的路径是以ws开头,这是因为WebSocket是一种全新的协议,不属于http无状态协议,协议名为”ws",与http协议使用相同的80端口,类似的,"wss"和https协议使用相同的443端口。

    客户端握手请求中的 Sec-WebSocket-Key 头字段中的内容是随机的,采用 base64 编码。服务端会接收到这个值之后,会将其与一个魔幻数字258EAFA5-E914-47DA-95CA-C5AB0DC85B11进行连接,使用SHA-1加密后,采用base64编码,以Sec-WebSocket-Accept响应头返回。

    如果服务端接受了客户端将http协议转换成websocket协议连接的请求,会返回类似如下响应,表示协议切换成功:HTTP/1.1 101 WebSocket Protocol Handshake

    Date: Fri, 10 Feb 2012 17:38:18 GMT

    Connection: Upgrade

    Server: Kaazing Gateway

    Upgrade: WebSocket

    Access-Control-Allow-Origin: http://websocket.org

    Access-Control-Allow-Credentials: true

    Sec-WebSocket-Accept: rLHCkw/SKsO9GAH/ZSFhBATDKrU=

    Access-Control-Allow-Headers: content-type

    WebSocket客户端开发

    支持html5的浏览器,一般都会提供一个内置的js对象Websocket ,开发者利用这个对象就可以与服务端建立websocket连接。特别的在FireFox中,这个对象为在Firefox中为MozWebSocket。

    可以通过以下js代码检测一个浏览器是否支持websocketwindow.WebSocket = window.WebSocket || window.MozWebSocket;

    if (!window.WebSocket){

    alert("WebSocket not supported by this browser");

    return;

    }

    websocket协议连接的创建var myWebSocket = new WebSocket("ws://www.websockets.org");

    关闭连接使用Websocket对象的close方法myWebSocket.close();

    发送消息使用Websocket对象的send方法myWebSocket.send("Hello WebSockets!");

    Websocket对象还提供了几个回调方法,在适当的实际会被回调://连接创建成功时被回调

    myWebSocket.onopen = function(evt) { alert("Connection open ..."); };

    //收到服务端的消息时被回调

    myWebSocket.onmessage = function(evt) { alert( "Received Message: " + evt.data); };

    //连接关闭时被回调

    myWebSocket.onclose = function(evt) { alert("Connection closed."); };

    完整的客户端案例:

    在这里我们编写一个界面,如下所示,在"客户端请求消息"文本框中填入要发送的内容,点击"发送WebSocket请求消息按钮",服务端的响应消息会显示在"服务端返回的应答消息文本框中"

    源码如下所示:html>

     Netty WebSocket 协议 

    客户端请求消息

    服务端返回的应答消息

    window.WebSocket = window.WebSocket || window.MozWebSocket;

    if (!window.WebSocket){

    alert("你的浏览器不支持websocket协议");

    }else{

    var socket = new WebSocket("ws://localhost:8080/websocket");

    socket.onmessage = function (event) {

    var ta = document.getElementById('responseText');

    ta.value = event.data

    };

    socket.onopen = function (event) {

    alert("websocket连接建立成功...");

    };

    socket.onclose = function (event) {

    alert("连接关闭");

    };

    function send(message) {

    if (!window.WebSocket) {

    return;

    }

    if (socket.readyState == WebSocket.OPEN) {

    socket.send(message);

    }

    else {

    alert("WebSocket not supported by this browser");

    }

    }

    }

    Netty Websocket服务端开发public class WebSocketServer {

    public void run(int port) throws Exception {

    EventLoopGroup bossGroup = new NioEventLoopGroup();

    EventLoopGroup workerGroup = new NioEventLoopGroup();

    try {

    ServerBootstrap b = new ServerBootstrap();

    b.group(bossGroup, workerGroup)

    .channel(NioServerSocketChannel.class)

    .childHandler(new ChannelInitializer() {

    @Override

    protected void initChannel(SocketChannel ch)

    throws Exception {

    ChannelPipeline pipeline = ch.pipeline();

    pipeline.addLast("http-codec",

    new HttpServerCodec());

    pipeline.addLast("aggregator",

    new HttpObjectAggregator(65536));

    ch.pipeline().addLast("http-chunked",

    new ChunkedWriteHandler());

    pipeline.addLast("handler",

    new WebSocketServerHandler());

    }

    });

    Channel ch = b.bind(port).sync().channel();

    System.out.println("Web socket server started at port " + port

    + '.');

    System.out

    .println("Open your browser and navigate to http://localhost:"

    + port + '/');

    ch.closeFuture().sync();

    } finally {

    bossGroup.shutdownGracefully();

    workerGroup.shutdownGracefully();

    }

    }

    public static void main(String[] args) throws Exception {

    int port = 8080;

    if (args.length > 0) {

    try {

    port = Integer.parseInt(args[0]);

    } catch (NumberFormatException e) {

    e.printStackTrace();

    }

    }

    new WebSocketServer().run(port);

    }

    }

    上述代码中的HttpServerCodec、HttpObjectAggregator、ChunkedWriteHandler 都是Netty提供的对tttp或webscoket协议支持的ChannelHandler:

    HttpServerCodec的作用是将请求或者应答消息按照HTTP协议的格式进行解码或者编码。

    HttpObjectAggregator的目的是将HTTP消息的多个部分组合成一条完整的HTTP消息,也就是处理粘包与解包问题。

    ChunkedWriteHandler用来向客户单发送HTML文件,它主要用于支持浏览器和服务端进行WebSocket通信。

    WebSocketServerHandler是我们自己编写的处理webscoket请求的ChannelHandler。public class WebSocketServerHandler extends SimpleChannelInboundHandler {

    private static final Logger logger = Logger

    .getLogger(WebSocketServerHandler.class.getName());

    private WebSocketServerHandshaker handshaker;

    @Override

    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {

    // 传统的HTTP接入

    if (msg instanceof FullHttpRequest) {

    handleHttpRequest(ctx, (FullHttpRequest) msg);

    }

    // WebSocket接入

    else if (msg instanceof WebSocketFrame) {

    handleWebSocketFrame(ctx, (WebSocketFrame) msg);

    }

    }

    private void handleHttpRequest(ChannelHandlerContext ctx,

    FullHttpRequest req) throws Exception {

    // 如果HTTP解码失败,返回HHTP异常

    if (!req.getDecoderResult().isSuccess()

    || (!"websocket".equals(req.headers().get("Upgrade")))) {

    sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1,

    BAD_REQUEST));

    return;

    }

    // 构造握手响应返回,本机测试

    WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(

    "ws://localhost:8080/websocket", null, false);

    handshaker = wsFactory.newHandshaker(req);

    if (handshaker == null) {

    WebSocketServerHandshakerFactory

    .sendUnsupportedVersionResponse(ctx.channel());

    } else {

    handshaker.handshake(ctx.channel(), req);

    }

    }

    private void handleWebSocketFrame(ChannelHandlerContext ctx,

    WebSocketFrame frame) {

    // 判断是否是关闭链路的指令

    if (frame instanceof CloseWebSocketFrame) {

    handshaker.close(ctx.channel(),

    (CloseWebSocketFrame) frame.retain());

    return;

    }

    // 判断是否是Ping消息

    if (frame instanceof PingWebSocketFrame) {

    ctx.channel().write(

    new PongWebSocketFrame(frame.content().retain()));

    return;

    }

    // 本例程仅支持文本消息,不支持二进制消息

    if (!(frame instanceof TextWebSocketFrame)) {

    throw new UnsupportedOperationException(String.format(

    "%s frame types not supported", frame.getClass().getName()));

    }

    // 返回应答消息

    String request = ((TextWebSocketFrame) frame).text();

    if (logger.isLoggable(Level.FINE)) {

    logger.fine(String.format("%s received %s", ctx.channel(), request));

    }

    ctx.channel().write(new TextWebSocketFrame(" 收到客户端请求:"+request));

    }

    private static void sendHttpResponse(ChannelHandlerContext ctx,

    FullHttpRequest req, FullHttpResponse res) {

    // 返回应答给客户端

    if (res.getStatus().code() != 200) {

    ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),

    CharsetUtil.UTF_8);

    res.content().writeBytes(buf);

    buf.release();

    setContentLength(res, res.content().readableBytes());

    }

    // 如果是非Keep-Alive,关闭连接

    ChannelFuture f = ctx.channel().writeAndFlush(res);

    if (!isKeepAlive(req) || res.getStatus().code() != 200) {

    f.addListener(ChannelFutureListener.CLOSE);

    }

    }

    @Override

    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

    ctx.flush();

    }

    @Override

    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)

    throws Exception {

    cause.printStackTrace();

    ctx.close();

    }

    }

    在上述代码中,当接收到客户端发送的请求消息时,首先通过msg是FullHttpRequest还是WebSocketFrame来判断判断是http请求还是websocket请求。然后分别调用handleHttpRequest、handleWebSocketFrame方法来处理。

    对于handleHttpRequest方法,仅仅在第一次请求的时候被调用一次

    连接建立时,第一次发送的肯定是一个http请求,因为将http升级websocket协议需要服务端支持,但是并不是所有的服务端都支持websocket协议,因此服务端需要在handleHttpRequest方法中,通知客户端,服务端是否同意将HTTP协议升级成WebSocket协议。在我们的案例中,通过WebSocketServerHandshaker的handshake方法,告诉客户端,服务端同意协议升级,此时websocket连接建立完成。

    一旦websocket协议建立完成,之后的所有的请求都会走handleWebSocketFrame方法,这个方法的处理顺序如下:

    1、判断是否是CloseWebSocketFrame 请求,如果是,关闭连接

    2、判断是否是PingWebSocketFrame 消息,如果是维持链路的Ping消息,则构造Pong消息返回。Ping和Pong是websocket里的心跳,用来保证客户端是在线的。

    3、判断消息是否是TextWebSocketFrame 文本消息。因为websocket消息是基于HTTP协议的,http除了支持文本消息,还是支持图片上传等复杂的二进制消息。按我们的案例中,只对文本消息进行处理。

    4、返回应答消息

    在handleWebSocketFrame方法中,我们使用到了webscocket中不同的帧类型,CloseWebSocketFrame、PingWebSocketFrame、TextWebSocketFrame。帧类型实际上就是WebSocket协议定义的消息类型。在一个完整的WebSocket帧中(如下图所示),包含了一个4位的二进制Opcode字段,其用来表示消息类型。

    Opcode的取值和消息类型的对应关系如下所示:OPCODE:4位

    0x0表示附加数据帧

    0x1表示文本数据帧

    0x2表示二进制数据帧

    0x3-7暂时无定义,为以后的非控制帧保留

    0x8表示连接关闭

    0x9表示ping

    0xA表示pong

    0xB-F暂时无定义,为以后的控制帧保留

    可以看到,虽然4位2进制最多支持16种消息类型,但是目前实际上只定义了六种消息类型。在Netty中,针对这每一种帧类型,都有一个java类与之对应,如下所示。

    其中Close、Ping以及Pong称之为控制帧,Close关闭帧很容易理解,客户端如果接受到了就关闭连接,客户端也可以发送关闭帧给服务端。Ping和Pong是websocket里的心跳,用来保证客户端是在线的。

    Text和Binary表示这个传输的帧是文本类型还是二进制类型,二进制类型传输的数据可以是图片或者语音之类的。

    如何实现消息推送

    之前的案例,还是通过客户端发送请求,服务端进行响应的方式进行交互。WebSocket协议还可以实现服务端主动向客户端推送消息的功能。

    主动推送需要考虑的问题包括:

    1、服务端要保存用户唯一标记(假设为userId)与其对应的SocketChannel的对应关系,以便之后根据这个唯一标记给用户推送消息。

    对应关系的保存很简单,用一个Map来记录即可。保存这种对应关系的最佳时机是websocket连接刚刚建立的时候,因为这种对应关系只需要保存一次 ,之后就可以拿着这个userId找到对应SocketChannel给用户推送消息。以之前的代码为例,很明显这个操作,应该放在handleHttpRequest方法中处理,因为这个方法在一个websocket连接的生命周期只会调用一次。

    我们知道,当连接建立时,我们在服务端就能获取到客户端对应的SocketChannel,我们如果获取到userId呢?WebSocket协议与HTTP协议一样,都支持在url中添加参数。如:ws://localhost:8080/websocket?userId=123456

    我们可以在handleHttpRequest中添加到以下代码获取到这个参数//这个map用于保存userId和SocketChannel的对应关系

    public static Map map=new ConcurrentHashMap();

    private void handleHttpRequest(ChannelHandlerContext ctx,

    FullHttpRequest req) throws Exception {

    ....

    //获取userId

    QueryStringDecoder queryStringDecoder = new QueryStringDecoder(req.getUri());

    Map> params = queryStringDecoder.parameters();

    String userId=params.get("userId").get(0);

    //保存userId和Channel的对应关系

    map.put(userId,ctx.channel());

    }

    此外,当用户断开与服务端的连接的时候,也就是当服务端接收到CloseWebSocketFrame帧的时候, 需要从Map中移除对应的userId和SocketChannel,以免Map中存储过多的无效用户数据导致内存溢出。

    2、通过userId给用户推送消息

    通常消息推送是由运营人员根据业务需要,从一个后台管理界面,过滤出符合特定条件的用户来推送消息,因此需要推送的userId和推送内容都是运营指定的。推送服务端只需要根据userId找到对应的Channel,将推送消息推送给浏览器即可。代码片段如下://运营人员制定要推送的userId和消息

    String userId="123456";

    String msg="xxxxxxxxxxxxxxxxxxxxxx";

    //根据userId找到对应的channel,并将消息写出

    Channel channel = map.get(userId);

    channel.writeAndFlush(new TextWebSocketFrame(msg));

    一般公司的推送的消息量都比较大,所以需要推送的消息一般会放到一个外部消息队列中,如rocketmq,kafka。推送服务端通过消费这些队列,来给用户推送消息。

    3、客户端ack接受到的消息

    一般情况下,我们会每一个推送的消息都设置一个唯一的msgId。客户端在接受推送的消息后,需要对这条消息进行ack,也就是告诉服务端自己接收到了这条消息,ack时需要将msgId带回来。

    如果我们只使用websocket做推送服务,那么之前代码中的handleWebSocketFrame方法,要处理的主要就是这类ack消息。

    4、离线消息

    如果当给某个用户推送消息的时候,其并不在线,可以将消息保存下来,一般都是保存到一个缓存服务器中(如redis),每次当一个用户与服务端建立连接的时候,首先检查缓存服务器中有没有其对应的离线消息,如果有直接取出来发送给用户。

    5、消息推送记录的存储

    服务端推送完成消息记录之后,还需要将这些消息存储下来,以支持一个用户查看自己曾经受到过的推送消息。

    6、疲劳度控制

    在实现推送功能时,我们通常还会做一个疲劳度控制的功能,也就是限制给一个用户每天推送消息的数量,以免频繁的推送消息给用户造成不好的体验。疲劳度控制可以从多个维度进行设计。例如用户一天可以接受到消息总数,根据业务划分的某种特定类型的消息的一天可以接受到的消息总数等。

    参考文章:

    http://websocket.org/aboutwebsocket.html

    http://www.jianshu.com/p/867274a5e054

    http://www.jianshu.com/p/1ee01218097b

    http://www.jianshu.com/p/eb4c1c724d9e

    展开全文
  • 主要介绍了PHP 实现 WebSocket 协议,结合具体实例形式较为详细的分析了websocket协议原理、以及PHP具体应用相关操作技巧,需要的朋友可以参考下
  • 人脸设备接口文档-websocket协议通讯
  • 包含JMeterWebSocketSampler-1.0.2-SNAPSHOT.jar及其依赖jar包: ... 2、jetty-io-9.4.9.v20180320.jar 3、jetty-util-9.4.9.v20180320.jar 4、websocket-api-9.4.9.v... 6、websocket-common-9.4.9.v20180320.jar
  • 看到这篇文章的题目,估计很多人都会问,这个组件是不是有些显的... websocket-sharp是一个C#实现websocket协议客户端和服务端,websocket-sharp支持RFC 6455;WebSocket客户端和服务器;消息压缩扩展;安全连接;HT
  • 该Demo实现了简单的视频直播功能
  • WebSocket协议介绍

    千次阅读 2021-09-01 21:24:37
    二、WebSocket出现之前的实时技术三、WebSocket应用场景四、WebSocket协议栈五、WebSocket与HTTP的区别六、WebSocket握手过程七、WebSocket帧格式八、WebSocket分片传输九、WebSocket相关扩展 前言 WebSocket是一...


    前言

    WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

    一、WebSocket是什么?

    • WebSocket 协议在2008年诞生,2011年成为国际标准。主流浏览器都已经支持。

    • WebSocket 是一种全新的协议。它将 TCP 的 Socket(套接字)应用在了网页上,从而使通信双方建立起一个保持在活动状态连接通道,并且属于全双工通信。

    • WebSocket 协议借用 HTTP协议 的 101 switch protocol 来达到协议转换,从HTTP协议切换WebSocket通信协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。

    二、WebSocket出现之前的实时技术

    • 轮询:最早的一种实现实时 Web 应用的方案。客户端以一定的时间间隔向服务端发出请求,以频繁请求的方式来保持客户端和服务器端的通信。
    • 长轮询:长轮询也采用轮询的方式,不过采取的是阻塞模型,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
    • 其他方式:如xhr-streaming、隐藏iframe、ActiveX控件、SSE。

    下图是长轮询的一个示意图:
    在这里插入图片描述
    轮询技术非真正实时技术。使用 Ajax 方式模拟实时效果,每次客户端和服务器端交互,都是一次 HTTP 的请求和应答过程,且每次的 HTTP 请求和应答都带有完整 HTTP 头信息,增加传输的数据量。需构建两个http连接。客户端和服务器端编程实现比较复杂,为模拟真实的实时效果,需构造两个 HTTP 连接来模拟客户端和服务器的双向通信,一个连接用来处理客户端到服务器端的数据传输,一个连接用来处理服务器端到客户端的数据传输,增加了编程实现的复杂度、服务器端的负载,制约了应用系统的扩展性。

    三、WebSocket应用场景

    BS架构下的即时通讯、游戏等应用需要客户端与服务端间的双向通信,而HTTP的请求/响应模型并不适合这种场景。会存在一定的问题:

    • 服务器端被迫提供两类接口,一类提供给客户端轮询新消息,一类提供给客户端推送消息给服务器端。
    • HTTP协议有较多的额外开销,每次发送消息都会有一个HTTP header信息,而且如果不用Keep-Alive每次还都要握手。
    • 客户端的脚本比如JS可能还需要跟踪整个过程,发送一个消息后,我可能需要跟踪这个消息的返回。

    Websocket出现使得浏览器提供socket的支持成为可能,从而在浏览器和服务器之间建立一条基于tcp的双向连接通道,web开发人员可以很方便的利用websocket构建实时web应用。
    WebSocket适用于以下场景:

    • 在线聊天场景:例如qq聊天、淘宝与客服聊天、在线客服等等。这种场景都是需要实时的接收服务器推送的内容。
    • 协同办公:例如腾讯在线文档,腾讯的在线文档是支持多人编辑的,在excel中,一旦有人修改就要立即同步给所有人。
    • 直播弹幕:例如虎牙、斗鱼等各大直播平台,在直播时都是有弹幕的,遇到一些精彩片段时,往往会有弹幕刷屏。在这种情况下使用WebSocket会有一个更好的用户体验。
    • 位置共享:例如微信里位置共享,这种场景需要用户实时的共享自己的位置给服务器,服务器收到位置信息后,要实时的推送给其它共享者的,实时性要求较高;百度地图导航系统,在自己位置到达某个地方之后,语音会立即播报前面道路情况,比如上高架、下地道、拐弯、直行、学校慢行等等。这种场景实时性特别高,汽车速度很快,延迟1秒钟,可能就错过了最佳提醒时机。
    • 其他通过定义WebSocket子协议的扩展支持:例如sip、mqtt、xmpp、stomp等。

    四、WebSocket协议栈

    在这里插入图片描述
    WebSocket是基于TCP的应用层协议。需要特别注意的是:虽然WebSocket协议在建立连接时会使用HTTP协议,但这并意味着WebSocket协议是基于HTTP协议实现的。

    五、WebSocket与HTTP的区别

    在这里插入图片描述

    • 通信方式不同。WebSocket是双向通信模式,客户端与服务器之间只有在握手阶段是使用HTTP协议的“请求-响应”模式交互,而一旦连接建立之后的通信则使用双向模式交互,不论是客户端还是服务端都可以随时将数据发送给对方;而HTTP协议则至始至终都采用“请求-响应”模式进行通信。也正因为如此,HTTP协议的通信效率没有WebSocket高。

    • 协议格式不同。HTTP协议的一个数据包就是一条完整的消息;而WebSocket客户端与服务端通信的最小单位是帧,由1个或多个帧组成一条完整的消息。即:发送端将消息切割成多个帧,并发送给服务端;服务端接收消息帧,并将关联的帧重新组装成完整的消息。

    六、WebSocket握手过程

    • 客户端到服务端
      在这里插入图片描述
      GET ws://localhost…… HTTP/1.1 :打开阶段握手,使用http1.1协议。
      Upgrade:websocket,表示请求为特殊http请求,请求的目的是要将客户端和服务端的通信协议从http升级为websocket。
      Sec-websocket-key:Base64 encode 的值,是浏览器随机生成的。客户端向服务端提供的握手信息。

    • 服务端到客户端
      在这里插入图片描述
      101状态码:表示切换协议。服务器根据客户端的请求切换到Websocket协议。
      Sec-websocket-accept: 将请求头中的Set-websocket-key添加字符串并做SHA-1加密后做Base64编码,告知客户端服务器能够发起websocket连接。

    • 客户端发起连接的约定
      如果请求为wss,则在TCP建立后,进行TLS连接建立。
      请求的方式必须为GET,HTTP版本至少为HTTP1.1。
      请求头中必须有Host。
      请求头中必须有Upgrade,取值必须为websocket。
      请求头中必须有Connection,取值必须为Upgrade。
      请求头中必须有Sec-WebSocket-Key,取值为16字节随机数的Base64编码。
      请求头中必须有Sec-WebSocket-Version,取值为13。
      请求头中可选Sec-WebSocket-Protocol,取值为客户端期望的一个或多个子协议(多个以逗号分割)。
      请求头中可选Sec-WebSocket-Extensitons,取值为子协议支持的扩展集(一般是压缩方式)。
      可以包含cookie、Authorization等HTTP规范内合法的请求头。

    • 客户端检查服务端的响应
      服务端返回状态码为101代表升级成功,否则判定连接失败。
      响应头中缺少Upgrade或取值不是websocket,判定连接失败。
      响应头中缺少Connection或取值不是Upgrade,判定连接失败。
      响应头中缺少Sec-WebSocket-Accept或取值非法(其值为请求头中的Set-websocket-key添加字符串并做SHA-1加密后做Base64编码),判定连接失败。
      响应头中有Sec-WebSocket-Extensions,但取值不是请求头中的子集,判定连接失败。
      响应头中有Sec-WebSocket-Protocol,但取值不是请求头中的子集,判定连接失败。

    • 服务端处理客户端连接
      服务端根据请求中的Sec-WebSocket-Protocol 字段,选择一个子协议返回,如果不返回,表示不同意请求的任何子协议。如果请求中未携带,也不返回。
      如果建立连接成功,返回状态码为101。
      响应头Connection设置为Upgrade。
      响应头Upgrade设置为websocket。
      Sec-WebSocket-Accpet根据请求头Set-websocket-key计算得到,计算方式为:Set-websocket-key的值添加字符串: 258EAFA5-E914-47DA-95CA-C5AB0DC85B11并做SHA-1加密后得到16进制表示的字符串,将每两位当作一个字节进行分隔,得到字节数组,对字节数组做Base64编码。

    七、WebSocket帧格式

    WebSocket通信流程如下:
    在这里插入图片描述
    Websocket帧格式如下:
    在这里插入图片描述

    • 第一部分
      在这里插入图片描述
      FIN:1位,用于描述消息是否结束,如果为1则该消息为消息尾部,如果为零则还有后续数据包。
      RSV1,RSV2,RSV3:各1位,用于扩展定义,如果没有扩展约定的情况则必须为0。
      OPCODE:4位,用于表示消息接收类型,如果接收到未知的opcode,接收端必须关闭连接。
      OPCODE说明:
      0x0表示附加数据帧,当前数据帧为分片的数据帧。
      0x1表示文本数据帧,采用UTF-8编码。
      0x2表示二进制数据帧。
      0x3-7暂时无定义,为以后的非控制帧保留。
      0x8表示连接关闭。
      0x9表示ping。
      0xA表示pong。
      0xB-F暂时无定义,为以后的控制帧保留。

    • 第二部分
      在这里插入图片描述
      MASK:1位,用于标识PayloadData是否经过掩码处理。服务端发送给客户端的数据帧不能使用掩码,客户端发送给服务端的数据帧必须使用掩码。如果一个帧的数据使用了掩码,那么在Maksing-key部分必须是一个32个bit位的掩码,用来给服务端解码数据。
      Payload len:数据的长度:默认位7个bit位。如果数据的长度小于125个字节(注意:是字节)则用默认的7个bit来标示数据的长度。如果数据的长度为126个字节,则用后面相邻的2个字节来保存一个16bit位的无符号整数作为数据的长度。如果数据的长度大于126个字节,则用后面相邻的8个字节来保存一个64bit位的无符号整数作为数据的长度。
      payload len本来是只能用7bit来表达的,也就是最多一个frame的payload只能有127个字节,为了表示更大的长度,给出的解决方案是添加扩展payload len字段。当payload实际长度超过126(包括),但在2^16-1长度内,则将payload len置为126,payload的实际长度由长为16bit的extended payload length来表达。当payload实际长度超过216(包括),但在264-1长度内,则将payload置为127,payload的实际长度由长为64bit的extended payload length来表达。

    • 第三部分
      在这里插入图片描述
      数据掩码:如果MASK设置位0,则该部分可以省略,如果MASK设置位1,则Masking-key是一个32位的掩码。用来解码客户端发送给服务端的数据帧。

    • 第四部分
      在这里插入图片描述
      数据:该部分,也是最后一部分,是帧真正要发送的数据,可以是任意长度。

    八、WebSocket分片传输

    在这里插入图片描述

    • 控制帧可能插在一个Message的多个分片之间,但一个Message的分片不能交替传输(除非有扩展特别定义)。
    • 控制帧不可分片。
    • 分片需要按照分送方提交顺序传递给接收方,但由于IP路由特性,实际并不能保证顺序到达。

    控制帧包括:
    Close:用于关闭连接,可以携带数据,表示关闭原因。
    Ping:可以携带数据。
    Pong:用于Keep-alive,返回最近一次Ping中的数据,可以只发送Pong帧,做单向心跳。

    连接关闭时状态码说明:
    在这里插入图片描述

    九、WebSocket相关扩展

    1. Stomp
      STOMP是基于帧的协议,它的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。是属于消息队列的一种协议, 和AMQP, JMS平级。它的简单性恰巧可以用于定义websocket的消息体格式. STOMP协议很多MQ都已支持, 比如RabbitMq, ActiveMq。生产者(发送消息)、消息代理、消费者(订阅然后收到消息)。

    在这里插入图片描述
    2. SockJs
    SockJS是一个浏览器JavaScript库,它提供了一个类似于网络的对象。SockJS提供了一个连贯的、跨浏览器的Javascript API,它在浏览器和web服务器之间创建了一个低延迟、全双工、跨域通信通道。
    SockJS的一大好处在于提供了浏览器兼容性。优先使用原生WebSocket,如果在不支持websocket的浏览器中,会自动降为轮询的方式。 除此之外,spring也对socketJS提供了支持。
    3. Socket.io
    Socket.io实际上是WebSocket的父集,Socket.io封装了WebSocket和轮询等方法,会根据情况选择方法来进行通讯。
    Sockei.io最早由Node.js实现,Node.js提供了高效的服务端运行环境,但由于Browser对HTML5的支持不一,为了兼容所有浏览器,提供实时的用户体验,并为开发者提供客户端与服务端一致的编程体验,于是Socket.io诞生了。Java模仿Node.js实现了Java版的Netty-socket.io库。
    Socket.io将WebSocket和Polling机制以及其它的实时通信方式封装成通用的接口,并在服务端实现了这些实时机制相应代码,包括:AJAX Long Polling、Adobe Flash Socket、AJAX multipart streaming、Forever Iframem、JSONP Polling。

    展开全文
  • ActiveMQ允许客户端使用多种协议接收消息,其中WebSocket协议的URL使用ws://开头,默认端口号是61614
  • websocket协议与实现原理

    千次阅读 2022-02-26 17:53:29
    文章目录一、websocket二、websocket协议实现websocket协议格式websocket如何验证客户端合法websocket传输的明文和密文的传输websocket如何断开实现 一、websocket websocket是一个基于tcp的应用层的协议,对...
  • WebSocket协议原理

    千次阅读 2022-01-19 18:01:15
    1、WebSocket协议是什么 1.1 WebSocket是应用层协议 WebSocket是基于TCP的应用层协议,用于在C/S架构的应用中实现双向通信,关于WebSocket协议的详细规范和定义参见rfc6455。 需要特别注意的是:虽然WebSocket协议...
  • 为什么需要 WebSocket协议

    千次阅读 多人点赞 2022-02-10 17:13:13
    websocket是HTML5的一种新协议,允许服务器向客户端传递信息,实现浏览器和客户端双工通信。
  • WebSocket协议主要分为两部分,第一部分是连接许可验证和验证后的数据交互.连接许可验证比较简单,由Client发送一个类似于HTTP的请求,服务端获取请求后根据请求的KEY生成对应的值并返回. websocket协议分析 这图有点...
  • WebSocket协议解析

    千次阅读 2022-01-02 13:33:09
    文章目录WebSocket解决了什么问题?简介设计原理协议格式数据帧格式掩码掩码算法掩码作用流程URI格式握手建立客户端服务端如何验证握手被服务器接受?心跳帧关闭帧关闭帧的错误码如何维持长连接应用场景负载均衡...
  • rfc6455的英文原版下载,资源来源自:http://www.52im.net/thread-3172-1-1.html
  • WebSocket是一种在单个TCP连接上进行全双工通信的协议WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,795
精华内容 24,318
关键字:

websocket协议

友情链接: music-.rar