WebSocket协议入门介绍

文章目录

    • WebSocket协议是什么
      • WebSocket是应用层协议
      • WebSocket与Http的区别
    • 为什么要使用WebSocket
    • 如何使用WebSocket
      • 客户端API
      • 在客户端使用WebSocket
      • 在服务端使用WebSocket
      • 反向代理对WebSocket的支持

WebSocket协议是什么

WebSocket是应用层协议

WebSocket是基于TCP的应用层协议,用于在C/S架构的应用中实现双向通信,关于WebSocket协议的详细规范和定义参见rfc6455。
需要特别注意的是:虽然WebSocket协议在建立连接时会使用HTTP协议,但这并意味着WebSocket协议是基于HTTP协议实现的。
WebSocket属于应用层协议

WebSocket与Http的区别

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

2.协议格式不同
WebSocket与HTTP的协议格式是完全不同的,具体来讲:
(1)HTTP协议(参见:rfc2616)比较臃肿,而WebSocket协议比较轻量。
(2)对于HTTP协议来讲,一个数据包就是一条完整的消息;而WebSocket客户端与服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message)。即:发送端将消息切割成多个帧,并发送给服务端;服务端接收消息帧,并将关联的帧重新组装成完整的消息。
WebSocket协议格式:

 0                   1                   2                   30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

HTTP请求消息格式:

Request-LineCRLF
general-headerCRLF
request-headerCRLF
entity-headerCRLF
CRLF
[ message-body ]

HTTP响应消息格式:

Status-LineCRLF
general-headerCRLF
response-headerCRLF
entity-headerCRLF
CRLF
[ message-body ]

虽然WebSocket和HTTP是不同应用协议,但rfc6455规定:“WebSocket设计为通过80和443端口工作,以及支持HTTP代理和中介”,从而使其与HTTP协议兼容。为了实现兼容性,WebSocket握手时使用HTTP Upgrade头从HTTP协议更改为WebSocket协议,参考:WebSocket维基百科 。

为什么要使用WebSocket

随着Web应用的发展,特别是动态网页的普及,越来越多的场景需要实现数据动态刷新。
在早期的时候,实现数据刷新的方式通常有如下3种:
1.客户端定时查询
客户端定时查询(如:每隔10秒钟查询一次)是最原始也是最简单的实现数据刷新的方法,服务端不用做任何改动,只需要在客户端添加一个定时器即可。但是这种方式的缺点也很明显:大量的定时请求都是无效的,因为服务端的数据并没有更新,相应地也导致了大量的带宽浪费。

2.长轮训机制
长轮训机制是对客户端定时查询的一种改进,即:客户端依旧保持定时发送请求给服务端,但是服务端并不立即响应,而是等到真正有数据更新的时候才发送给客户端。实际上,并不是当没有数据更新时服务端就永远都不响应客户端,而是需要在等待一个超时时间之后结束该次长轮训请求。相对于客户端定时查询方式而言,当数据更新频率不确定时长轮训机制能够很明显地减少请求数。但是,在数据更新比较频繁的场景下,长轮训方式的优势就没那么明显了。
在Web开发中使用得最为普遍的长轮训实现方案为Comet(Comet (web技术)),Tomcat和Jetty都有对应的实现支持,详见:WhatIsComet,Why Asynchronous Servlets。

3.HTTP Streaming
不论是长轮训机制还是传统的客户端定时查询方式,都需要客户端不断地发送请求以获取数据更新,而HTTP Streaming则试图改变这种方式,其实现机制为:客户端发送获取数据更新请求到服务端时,服务端将保持该请求的响应数据流一直打开,只要有数据更新就实时地发送给客户端。
虽然这个设想是非常美好的,但这带来了新的问题:
(1)HTTP Streaming的实现机制违背了HTTP协议本身的语义,使得客户端与服务端不再是“请求-响应”的交互方式,而是直接在二者建立起了一个单向的“通信管道”。
(2)在HTTP Streaming模式下,服务端只要得到数据更新就发送给客户端,那么就需要客户端与服务端协商如何区分每一个更新数据包的开始和结尾,否则就可能出现解析数据错误的情况。
(3)另外,处于客户端与服务端的网络中介(如:代理)可能会缓存响应数据流,这可能会导致客户端无法真正获取到服务端的更新数据,这实际上与HTTP Streaming的本意是相违背的。
鉴于上述原因,在实际应用中HTTP Streaming并没有真正流行起来,反之使用得最多的是长轮训机制。

显然,上述几种实现数据动态刷新的方式都是基于HTTP协议实现的,或多或少地存在这样那样的问题和缺陷;而WebSocket是一个全新的应用层协议,专门用于Web应用中需要实现动态刷新的场景。
相比起HTTP协议,WebSocket具备如下特点:

  1. 支持双向通信,实时性更强。
  2. 更好的二进制支持。
  3. 较少的控制开销:连接创建后,WebSockete客户端、服务端进行数据交换时,协议控制的数据包头部较小。
  4. 支持扩展。

如何使用WebSocket

客户端API

在Web应用的网页中使用WebSocket,WebSocket对象提供了用于创建和管理WebSocket连接,以及可以通过该连接发送和接收数据的API。
1.构造函数

可以使用WebSocket类的构造函数(WebSocket(url[, protocols]))实例化一个对象,如:

var url = "ws://host:port/endpoint";
var ws = new WebSocket(url);

执行上述语句之后,浏览器将与服务端建立一个WebSocket连接,同时返回一个WebSocket实例对象ws。

2.对象属性
WebSocket实例对象具备如下属性:

  • WebSocket.binaryType: 返回websocket连接所传输二进制数据的类型。
  • WebSocket.bufferedAmount:只读属性,用于返回已经被send()方法放入队列中但还没有被发送到网络中的数据的字节数。一旦队列中的所有数据被发送至网络,则该属性值将被重置为0。但是,若在发送过程中连接被关闭,则属性值不会重置为0。如果你不断地调用send(),则该属性值会持续增长。
  • WebSocket.extensions:只读属性,返回服务器已选择的扩展值。目前,链接可以协定的扩展值只有空字符串或者一个扩展列表。
  • WebSocket.protocol:只读属性,用于返回服务器端选中的子协议的名字;这是一个在创建WebSocket对象时,在参数protocols中指定的字符串。
  • WebSocket.readyState:只读属性,返回当前WebSocket对象的链接状态,可能的值为WebSocket中定义的常量:WebSocket.CONNECTING,WebSocket.OPEN,WebSocket.CLOSING,WebSocket.CLOSED。
  • WebSocket.url:只读属性,返回值为当构造函数创建WebSocket实例对象时URL的绝对路径。
  • WebSocket.onopen:用于指定连接成功后的回调函数,当WebSocket的连接状态readyState变为“OPEN”时调用;这意味着当前连接已经准备好发送和接受数据,这个事件处理程序通过事件(建立连接时)触发。
  • WebSocket.onclose:用于指定连接关闭后的回调函数,当WebSocket的连接状态readyState变为“CLOSED”时被调用,它接收一个名字为“close”的CloseEvent事件对象。
  • WebSocket.onmessage:用于指定当从服务器接受到信息时的回调函数,当从服务器收到一条消息时,该回调函数将被调用,在函数中接受一命名为“message”的MessageEvent事件对象。
  • WebSocket.onerror:用于指定连接失败后的回调函数,定义一个发生错误时执行的回调函数,此事件的事件名为"error"。

3.对象方法
WebSocket定义了2个方法:
(1)WebSocket.send(data):向服务器发送数据,将需要通过WebSocket连接传输至服务器的数据排入队列,并根据所需要传输的数据字节的大小来增加属性bufferedAmount的值 。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭。
参数data为传输至服务器的数据,它必须是以下类型之一:

  • USVString:文本字符串。字符串将以UTF-8格式添加到缓冲区,并且属性bufferedAmount将加上该字符串以UTF-8格式编码时的字节数的值。
  • ArrayBuffer:您可以使用一个有类型的数组对象发送底层二进制数据,其二进制数据内存将被缓存于缓冲区,属性bufferedAmount将加上所需字节数的值。
  • Blob:Blob类型将队列blob中的原始数据以二进制传输,属性bufferedAmount将加上原始数据的字节数的值。
  • ArrayBufferView:以二进制帧的形式发送任何JavaScript类数组对象,其二进制数据内容将被队列于缓冲区中,属性bufferedAmount将加上对应字节数的值。

(2)WebSocket.close([code[, reason]]):关闭当前连接,如果连接已经关闭,则此方法不执行任何操作。
参数:

  • code:可选,为一个数字状态码,它解释了连接关闭的原因。如果没有传这个参数,默认使用1005。CloseEvent的允许的状态码见状态码列表。
  • reason:可选,一个人类可读的字符串,它解释了连接关闭的原因,这个UTF-8编码的字符串不能超过123个字节。

异常:

  • INVALID_ACCESS_ERR:一个无效的code。
  • SYNTAX_ERR:reason字符串太长(超过123字节)。

更多WebSockete API的详细内容参见W3C的定义:The WebSocket API。

在客户端使用WebSocket

如下为在网页中使用原生WebSocket的实现方式。

var url = "ws://localhost:8080/websocket/text";
var ws = new WebSocket(url);
ws.onopen = function(event) {console.log("websocket connection open.");console.log(event);
};ws.onmessage = function(event) {console.log("websocket message received.")console.log(event.data);
};ws.onclose = function (event) {console.log("websocket connection close.");console.log(event.code);
};ws.onerror = function(event) {console.log("websocket connection error.");console.log(event);
};

在Web网页中使用WebSocket需要浏览器支持,不同浏览器软件版本对WebSocket的支持情况详见浏览器兼容性。

另外,WebSocket客户端除了可以在网页中使用,目前还存在一些独立的客户端组件,如:
1.Jetty WebSocket Client API
2.websockets-api-java-spring-client
3.Java-WebSocket

在服务端使用WebSocket

在服务端使用WebSocket需要服务器组件支持,如下以在Tomcat 8.5.41(Tomcat 7之后才支持WebSocket)中使用原生WebSocket为例。
由于在服务端使用WebSocket需要使用到WebSocket的API,因此需要添加API依赖管理:

<dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-websocket-api</artifactId><version>8.5.41</version>
</dependency>

使用注解方式编写WebSocket服务端:

@ServerEndpoint(value="/websocket/text")
public class WebSocketTest {private static final Logger logger = LoggerFactory.getLogger(WsChatAnnotation.class);private static final AtomicInteger counter = new AtomicInteger(0);                                    // 客户端计数器private static final Set<WsChatAnnotation> connections = new CopyOnWriteArraySet<WsChatAnnotation>(); // 客户端websocket连接集合private Session session = null;                                                                       // WebSocket会话对象private Integer number = 0;                                                                           // 客户端编号public WsChatAnnotation() {number = counter.incrementAndGet();}/*** 客户端建立websocket连接* @param session*/@OnOpenpublic void start(Session session) {logger.info("on open");this.session = session;connections.add(this);try {session.getBasicRemote().sendText(new StringBuffer().append("Hello: ").append(number).toString());} catch (IOException e) {e.printStackTrace();}}/*** 客户端断开websocket连接*/@OnClosepublic void close() {logger.info("session close");try {this.session.close();} catch (IOException e) {e.printStackTrace();} finally {connections.remove(this);}}/*** 接收客户端发送的消息* @param message*/@OnMessagepublic void message(String message) {logger.info("message: {}", message);for(WsChatAnnotation client : connections) {synchronized (client) {try {client.session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();}}}}@OnErrorpublic void error(Throwable t) {logger.error("client: {} error", number, t.getMessage());}
}

反向代理对WebSocket的支持

当下的Web应用架构通常都是集群化部署,前端使用反向代理或者直接部署负载均衡器,这就要求反向代理或者负载均衡器必须支持WebSocket协议。
目前Nginx,Haporxy都已经支持WebSocket协议。

如下为在使用nginx作为反向代理的场景下,配置nginx代理websocket协议。

# add websocket proxy
location ~ /ws {proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_pass http://8080;
}

【参考】
https://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/ Spring MVC 3.2 Preview: Techniques for Real-time Updates
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket#构造函数 WebSocket
https://www.cnblogs.com/chyingp/p/websocket-deep-in.html WebSocket协议:5分钟从入门到精通
http://www.ruanyifeng.com/blog/2017/05/websocket.html WebSocket 教程
https://blog.csdn.net/chszs/article/details/26369257 Nginx担当WebSockets代理
http://blog.fens.me/nodejs-websocket-nginx/ Nginx反向代理Websocket

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555514.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

远程ykvm 插件移值java_Centos7 命令行下kvm安装windows,linux

查看是否支持egrep "svm|vmx" /proc/cpuinfo |uniq安装软件yum install libvirt -yyum -y install qemu-kvmsystemctl enable libvirtd && systemctl start libvirtd# 启动libvitd服务查看 ip add #查看是否有 virbr0网卡 yum install virt-installcentos7 例…

php7.0扩展yac,php扩展之yac安装

git克隆$ git clone https://github.com/laruence/yac.git解压安装注意这里的php-config可能不一样查看php-config位置$whereis php编译安装$ cd yac$ phpize$ ./configure --with-php-config/usr/bin/php-config$ make$ make install修改php.ini,载入扩展,重启php-fpmextensio…

springboot+websocket实现服务端、客户端

一、引言 小编最近一直在使用springboot框架开发项目&#xff0c;毕竟现在很多公司都在采用此框架&#xff0c;之后小编也会陆续写关于springboot开发常用功能的文章。 什么场景下会要使用到websocket的呢&#xff1f; websocket主要功能就是实现网络通讯&#xff0c;比如说…

输出有样式的php,PHP导出带样式的Excel

工作中做导出的时候&#xff0c;需要导出自定义的表格或嫌弃导出的Excel格式太难看了。需要设置颜色、字号大小、加粗、合并单元格等等。Paste_Image.pngPHP代码&#xff1a;php/**导出文件return string*/public function export(){$file_name "成绩单-".date(&quo…

idea创建多模块Springboot项目、导入多模块、删除多模块

前言 在eclipse中有Workspace&#xff08;工作空间&#xff09;和 Project&#xff08;工程&#xff09;的概念在 IDEA中只有 Project&#xff08;工程&#xff09;和 Module&#xff08;模块&#xff09;的概念。这个地方刚开始用的时候会很容易理不清它们之间的关系。在eclip…

包含内部类的.java文件编译后生成几个.class文件

如果一个类有内部类&#xff0c;编译将生成几个字节码文件&#xff0c;规则是怎样呢&#xff1f; 写在前&#xff0c;自己动手丰衣足食&#xff0c;结论只有个人实验支持&#xff0c;没有官方数据支持&#xff0c;欢迎自行查阅文档然后来指正&#xff0c;轻喷&#xff0c;谢谢…

Intellij IDEA远程debug教程实战和要点总结

远程调试&#xff0c;特别是当你在本地开发的时候&#xff0c;你需要调试服务器上的程序时&#xff0c;远程调试就显得非常有用。 JAVA支持调试功能&#xff0c;本身提供了一个简单的调试工具JDB&#xff0c;支持设置断点及线程级的调试同时&#xff0c;不同的JVM通过接口的协…

Idea进行远程Debug

Idea远程调试 当把一个本地项目部署到远程服务器后有可能出现意想不到错误&#xff0c;这个时候通过远程调试能够更清楚的找到bug所在位置。本篇主要讲解如何使用Idea开发工具 进行调试1.远程调试服务器上面的SpringBoot 项目2.远程调试服务器上运行在tomcat中的项目&#xff…

matlab表示DFT和DTFT,【 MATLAB 】离散傅里叶级数(DFS)与DFT、DTFT及 z变换之间的关系...

上篇博文我们简单的讨论了离散傅里叶级数DFS和离散傅里叶变换DFT之间的关系&#xff0c;简单地说&#xff0c;DFT就是DFS在一个周期内的表现。为了后面讨论方便&#xff0c;这里给出DFS的系数公式(分析公式)&#xff1a;(1) 其中&#xff1a;综合公式&#xff1a;(2) 为了对比&…

php分列显示,【杂谈】PHP数组怎样按键名完成降序分列

PHP关联数组按键名完成降序分列&#xff0c;我们能够直接经由过程PHP中的krsor()函数来完成。krsort函数示意对数组根据键名逆向排序。那末在前面的文章中&#xff0c;已给人人引见过PHP关联数组按键名完成升序分列的要领。下面我们继承连系简朴的示例&#xff0c;给人人引见PH…

spike 序列matlab,SPKtool1.0.1 神经信号spike 分类及处理 工具包 matlab 238万源代码下载- www.pudn.com...

文件名称: SPKtool1.0.1下载收藏√ [5 4 3 2 1 ]开发工具: matlab文件大小: 937 KB上传时间: 2013-03-05下载次数: 3提 供 者: 无语详细说明&#xff1a;神经信号spike 分类及处理 工具包-A toolbox for neurophysiological data processing文件列表(点击判断是否您需要的…

cuda加速求解龙格库塔四阶五步积分

一般代码使用cuda加速的方法&#xff1a; 使用PyTorch进行加速&#xff1a; 首先&#xff0c;你需要将你的ODE系统定义为PyTorch模型&#xff0c;这样可以利用PyTorch的自动微分功能和GPU加速。然后&#xff0c;你需要将数据和参数转换为PyTorch张量&#xff0c;并将它们移动到…

Idea怎么实现画类图

1.【file】-【Settings】-【Tools】-【Diagrams】-勾选Java Class Diagram的选项 2.右击类&#xff0c;【Diagrams】-【Show Diagram】

如何在linux下创建一个可运行shell脚本?

linux系统下&#xff0c;经常会用到自启动脚本&#xff0c;那么如何新建一个自启动脚本&#xff1f; 工具/原料 linux系统 方法/步骤 1 新建一个.sh文件&#xff0c;touch test.sh 2 编辑test.sh文件&#xff0c;vi test.sh 然后键入i&#xff0c;输入内容&#xff0c; #!…

TCP 协议(包含三次握手,四次挥手)

文章目录1.确认应答机制 (ACK)2.超时重传3.1建立连接 - 三次握手 ▲3.2.断开连接 - 四次挥手 ▲1.确认应答机制 (ACK) 确认应答是可靠传输的最核心机制 接收方反馈一个应答报文(ACK)&#xff0c;表示已收到 假设现在 A 想去 B 家里玩游戏&#xff0c;于是 A 给 B 发消息&…

Redis使用单线程却快到飞起的原因

文章目录Redis为什么用单线程&#xff1f;多线程的开销Redis使用单线程为什么还这么快&#xff1f;网络与IO操作的潜在阻塞点基于多路复用的高性能IO模型回调机制Redis的性能瓶颈点其他Redis相关的有趣问题1. 为什么要用Redis&#xff0c;直接访问内存不好吗&#xff1f;2. 数据…

线程池参数到底要怎么配?

文章目录1 线程池快速回顾2 现有设置参数的方法及不足3 如何设置核心线程数&#xff08;corePoolSize&#xff09;4 如何设置最大线程数&#xff08;maxPoolSize&#xff09;5 如何改变等待队列长度想必大家对Java里面线程池&#xff08; 类&#xff09;一定不陌生吧&#xff0…

彻底搞懂Cookie、Session、JWT和Token

文章目录引入&#xff1a;http是一个无状态协议&#xff1f;怎么解决呢&#xff1f;一、Cookie和Session1.1 cookie 注意事项&#xff1a;1.2 cookie 重要的属性1.3 session 注意事项&#xff1a;1.4 Cookie 和 Session 的区别&#xff1a;二、token&#xff08;令牌&#xff0…

你真的知道什么是多线程吗?为什么要学习多线程?

文章目录1、多线程的含义2、原理3、优势4、线程与进程的区别5、线程与多线程的区别6、线程调度的分类7、同步与异步8、并发与并行9、为什么要使用线程池10、线程池的好处11、线程池的分类12、意义1、多线程的含义 多线程&#xff08;multithreading&#xff09;&#xff0c;是…

oracle 表关联索引优化,Oracle执行计划调优-超级大表关联超级小表的性能调优

今日客户现场出现一个查询SQL异常慢的情况。用时分钟级别。SELECT *FROM (SELECT a1.*, rownum rnFROM (SELECT openOrder2017.exchId,............openOrder2017.internalbizmark,customer.typeIdListFROM openOrder2017, customerWHERE openOrder2017.custId customer.custI…