【万字长文和Demo搞懂】全双工通讯的WebSocket协议

2024-03-23 01:40

本文主要是介绍【万字长文和Demo搞懂】全双工通讯的WebSocket协议,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 1. HTTP轮询
      • 1.1 短连接
      • 1.2 长连接
      • 1.3 Socket
    • 2. WebSocket
      • 2.1 WebSocket的演进
      • 2.2 🚀Websocket
      • 2.3 为什么要选择WebSocket
      • 2.4 基于TCP
      • 2.5 成熟的WebSocket服务器端实现
    • 3. WebSocket协议通信原理
      • 3.1 协议升级
      • 3.2 建立WebSocket连接
      • 3.3 握手
      • 3.4 请求数据
    • 4. 客户端通信
      • 4.1 WebSocket对象
      • 4.2 WebSocket事件
      • 4.3 WebSocket方法
    • 5. 服务端通信
      • 心跳保活
    • 6. WebSocket示例
    • 7. WebSocket介绍和Socket的区别
    • 8. WebSocket的使用场景

📚 官方文档 The WebSocket protocol

🧊 WebSocket官网

🔗安利文章 全双工通信的 WebSocket

【实时 Web 应用的窘境】

Web 应用的信息交互过程通常是客户端通过浏览器发出一个请求,服务器端接收和审核完请求后进行处理并返回结果给客户端,然后客户端浏览器将信息呈现出来,这种机制对于信息变化不是特别频繁的应用尚能相安无事,但是对于那些实时要求比较高的应用来说,比如说在线游戏、在线证券、设备监控、新闻在线播报、RSS 订阅推送等等,当客户端浏览器准备呈现这些信息的时候,这些信息在服务器端可能已经过时了。所以保持客户端和服务器端的信息同步是实时 Web 应用的关键要素,对 Web 开发人员来说也是一个难题。在 WebSocket 规范出来之前,开发人员想实现这些实时的 Web 应用,不得不采用一些折衷的方案,其中最常用的就是轮询 (Polling) 和 Comet 技术,而 Comet 技术实际上是轮询技术的改进,又可细分为两种实现方式,一种是长轮询机制,一种称为流技术。


1. HTTP轮询

首先HTTP中,没有长连接这样的类型HTTP要实现长连接,是建立在TCP协议的基础上的。

TCP的keep alive和HTTP的Keep-alive

  • TCP的keep alive是检查当前TCP连接是否活着
  • HTTP的Keep-alive是要让一个TCP连接活久点

1.1 短连接

Web 应用通过通过频繁的异步 JavaScript 和 XML (AJAX) 请求来实现轮循

在这里插入图片描述

客户端和服务端进行一次HTTP请求/响应之后,就关闭连接。下一次的HTTP请求/响应操作需要重新建立。

# 在首部字段中设置短连接
Connection:close

在一次请求/响应之后,就会关闭连接。这种方式下,是不适合获取实时信息的,客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。客户端会轮询,有没有新消息。这种方式连接数会很多,一个接受,一个发送。而且每次发送请求都会有 HTTP 的 Header,会很耗流量,也会消耗 CPU 的利用率。

  • 优点:短连接,服务器处理简单,支持跨域、浏览器兼容性较好。
  • 缺点:有一定延迟、服务器压力较大,浪费带宽流量、大部分是无效请求。

1.2 长连接

HTTP1.1支持了并且默认了长连接。因为有了长连接,所以不用定时轮询来房子连接关闭。长轮询是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。

客户端打开一个到服务器端的 AJAX 请求,然后等待响应,服务器端需要一些特定的功能来允许请求被挂起,只要一有事件发生,服务器端就会在挂起的请求中送回响应并关闭该请求。客户端在处理完服务器返回的信息后,再次发出请求,重新建立连接,如此循环。

在这里插入图片描述

客户端和服务端建立一次连接之后,会复用这条链接,可以在这条连接上进行多次请求/响应操作。当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。

这种方式也有一定的弊端,实时性不高。如果是高实时的系统,肯定不会采用这种办法。因为一个 GET 请求来回需要 2个 RTT,很可能在这段时间内,数据变化很大,客户端拿到的数据已经延后很多了。

另外,网络带宽低利用率的问题也没有从根源上解决。每个 Request 都会带相同的 Header。

// 只设置Connection:keep-alive,表明连接永久有效
Connection: keep-aliveKeep-Alive: timeout = 60
  • 表明连接建立之后,空闲时间超过60秒,连接失效
  • 如果在空闲第58秒使用此连接,则仍然有效,
  • 并且使用完之后,重新计数空闲时间,空闲60秒无再使用,连接失效

持久连接可以设置过期时间,也可以不设置。

  • 优点:减少轮询次数,低延迟,浏览器兼容性较好。
  • 缺点:服务器需要保持大量连接。

1.3 Socket

基于流的方式,在服务器往客户端推送,这个方向的流实时性比较好。但是依旧是单向的,客户端请求服务器依然还需要一次 HTTP 请求

Socket网络编程

  • Socket在应用之间建立信道连接,通过IO流来传输数据
  • 基于JFrame,封装性、易用性低

在这里插入图片描述

基于TCP协议

【客户端】:

  • 创建客户端套接字(尝试连接)
  • 等待连接
  • 获取输入流、输出流
  • 关闭连接

【服务端】:

  • 创建服务端套接字
  • 等待连接
  • 获取输入流、输出流
  • 关闭连接

【数据传输】:在两个套接字建立的socket通道中进行传输数据流


  • 短轮询效率低,非常浪费资源(网络带宽和计算资源)。有一定延迟、服务器压力较大,并且大部分是无效请求。

  • 长轮询虽然省去了大量无效请求,减少了服务器压力和一定的网络带宽的占用,但是还是需要保持大量的连接。

  • 最后到了基于流的方式,在服务器往客户端推送,这个方向的流实时性比较好。但是依旧是单向的,客户端请求服务器依然还需要一次 HTTP 请求。



2. WebSocket

2.1 WebSocket的演进

综合这几种方案,会发现这些目前我们所使用的所谓的实时技术并不是真正的实时技术,它们只是在用 Ajax 方式来模拟实时的效果,在每次客户端和服务器端交互的时候都是一次 HTTP 的请求和应答的过程,而每一次的 HTTP 请求和应答都带有完整的 HTTP 头信息,这就增加了每次传输的数据量。

而且这些方案中客户端和服务器端的编程实现都比较复杂,在实际的应用中,为了模拟比较真实的实时效果,开发人员往往需要构造两个 HTTP 连接来模拟客户端和服务器之间的双向通讯,一个连接用来处理客户端到服务器端的数据传输,一个连接用来处理服务器端到客户端的数据传输,这不可避免地增加了编程实现的复杂度,也增加了服务器端的负载,制约了应用系统的扩展性。


2.2 🚀Websocket

Websocket是一种网络通信协议。

Websocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。没有了 Request 和 Response 的概念,两者地位完全平等,连接一旦建立,就建立了真持久性连接,双方可以随时向对方发送数据。使得服务器和客户端交互数据更加简单。

浏览器和客户端只需要完成一次握手,就可以创建持久性的连接,并进行双向的数据传输

HTTP协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求 / 响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理

这种通信模型有一个弊端:HTTP协议无法实现服务器主动向客户端发起消息。

在这里插入图片描述

这种单向请求的特点,注定了如果服务器有连续的状态变化,客戶端要获知就非常麻烦。

  • 因为服务器不会主动回复客户端说我这里有数据变化
  • 只能是客户端问服务器有没有数据发生变化,服务器给客户端做回应。

为了保持长时间的通信连接,就需要同步客户端不断地发送请求询问,称为轮询

大多数Web应用程序将通过频繁的异步AJAX请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者HTTP连接始终打开)

在这里插入图片描述

所以 WebSocket 和 HTTP 有一些交集,两者相异的地方还是很多。两者交集的地方在 HTTP 握手阶段,握手成功后,数据就直接从 TCP 通道传输。

轮询:在客户端定时的向服务器发送AJAX请求,获取服务端最新的数据

2.3 为什么要选择WebSocket

轮询都是先由客户端发起 Ajax 请求,才能进行通信,走的是HTTP协议,服务器端无法主动向客户端推送信息。

当出现类似体育赛事、聊天室、实时位置之类的场景时,轮询就显得十分低效和浪费资源

选择WebSocket的理由:

  • 全双工通信,服务器和客户端可以互相主动通信。由于协议是全双工的,所以服务器可以随时主动给客户端发数据。而HTTP协议中服务器不能主动向客户端发送请求,是单向通信的
  • 较少的控制开销。协议控制的数据包头部相对较小,长连接减少了每次连接时握手挥手的资源消耗
  • 保持长连接状态,实时性。HTTP要不断发送请求来保持连接,否则服务器响应完客户端就断开连接了

2.4 基于TCP

首先来阐明结论:

WebSocket和HTTP都是基于TCP协议的,如果要用UDP可以用 WebRTC协议

参见官方文档 Relationship to TCP and HTTP

相关问题参见stackoverflow 🔗 WebSockets with UDP

1.7 Relationship to TCP and HTTP

This section is non-normative.

The WebSocket protocol is an independent TCP-based protocol. Its
only relationship to HTTP is that its handshake is interpreted by
HTTP servers as an Upgrade request.

Based on the expert recommendation of the IANA, the WebSocket
protocol by default uses port 80 for regular WebSocket connections
and port 443 for WebSocket connections tunneled over TLS.

WebSocket 协议是基于 TCP 的独立协议。其与HTTP 的唯一关系是,其握手由HTTP 服务器作为升级请求。根据IANA的专家建议,WebSocket协议默认使用端口80作为常规的WebSocket连接,使用端口443作为通过TLS隧道的WebSocket连接。


2.5 成熟的WebSocket服务器端实现

目前一些比较成熟的 WebSocket 服务器端实现,如:

  • Kaazing WebSocket Gateway — 一个 Java 实现的 WebSocket Server
  • mod_pywebsocket — 一个 Python 实现的 WebSocket Server
  • Netty —一个 Java 实现的网络框架其中包括了对 WebSocket 的支持
  • node.js —一个 Server 端的 JavaScript 框架提供了对 WebSocket 的支持


3. WebSocket协议通信原理

HTTP协议:

在这里插入图片描述

延时时间长,而且只能是单向通信,单次通信完,连接就断开了。


3.1 协议升级

WebSocket协议是建立在HTTP协议基础之上的,在建立HTTP协议时,传输层如果是TCP协议就必然经历过了三次握手

建立TCP连接之后,开始建立WebSocket连接,上文说过WebSocket连接只需一次成功握手即可建立。握手过程如下图所示(图片来自互联网):

在这里插入图片描述

1. 首先客户端会发送一个握手包

这里就体现出了WebSocket与Http协议的联系,握手包的报文格式必须符合HTTP报文格式的规范。其中:

  • 方法必须位GET方法
  • HTTP版本不能低于1.1
  • 必须包含Upgrade头部,值必须为websocket
  • 必须包含Sec-WebSocket-Key头部,值是一个Base64编码的16字节随机字符串。
  • 必须包含Sec-WebSocket-Version头部,值必须为13
  • 其他可选首部可以参考:https://tools.ietf.org/html/rfc6455#section-4.1

2. 服务端验证客户端的握手包符合规范之后也会发送一个握手包给客户端。

格式如下:

  • 必须包含Connection头部,值必须为Upgrade
  • 必须包含一个Upgrade头部,值必须为websocket
  • 必须包含一个Sec-Websocket-Accept头部,值是根据如下规则计算的:

首先将一个固定的字符串

258EAFA5-E914-47DA-95CA-C5AB0DC85B11

拼接到Sec-WebSocket-Key对应值的后面。对拼接后的字符串进行一次SHA-1计算,将计算结果进行Base-64编码

3. 客户端收到服务端的握手包之后,验证报文格式时候符合规范,以2中同样的方式计算Sec-WebSocket-Accept并与服务端握手包里的值进行比对

其中任何一步不通过则不能建立WebSocket连接。


客户端发送Upgrade请求头来告知服务端协议升级,想要建立一个 WebSocket 连接。
在这里插入图片描述

【101状态码】

当客户端在请求里使用Upgrade报头,通知服务器它想改用除HTTP协议之外的其他协议时,客户端将获得此响应代码。101 响应代码表示“行,我现在改用另一个协议了”。

通常HTTP客户端会在收到服务器发来的101响应后关闭与服务器的TCP连接。101响应代码意味着,该客户端不再是一个HTTP客户端,而将成为另一种客户端。


3.2 建立WebSocket连接

在客户端建立一个 WebSocket 连接非常简单:要连接到端点,只需创建一个新的 WebSocket 实例,为新对象提供一个 URL,该 URL 表示要连接到的端点

var ws = new WebSocket('ws://localhost:9000');

请求的 URL 是ws://或者 wss://开头的,而不是 httP:// 或者 https://


3.3 握手

WebSocket协议的建立是基于HTTP协议之上的,先建立HTTP协议,然后通过发送101状态码,使用Upgrade报头升级为WebSocket协议

WebSocket 连接是在客户端和服务器之间的初始握手期间从 HTTP 协议升级到 WebSocket 协议建立的

此时,HTTP 连接断开,并由 WebSocket 连接通过相同的基础 TCP/IP 连接替换。默认情况下,WebSocket 连接使用与 HTTP (80) 和 HTTPS (443) 相同的端口。

在这里插入图片描述

WebSocket协议有两部分:握手数据传输,握手是基于HTTP协议的

3.4 请求数据

客户端request请求头:

GET ws://localhostchat HTTP/1.1
Host: localhost
Upgrade: websocket  //表示要升级到websocket协议
Connection: Upgrade
Sec-WebSocket-Extensions: permessage-deflate;
Sec-WebSocket-Key: 5fTJ1LTuh3RKjSJxydyifQ==		// 与响应头 Sec-WebSocket-Accept 相对应
Sec-WebSocket-Version: 13	//表示websocket协议的版本	

服务端response响应头:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: ZUip34t+bCjhkvxxwhmdEOyx9hE=
Sec-WebSocket-Extensions: permessage-deflate;

【字段说明】

头名称说明
Connection: Upgrade表示该HTTP请求是一个人协议升级请求
Upgrade: websocket协议升级为WebSocket协议
Sec-WebSocket-Version: 13表示websocket协议的版本
Sec-WebSocket-Key与响应头 Sec-WebSocket-Accept 相对应,用来唯一标识客户端和服务器
Sec-WebSocket-Extensions协议拓展

如果一次发送的消息过长,WebSocket的单个消息可被分割为多个数据帧来分组发送,直至末尾为FIN标志位 (PS:既然看到了FIN标志位,说明WebSocket协议是依据TCP的)

来自维基百科:

Once the connection is established, the client and server can send WebSocket data or text frames back and forth in full-duplex mode. The data is minimally framed, with a small header followed by payload.[36] WebSocket transmissions are described as “messages”, where a single message can optionally be split across several data frames. This can allow for sending of messages where initial data is available but the complete length of the message is unknown (it sends one data frame after another until the end is reached and marked with the FIN bit). With extensions to the protocol, this can also be used for multiplexing several streams simultaneously (for instance to avoid monopolizing use of a socket for a single large payload).[37]

一旦建立了连接,客户端和服务器可以在全双工模式下来回发送WebSocket数据或文本帧。WebSocket 传输被描述为 “消息”,其中单个消息可以选择在多个数据帧中分割,这允许发送初始数据可用但消息的完整长度未知的消息(它发送一个又一个数据帧,直到到达终点并标有 FIN 位)。通过协议的扩展,这还可用于同时多路复用多个流(例如,避免对单个大型有效负载的套接字进行独占使用)。


4. 客户端通信

4.1 WebSocket对象

​ 创建WebSocket对象:

var ws = new WebSocket(url);  //请求的地址

参数url格式:ws://ip地址:端口号/资源名称

4.2 WebSocket事件

WebSocket对象的相关事件:

事件事件处理程序描述
openwebsocket对象.onopen连接建立时触发
messagewebsocket对象.onmessage客户端接收服务端数据时触发
errorwebsocket对象.onerror通信发生错误是触发
closewebsocket对象.onclose连接关闭时触发

4.3 WebSocket方法

WebSocket对象的相关方法:

send():使用连接发送数据


5. 服务端通信

Java Websocket应用由一系列的 WebSocketEndpoint组成。 Endpoint是一个java对象,代表 WebSocket链接的一端。对于服务端,我们可以视为处理具体WebSocket消息的接口,就像Servlet之与HTTP请求一样。
我们可以通过两种方式定义 Endpoint:

  • 第一种是编程式,即继承类 javax.websocket,Endpoint并实现其方法
  • 第二种是注解式,即定义一个POJO,并添加 ServerEndpoint相关注解

Endpoint实例在 WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。在Endpoint接口中明确定义了与其生命周期相关的方法,规范实现者确保生命周期的各个阶段调用实例的相关方法。生命周期方法如下:

方法含义注解
onClose当会话开启时调用@OoClose
onOpen当开启一个新的会话,客户端与服务器握手成功后调用@OnOpen
onError连接规程中产生异常@OnError
OnMessage客户端接收服务端数据时触发@OnMessage

服务端如何接收客户端发送的数据呢?

通过为 Session添加 MessageHandler消息处理器来接收消息,当采用注解方式定义 Endpoint时,我们还可以通过@OnMessage注解指定接收消息的方法。

服务端如何推送数据绐客户端呢?

发送消息则由 RemoteEndpoint完成,其实例由 session维护,根据使用情况,我们可以通过Session.getBasicRemote获取同步消息发送的实例,然后调用其 sendXxx()方法就可以发送消息,可以通过Session.getAsyncRemote获取异步消息发送实例

心跳保活

在实际使用 WebSocket 中,长时间不通消息可能会出现一些连接不稳定的情况,这些未知情况导致的连接中断会影响客户端与服务端之前的通信,

为了防止这种的情况的出现,有一种心跳保活的方法:客户端就像心跳一样每隔固定的时间发送一次 ping,来告诉服务器,我还活着,而服务器也会返回 pong,来告诉客户端,服务器还活着。ping/pong 其实是一条与业务无关的假消息,也称为心跳包。

可以在连接成功之后,每隔一个固定时间发送心跳包,比如 60s:

setInterval(() => {ws.send('这是一条心跳包消息');
}, 60000)

通过上面的介绍,大家应该对 WebSocket 有了一定认识,其实并不神秘,这里对文章内容简单总结一下。当创建 WebSocket 实例的时候,会发一个 HTTP 请求,请求报文中有个特殊的字段 Upgrade,然后这个连接会由 HTTP 协议转换为 WebSocket 协议,这样客户端和服务端建立了全双工通信,通过 WebSocket 的 send 方法和 onmessage 事件就可以通过这条通信连接交换信息。


6. WebSocket示例

1. 首先创建maven工程

【目录结构如图】

WebSocketChatDemo├── pom.xml├── src│   └── main│       ├── java│       │   └── top.iqqcode│	             └── WebSocketTest.java│       └── webapp│           ├── index.html│           └── WEB-INF

2. pom依赖

	<dependency><groupId>javax</groupId><artifactId>javaee-api</artifactId><version>7.0</version><scope>provided</scope></dependency><!-- https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket --><dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.5.1</version></dependency>

3. 服务端代码-WebSocketTest.java

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;/*** @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端*/
@ServerEndpoint("/websocket")
public class WebSocketTest {// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。private static AtomicInteger onlineCount = new AtomicInteger();// (群聊) Concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。// (私聊) 若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识private static CopyOnWriteArraySet<WebSocketTest> webSocketSet = new CopyOnWriteArraySet<WebSocketTest>();// 与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 连接建立成功调用的方法** @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据*/@OnOpenpublic void onOpen(Session session) {this.session = session;webSocketSet.add(this);     //加入set中addOnlineCount();           //在线数加1System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());}/*** 收到客户端消息后调用的方法** @param message 客户端发送过来的消息* @param session 可选的参数*/@OnMessagepublic void onMessage(String message, Session session) {System.out.println("来自客户端的消息:" + message);//群发消息for (WebSocketTest item : webSocketSet) {try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();continue;}}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {webSocketSet.remove(this);  //从set中删除subOnlineCount();           //在线数减1System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());}/*** 发生错误时调用** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("发生错误");error.printStackTrace();}/*** 发送信息方法* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法** @param message* @throws IOException*/public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);//this.session.getAsyncRemote().sendText(message);}/*** (线程安全) 获取在线人数* @return*/public static synchronized AtomicInteger getOnlineCount() {return onlineCount;}// (线程安全) 在线人数 +1public static synchronized void addOnlineCount() {WebSocketTest.onlineCount.incrementAndGet();}// (线程安全) 在线人数 -1public static synchronized void subOnlineCount() {WebSocketTest.onlineCount.decrementAndGet();}
}

4. 页面及客户端代码

<!DOCTYPE html>
<html>
<head><title>ChatDemo</title>      <meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'/><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
Welcome<br/><input id="text" type="text"/>
<button onclick="send()">发送消息</button>
<hr/>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<hr/>
<div id="message"></div>
</body><!--客户端代码-->    
<script type="text/javascript">var websocket = null;//判断当前浏览器是否支持WebSocketif ('WebSocket' in window) {websocket = new WebSocket("ws://localhost:8080/websocket");} else {alert('当前浏览器 Not support websocket')}//连接发生错误的回调方法websocket.onerror = function () {setMessageInnerHTML("WebSocket连接发生错误");};//连接成功建立的回调方法websocket.onopen = function () {setMessageInnerHTML("WebSocket连接成功");}//接收到消息的回调方法websocket.onmessage = function (event) {setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose = function () {setMessageInnerHTML("WebSocket连接关闭");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function () {closeWebSocket();}//将消息显示在网页上function setMessageInnerHTML(innerHTML) {document.getElementById('message').innerHTML += innerHTML + '<br/>';}//关闭WebSocket连接function closeWebSocket() {websocket.close();}//发送消息function send() {var message = document.getElementById('text').value;websocket.send(message);}
</script>
</html>

5. 测试

双方互相发送消息~

image-20201211090322335

后台日志打印当前聊天人数:


7. WebSocket介绍和Socket的区别

以下两个标题段为转载内容,原文查看👉一缕殇流化隐半边冰霜.《谈谈 Websocket》https://halfrost.com/ios_weixin_qq_websocket/

Socket 其实并不是一个协议。它工作在 OSI 模型会话层(第5层),是为了方便大家直接使用更底层协议(一般是 TCP 或 UDP )而存在的一个抽象层。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。

在这里插入图片描述

当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依靠于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。Socket是传输控制层协议,WebSocket是应用层协议。

Socket通常也称作 套接字,用于描述IP地址和端口,是一个通信链的句柄。网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket,一个Socket由一个IP地址和一个端口号唯一确定。应用程序通常通过”套接字”向网络发出请求或者应答网络请求。

Socket在通讯过程中,服务端监听某个端口是否有连接请求,客户端向服务端发送连接请求,服务端收到连接请求向客户端发出接收消息,这样一个连接就建立起来了。客户端和服务端也都可以相互发送消息与对方进行通讯,直到双方连接断开。

所以基于WebSocket和基于Socket都可以开发出IM社交聊天类的即时通讯app


8. WebSocket的使用场景

1.社交聊天

最著名的就是微信,QQ,这一类社交聊天的app。这一类聊天app的特点是低延迟,高即时。即时是这里面要求最高的,如果有一个紧急的事情,通过IM软件通知你,假设网络环境良好的情况下,这条message还无法立即送达到你的客户端上,紧急的事情都结束了,你才收到消息,那么这个软件肯定是失败的。

2.弹幕

说到这里,大家一定里面想到了A站和B站了。确实,他们的弹幕一直是一种特色。而且弹幕对于一个视频来说,很可能弹幕才是精华。发弹幕需要实时显示,也需要和聊天一样,需要即时。

3.多玩家游戏

4.协同编辑

现在很多开源项目都是分散在世界各地的开发者一起协同开发,此时就会用到版本控制系统,比如Git,SVN去合并冲突。但是如果有一份文档,支持多人实时在线协同编辑,那么此时就会用到比如WebSocket了,它可以保证各个编辑者都在编辑同一个文档,此时不需要用到Git,SVN这些版本控制,因为在协同编辑界面就会实时看到对方编辑了什么,谁在修改哪些段落和文字。

5.股票基金实时报价

金融界瞬息万变——几乎是每毫秒都在变化。如果采用的网络架构无法满足实时性,那么就会给客户带来巨大的损失。几毫秒钱股票开始大跌,几秒以后才刷新数据,一秒钟的时间内,很可能用户就已经损失巨大财产了。

6.体育实况更新

全世界的球迷,体育爱好者特别多,当然大家在关心自己喜欢的体育活动的时候,比赛实时的赛况是他们最最关心的事情。这类新闻中最好的体验就是利用Websocket达到实时的更新!

7.视频会议/聊天

视频会议并不能代替和真人相见,但是他能让分布在全球天涯海角的人聚在电脑前一起开会。既能节省大家聚在一起路上花费的时间,讨论聚会地点的纠结,还能随时随地,只要有网络就可以开会。

8.基于位置的应用

越来越多的开发者借用移动设备的GPS功能来实现他们基于位置的网络应用。如果你一直记录用户的位置(比如运行应用来记录运动轨迹),你可以收集到更加细致化的数据。

9.在线教育

在线教育近几年也发展迅速。优点很多,免去了场地的限制,能让名师的资源合理的分配给全国各地想要学习知识的同学手上,Websocket是个不错的选择,可以视频聊天、即时聊天以及其与别人合作一起在网上讨论问题…

10.智能家居

这也是我一毕业加入的一个伟大的物联网智能家居的公司。考虑到家里的智能设备的状态必须需要实时的展现在手机app客户端上,毫无疑问选择了Websocket。

从上面我列举的这些场景来看,一个共同点就是,高实时性!


【参考资料】

[1] http://websocket.org/

[2] HyBi Working Group.The WebSocket protocol.https://tools.ietf.org/html/draft-abarth-thewebsocketprotocol-00

[3] wiki.websocket.https://en.wikipedia.org/wiki/WebSocket#:~:text=WebSocket
[4] Google.WebRTC.https://webrtc.org/

[5] IBM Developer.使用 HTML5 WebSocket 构建实时 Web 应用.https://developer.ibm.com/zh/articles/1112-huangxa-websocket/

[6] spring.Using WebSocket to build an interactive web application.https://spring.io/guides/gs/messaging-stomp-websocket/

[7] 阮一峰的网络日志.WebSocket.http://www.ruanyifeng.com/blog/2017/05/websocket.html

[8] 菜鸟教程.HTML5 WebSocket.https://www.runoob.com/html/html5-websocket.html

[9] 一缕殇流化隐半边冰霜.全双工通信的 WebSocket.https://halfrost.com/websocket/

[10] 小豆丁个人博客.SpringBoot实现Websocket通信详解.http://www.mydlq.club/article/86/

这篇关于【万字长文和Demo搞懂】全双工通讯的WebSocket协议的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/836742

相关文章

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP        我们在网络的应用层中可以自己定义协议,但是,已经有大佬定义了一些现成的,非常好用的应用层协议,供我们直接使用,HTTP(超文本传输协议)就是其中之一。        在互联网世界中,HTTP(超文本传输协议)是一个至关重要的协议,他定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或者传输超文本(比如HTML文档)。

Java Websocket实例【服务端与客户端实现全双工通讯】

Java Websocket实例【服务端与客户端实现全双工通讯】 现很多网站为了实现即时通讯,所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发 出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request 的模式带来很明显的缺点 – 浏 览器需要不断的向服务器发出请求,然而HTTP

【Go】go连接clickhouse使用TCP协议

离开你是傻是对是错 是看破是软弱 这结果是爱是恨或者是什么 如果是种解脱 怎么会还有眷恋在我心窝 那么爱你为什么                      🎵 黄品源/莫文蔚《那么爱你为什么》 package mainimport ("context""fmt""log""time""github.com/ClickHouse/clickhouse-go/v2")func main(

2024.9.8 TCP/IP协议学习笔记

1.所谓的层就是数据交换的深度,电脑点对点就是单层,物理层,加上集线器还是物理层,加上交换机就变成链路层了,有地址表,路由器就到了第三层网络层,每个端口都有一个mac地址 2.A 给 C 发数据包,怎么知道是否要通过路由器转发呢?答案:子网 3.将源 IP 与目的 IP 分别同这个子网掩码进行与运算****,相等则是在一个子网,不相等就是在不同子网 4.A 如何知道,哪个设备是路由器?答案:在 A

Modbus-RTU协议

一、协议概述 Modbus-RTU(Remote Terminal Unit)是一种基于主从架构的通信协议,采用二进制数据表示,消息中的每个8位字节含有两个4位十六进制字符。它主要通过RS-485、RS-232、RS-422等物理接口实现数据的传输,传输距离远、抗干扰能力强、通信效率高。 二、报文结构 一个标准的Modbus-RTU报文通常包含以下部分: 地址域:单个字节,表示从站设备

CSP-J基础之数学基础 初等数论 一篇搞懂(一)

文章目录 前言声明初等数论是什么初等数论历史1. **古代时期**2. **中世纪时期**3. **文艺复兴与近代**4. **现代时期** 整数的整除性约数什么样的整数除什么样的整数才能得到整数?条件:举例说明:一般化: 判断两个数能否被整除 因数与倍数质数与复合数使用开根号法判定质数哥德巴赫猜想最大公因数与辗转相除法计算最大公因数的常用方法:举几个例子:例子 1: 计算 12 和 18

网络原理之TCP协议(万字详解!!!)

目录 前言 TCP协议段格式 TCP协议相关特性 1.确认应答 2.超时重传 3.连接管理(三次握手、四次挥手) 三次握手(建立TCP连接) 四次挥手(断开连接)  4.滑动窗口 5.流量控制 6.拥塞控制 7.延迟应答 8.捎带应答  9.基于字节流 10.异常情况的处理 小结  前言 在前面,我们已经讲解了有关UDP协议的相关知识,但是在传输层,还有

linux 内核提权总结(demo+exp分析) -- 任意读写(四)

hijack_modprobe_path篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm     原理同hijack_prctl, 当用户执行错误格式的elf文件时内核调用call_usermod

linux 内核提权总结(demo+exp分析) -- 任意读写(三)

hijack_prctl篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm   prctl函数: 用户态函数,可用于定制进程参数,非常适合和内核进行交互 用户态执行prctl函数后触发prctl系统