全双工通讯的WebSocket协议
📚 官方文档 The WebSocket protocol (opens new window)
【实时 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
2
在一次请求/响应之后,就会关闭连接。这种方式下,是不适合获取实时信息的,客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。客户端会轮询,有没有新消息。这种方式连接数会很多,一个接受,一个发送。而且每次发送请求都会有 HTTP 的 Header,会很耗流量,也会消耗 CPU 的利用率。
- 优点:短连接,服务器处理简单,支持跨域、浏览器兼容性较好。
- 缺点:有一定延迟、服务器压力较大,浪费带宽流量、大部分是无效请求。
# 1.2 长连接
HTTP1.1支持了并且默认了长连接。因为有了长连接,所以不用定时轮询来房子连接关闭。长轮询是对定时轮询的改进和提高,目地是为了降低无效的网络传输。当服务器端没有数据更新的时候,连接会保持一段时间周期直到数据或状态改变或者时间过期,通过这种机制来减少无效的客户端和服务器间的交互。
客户端打开一个到服务器端的 AJAX 请求,然后等待响应,服务器端需要一些特定的功能来允许请求被挂起,只要一有事件发生,服务器端就会在挂起的请求中送回响应并关闭该请求。客户端在处理完服务器返回的信息后,再次发出请求,重新建立连接,如此循环。
客户端和服务端建立一次连接之后,会复用这条链接,可以在这条连接上进行多次请求/响应
操作。当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。
这种方式也有一定的弊端,实时性不高。如果是高实时的系统,肯定不会采用这种办法。因为一个 GET 请求来回需要 2个 RTT,很可能在这段时间内,数据变化很大,客户端拿到的数据已经延后很多了。
另外,网络带宽低利用率的问题也没有从根源上解决。每个 Request 都会带相同的 Header。
// 只设置Connection:keep-alive,表明连接永久有效
Connection: keep-alive
Keep-Alive: timeout = 60
2
3
4
- 表明连接建立之后,空闲时间超过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协议 (opens new window)
参见官方文档 Relationship to TCP and HTTP (opens new window)
相关问题参见stackoverflow 🔗 WebSockets with UDP (opens new window)
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 (opens new window)
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协议的版本
2
3
4
5
6
7
服务端response响应头:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: ZUip34t+bCjhkvxxwhmdEOyx9hE=
Sec-WebSocket-Extensions: permessage-deflate;
2
3
4
5
【字段说明】
头名称 | 说明 |
---|---|
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 (opens new window) mode. The data is minimally framed, with a small header followed by payload (opens new window).[36] (opens new window) 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] (opens new window)
一旦建立了连接,客户端和服务器可以在全双工模式下来回发送WebSocket数据或文本帧。WebSocket 传输被描述为 "消息",其中单个消息可以选择在多个数据帧中分割,这允许发送初始数据可用但消息的完整长度未知的消息(它发送一个又一个数据帧,直到到达终点并标有 FIN 位)。通过协议的扩展,这还可用于同时多路复用多个流(例如,避免对单个大型有效负载的套接字进行独占使用)。
# 4. 客户端通信
# 4.1 WebSocket对象
创建WebSocket对象:
var ws = new WebSocket(url); //请求的地址
参数url格式:
ws://ip地址:端口号/资源名称
# 4.2 WebSocket事件
WebSocket对象的相关事件:
事件 | 事件处理程序 | 描述 |
---|---|---|
open | websocket对象.onopen | 连接建立时触发 |
message | websocket对象.onmessage | 客户端接收服务端数据时触发 |
error | websocket对象.onerror | 通信发生错误是触发 |
close | websocket对象.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)
2
3
通过上面的介绍,大家应该对 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
3
4
5
6
7
8
9
10
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>
2
3
4
5
6
7
8
9
10
11
12
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为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
//群发消息
for (WebSocketTest item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public 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;
}
// (线程安全) 在线人数 +1
public static synchronized void addOnlineCount() {
WebSocketTest.onlineCount.incrementAndGet();
}
// (线程安全) 在线人数 -1
public static synchronized void subOnlineCount() {
WebSocketTest.onlineCount.decrementAndGet();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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;
//判断当前浏览器是否支持WebSocket
if ('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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
5. 测试
双方互相发送消息~
后台日志打印当前聊天人数:
# 7. WebSocket介绍和Socket的区别
以下两个标题段为转载内容,原文查看👉一缕殇流化隐半边冰霜.《谈谈 Websocket》https://halfrost.com/ios_weixin_qq_websocket/ (opens new window)
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] 一缕殇流化隐半边冰霜 (opens new window).全双工通信的 WebSocket.https://halfrost.com/websocket/
[10] 小豆丁个人博客.SpringBoot实现Websocket通信详解.http://www.mydlq.club/article/86/