HTML5 WebSocket 详解及使用
<h2>1.WebSocket 是什么?</h2><p> WebSocket 是 HTML5 提供的一种在单个 TCP 连接上进行全双工通讯的协议。(双向通信协议)</p>
<h2>2.WebSocket 的作用?</h2>
<p> <span style="color: rgba(0, 0, 255, 1)"><strong>实现客户端与服务器之间的双向通信,允许服务端主动向客户端推送数据。</strong></span></p>
<p><span style="color: rgba(0, 0, 255, 1)"><strong> 在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。</strong></span></p>
<h2>3.WebSocket 和 HTTP 区别?</h2>
<p style="margin-left: 30px">WebSocket 与 HTTP的关系图:</p>
<p style="margin-left: 30px"><img src="https://img2024.cnblogs.com/blog/2022570/202408/2022570-20240806162648510-210701079.png"></p>
<p> </p>
<p style="margin-left: 30px">相同点:</p>
<p style="margin-left: 30px"> 1. WebSocket 都是一样基于 TCP 的可靠性传输协议,都是应用层协议</p>
<p style="margin-left: 30px">异同点:</p>
<p style="margin-left: 60px">1. WebSocket 可以双向发送或接受信息,而 HTTP 是单向的(HTTP 通信只能由客户端发起,不具备服务器主动推送能力);</p>
<p style="margin-left: 60px">2. WebSocket 的使用,需要先进行一次 客户端与服务器的握手,两者建立连接后才可以正常双向通信,而 HTTP 是一个 主动的 Request 对应一个 被动的Response;</p>
<h2>4.WebSocket 的协议标识符?</h2>
<p style="margin-left: 30px">如果服务器网址是 HTTP 那么 WebSocket 对应的是<span style="color: rgba(0, 0, 255, 1)"><strong> ws</strong></span></p>
<p style="margin-left: 30px">如果服务器网址是 HTTPS 加密的 那么 WebSocket 对应的是<strong><span style="color: rgba(0, 0, 255, 1)"> wss</span></strong></p>
<h2>5.WebSocket 的作用总结?</h2>
<p style="margin-left: 30px">WebSocket 是为了能够实现在 web 应用上与服务器进行<span style="color: rgba(0, 0, 255, 1)"><strong>双向通信</strong></span>的需求 而产生出来的协议,</p>
<p style="margin-left: 30px">相比于轮询 HTTP 请求的方式,WebSocket<strong><span style="color: rgba(0, 0, 255, 1)"> 节省了服务器资源</span></strong>,有效的提高了效率。</p>
<h2>6.HTML5 WebSocket的使用方式描述</h2>
<p> 通过JavaScript中的WebSocket对象来创建WebSocket连接,并通过send()方法向服务器发送数据,通过onmessage()方法接收服务器返回的数据。</p>
<p> 目前,WebSocket协议已经广泛应用于实时聊天、在线游戏、语音视频聊天、股票行情等领域。</p>
<h2>7.WebSocket协议的特点</h2>
<p>最大特点:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。</p>
<p>其他特点:</p>
<p style="margin-left: 30px">(1)实时性:与传统的 HTTP 请求/响应模式不同,WebSocket 允许实时双向通信,使得服务器能够主动向客户端推送数据,而不需要客户端发起请求。</p>
<p style="margin-left: 30px">(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。</p>
<p style="margin-left: 30px">(3)服务器压力减少:WebSocket 连接保持打开状态,因此服务器不需要为每个请求创建一个新的连接。这可以减轻服务器的负载并提高性能。</p>
<p style="margin-left: 30px">(4)可以发送文本,也可以发送二进制数据。</p>
<p style="margin-left: 30px">(5)没有同源限制,客户端可以与任意服务器通信。</p>
<p style="margin-left: 30px">(6)协议标识符是<code>ws</code>(如果加密,则为<code>wss</code>),服务器网址就是 URL。</p>
<p style="margin-left: 30px">(7)只需要经过一次 HTTP 请求,就可以一直传送消息(也称为回调)。</p>
<h2>8.WebSocket 工作原理</h2>
<p>WebSocket 的工作原理可以分为三个阶段:握手、数据传输和断开连接。</p>
<ul>
<li>握手:客户端发起 WebSocket 连接时,通过向服务器发送一个特殊的 HTTP 请求头来建立连接。服务器检查请求头中的特定字段,确认支持 WebSocket 协议后,发送特殊的 HTTP 响应头进行握手确认。握手成功后,双方建立了 WebSocket 连接,可以进行后续的数据传输。</li>
<li>数据传输:一旦建立了 WebSocket 连接,客户端和服务器可以通过该连接进行双向的实时数据传输。双方可以发送和接收消息,消息以帧的形式进行传输。WebSocket 协议定义了不同类型的帧,如文本帧和二进制帧,用于传输不同类型的数据。</li>
<li>断开连接:当连接不再需要时,客户端或服务器可以发起关闭连接的请求。双方会交换特殊的关闭帧,以协商关闭连接,并确保双方都接收到了关闭请求。</li>
</ul>
<h2>9.WebSocket连接的过程</h2>
<div>
<div>
<p style="margin-left: 30px">首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;</p>
<p style="margin-left: 30px">然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;</p>
<p style="margin-left: 30px">最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信</p>
</div>
<h2>10、HTML5 WebSocket 需要后端配合吗</h2>
<p>是的,</p>
<p>HTML5 WebSocket 需要后端配合。具体来说,WebSocket 需要在后端实现一个 WebSocket 服务器,该服务器能够接收 WebSocket 请求,并在客户端和服务器之间建立 WebSocket 连接。通常情况下,后端可以使用一些 WebSocket 服务器库(如 Node.js 的 Socket.IO 或 Java 的 Java-WebSocket)来实现 WebSocket 服务器。在客户端,HTML5 提供了 WebSocket API,让客户端能够与服务器建立 WebSocket 连接、发送和接收 WebSocket 消息。因此,HTML5 WebSocket 需要客户端和服务器的配合。</p>
</div>
<p>以上原理相关内容结束,以下就是实际使用相关的内容</p>
<h2><strong>11.客户端API</strong></h2>
<table border="0">
<tbody>
<tr>
<td>
<h3>WebSocket 构造函数</h3>
</td>
<td>
<pre class=" language-javascript"><code class=" language-javascript"><span class="token keyword">新建 WebSocket 实例:<br><br>var ws <span class="token operator">= <span class="token keyword">new <span class="token class-name">WebSocket<span class="token punctuation">(<span class="token string">'ws://localhost:8080'<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></code></pre>
</td>
<td>执行上面语句之后,客户端就会与服务器进行连接。</td>
<td> </td>
</tr>
<tr>
<td>Socket.readyState</td>
<td>获取实例的当前链接状态</td>
<td>
<ul>
<li>CONNECTING:值为0,表示正在连接。</li>
<li>OPEN:值为1,表示连接成功,可以通信了。</li>
<li>CLOSING:值为2,表示连接正在关闭。</li>
<li>CLOSED:值为3,表示连接已经关闭,或者打开连接失败。</li>
</ul>
</td>
<td>
<p>示例:</p>
<pre class=" language-javascript"><code class=" language-javascript">switch <span class="token punctuation">(ws<span class="token punctuation">.readyState<span class="token punctuation">) <span class="token punctuation">{
case WebSocket<span class="token punctuation">.CONNECTING<span class="token punctuation">:
<span class="token comment"> // do something
<span class="token keyword">break<span class="token punctuation">;
case WebSocket<span class="token punctuation">.OPEN<span class="token punctuation">:
<span class="token comment"> // do something
<span class="token keyword">break<span class="token punctuation">;
case WebSocket<span class="token punctuation">.CLOSING<span class="token punctuation">:
<span class="token comment"> // do something
<span class="token keyword">break<span class="token punctuation">;
case WebSocket<span class="token punctuation">.CLOSED<span class="token punctuation">:
<span class="token comment"> // do something
<span class="token keyword">break<span class="token punctuation">;
default<span class="token punctuation">:
<span class="token comment"> // this never happens
<span class="token keyword">break<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</td>
</tr>
<tr>
<td>
<h3>webSocket.onopen</h3>
</td>
<td>
<p> 用于指定连接成功后的回调函数</p>
<pre class=" language-javascript"></pre>
</td>
<td>
<pre class=" language-javascript"><code class=" language-javascript">ws<span class="token punctuation">.onopen <span class="token operator">= <span class="token keyword">function <span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
ws<span class="token punctuation">.<span class="token function">send<span class="token punctuation">(<span class="token string">'Hello Server!'<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>如果要指定多个回调函数,可以使用<code>addEventListener</code>方法。</p>
<pre class=" language-javascript"><code class=" language-javascript">ws<span class="token punctuation">.<span class="token function">addEventListener<span class="token punctuation">(<span class="token string">'open'<span class="token punctuation">, <span class="token keyword">function <span class="token punctuation">(event<span class="token punctuation">) <span class="token punctuation">{
ws<span class="token punctuation">.<span class="token function">send<span class="token punctuation">(<span class="token string">'Hello Server!'<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</td>
<td> </td>
</tr>
<tr>
<td>
<h3>webSocket.onclose</h3>
</td>
<td> 用于指定连接关闭后的回调函数</td>
<td>
<pre class=" language-javascript"><code class=" language-javascript">ws<span class="token punctuation">.onclose <span class="token operator">= <span class="token keyword">function<span class="token punctuation">(event<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">var code <span class="token operator">= event<span class="token punctuation">.code<span class="token punctuation">;
<span class="token keyword">var reason <span class="token operator">= event<span class="token punctuation">.reason<span class="token punctuation">;
<span class="token keyword">var wasClean <span class="token operator">= event<span class="token punctuation">.wasClean<span class="token punctuation">;
<span class="token comment"> // handle close event
<span class="token punctuation">}<span class="token punctuation">;
ws<span class="token punctuation">.<span class="token function">addEventListener<span class="token punctuation">(<span class="token string">"close"<span class="token punctuation">, <span class="token keyword">function<span class="token punctuation">(event<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">var code <span class="token operator">= event<span class="token punctuation">.code<span class="token punctuation">;
<span class="token keyword">var reason <span class="token operator">= event<span class="token punctuation">.reason<span class="token punctuation">;
<span class="token keyword">var wasClean <span class="token operator">= event<span class="token punctuation">.wasClean<span class="token punctuation">;
<span class="token comment"> // handle close event
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</td>
<td> </td>
</tr>
<tr>
<td>
<h3>webSocket.onmessage</h3>
</td>
<td>用于指定收到服务器数据后的回调函数</td>
<td>
<blockquote>
<pre class=" language-javascript"><code class=" language-javascript">ws<span class="token punctuation">.onmessage <span class="token operator">= <span class="token keyword">function<span class="token punctuation">(event<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">var data <span class="token operator">= event<span class="token punctuation">.data<span class="token punctuation">;
<span class="token comment"> // 处理数据
<span class="token punctuation">}<span class="token punctuation">;
ws<span class="token punctuation">.<span class="token function">addEventListener<span class="token punctuation">(<span class="token string">"message"<span class="token punctuation">, <span class="token keyword">function<span class="token punctuation">(event<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">var data <span class="token operator">= event<span class="token punctuation">.data<span class="token punctuation">;
<span class="token comment"> // 处理数据
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</blockquote>
<p>注意,服务器数据可能是文本,也可能是二进制数据(<code>blob</code>对象或<code>Arraybuffer</code>对象)。</p>
<blockquote>
<pre class=" language-javascript"><code class=" language-javascript">
ws<span class="token punctuation">.onmessage <span class="token operator">= <span class="token keyword">function<span class="token punctuation">(event<span class="token punctuation">)<span class="token punctuation">{
<span class="token keyword">if<span class="token punctuation">(<span class="token keyword">typeof event<span class="token punctuation">.data <span class="token operator">=== String<span class="token punctuation">) <span class="token punctuation">{
console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">"Received data string"<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">if<span class="token punctuation">(event<span class="token punctuation">.data <span class="token keyword">instanceof <span class="token class-name">ArrayBuffer<span class="token punctuation">)<span class="token punctuation">{
<span class="token keyword">var buffer <span class="token operator">= event<span class="token punctuation">.data<span class="token punctuation">;
console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">"Received arraybuffer"<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</blockquote>
<p>除了动态判断收到的数据类型,也可以使用<code>binaryType</code>属性,显式指定收到的二进制数据类型。</p>
<blockquote>
<pre class=" language-javascript"><code class=" language-javascript"><span class="token comment">
// 收到的是 blob 数据
ws<span class="token punctuation">.binaryType <span class="token operator">= <span class="token string">"blob"<span class="token punctuation">;
ws<span class="token punctuation">.onmessage <span class="token operator">= <span class="token keyword">function<span class="token punctuation">(e<span class="token punctuation">) <span class="token punctuation">{
console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(e<span class="token punctuation">.data<span class="token punctuation">.size<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">;
<span class="token comment">
// 收到的是 ArrayBuffer 数据
ws<span class="token punctuation">.binaryType <span class="token operator">= <span class="token string">"arraybuffer"<span class="token punctuation">;
ws<span class="token punctuation">.onmessage <span class="token operator">= <span class="token keyword">function<span class="token punctuation">(e<span class="token punctuation">) <span class="token punctuation">{
console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(e<span class="token punctuation">.data<span class="token punctuation">.byteLength<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</blockquote>
</td>
<td> </td>
</tr>
<tr>
<td>
<h3>webSocket.send()</h3>
</td>
<td> 向服务器发送数据</td>
<td>
<p>发送文本的例子。</p>
<pre class=" language-javascript"><code class=" language-javascript">ws<span class="token punctuation">.<span class="token function">send<span class="token punctuation">(<span class="token string">'your message'<span class="token punctuation">)<span class="token punctuation">;
</span></span></span></span></span></span></code></pre>
<p>发送 Blob 对象的例子。</p>
<pre class=" language-javascript"><code class=" language-javascript"><span class="token keyword">var file <span class="token operator">= document
<span class="token punctuation">.<span class="token function">querySelector<span class="token punctuation">(<span class="token string">'input'<span class="token punctuation">)
<span class="token punctuation">.files<span class="token punctuation">[<span class="token number">0<span class="token punctuation">]<span class="token punctuation">;
ws<span class="token punctuation">.<span class="token function">send<span class="token punctuation">(file<span class="token punctuation">)<span class="token punctuation">;
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>发送 ArrayBuffer 对象的例子。</p>
<pre class=" language-javascript"><code class=" language-javascript"><span class="token comment">// Sending canvas ImageData as ArrayBuffer
<span class="token keyword">var img <span class="token operator">= canvas_context<span class="token punctuation">.<span class="token function">getImageData<span class="token punctuation">(<span class="token number">0<span class="token punctuation">, <span class="token number">0<span class="token punctuation">, <span class="token number">400<span class="token punctuation">, <span class="token number">320<span class="token punctuation">)<span class="token punctuation">;
<span class="token keyword">var binary <span class="token operator">= <span class="token keyword">new <span class="token class-name">Uint8Array<span class="token punctuation">(img<span class="token punctuation">.data<span class="token punctuation">.length<span class="token punctuation">)<span class="token punctuation">;
<span class="token keyword">for <span class="token punctuation">(<span class="token keyword">var i <span class="token operator">= <span class="token number">0<span class="token punctuation">; i <span class="token operator">< img<span class="token punctuation">.data<span class="token punctuation">.length<span class="token punctuation">; i<span class="token operator">++<span class="token punctuation">) <span class="token punctuation">{
binary<span class="token punctuation"> <span class="token operator">= img<span class="token punctuation">.data<span class="token punctuation"><span class="token punctuation">;
<span class="token punctuation">}
ws<span class="token punctuation">.<span class="token function">send<span class="token punctuation">(binary<span class="token punctuation">.buffer<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</td>
<td> </td>
</tr>
<tr>
<td>
<h3>webSocket.bufferedAmount</h3>
</td>
<td> 表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。</td>
<td>
<pre class=" language-javascript"><code class=" language-javascript"><span class="token keyword">var data <span class="token operator">= <span class="token keyword">new <span class="token class-name">ArrayBuffer<span class="token punctuation">(<span class="token number">10000000<span class="token punctuation">)<span class="token punctuation">;
socket<span class="token punctuation">.<span class="token function">send<span class="token punctuation">(data<span class="token punctuation">)<span class="token punctuation">;
<span class="token keyword">if <span class="token punctuation">(socket<span class="token punctuation">.bufferedAmount <span class="token operator">=== <span class="token number">0<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment"> // 发送完毕
<span class="token punctuation">} <span class="token keyword">else <span class="token punctuation">{
<span class="token comment"> // 发送还没结束
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</td>
<td> </td>
</tr>
<tr>
<td>
<h3>webSocket.onerror</h3>
</td>
<td> 用于指定报错时的回调函数</td>
<td>
<pre class=" language-javascript"><code class=" language-javascript">socket<span class="token punctuation">.onerror <span class="token operator">= <span class="token keyword">function<span class="token punctuation">(event<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment"> // handle error event
<span class="token punctuation">}<span class="token punctuation">;
socket<span class="token punctuation">.<span class="token function">addEventListener<span class="token punctuation">(<span class="token string">"error"<span class="token punctuation">, <span class="token keyword">function<span class="token punctuation">(event<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment"> // handle error event
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</td>
<td> </td>
</tr>
</tbody>
</table>
<h2><strong>代码实例:</strong></h2>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"><!</span><span style="color: rgba(255, 0, 255, 1)">DOCTYPE HTML</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">html</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">head</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">meta </span><span style="color: rgba(255, 0, 0, 1)">charset</span><span style="color: rgba(0, 0, 255, 1)">="utf-8"</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">title</span><span style="color: rgba(0, 0, 255, 1)">></span>菜鸟教程(runoob.com)<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">title</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">script </span><span style="color: rgba(255, 0, 0, 1)">type</span><span style="color: rgba(0, 0, 255, 1)">="text/javascript"</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> WebSocketTest()
{
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">if</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">WebSocket</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">in</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> window)
{
alert(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">您的浏览器支持 WebSocket!</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 打开一个 web socket</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">var</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> ws </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">new</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> WebSocket(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">ws://localhost:9998/echo</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
ws.onopen </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">()
{
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> Web Socket 已连接上,使用 send() 方法发送数据</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> ws.send(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">发送数据</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
alert(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">数据发送中...</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
};
ws.onmessage </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> (evt)
{
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">var</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> received_msg </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> evt.data;
alert(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">数据已接收...</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
};
ws.onclose </span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">=</span> <span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">function</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">()
{
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 关闭 websocket</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> alert(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">连接已关闭...</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
};
}
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 255, 1)">else</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">
{
</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)">//</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 128, 0, 1)"> 浏览器不支持 WebSocket</span>
<span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)"> alert(</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">您的浏览器不支持 WebSocket!</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">"</span><span style="background-color: rgba(245, 245, 245, 1); color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">script</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">head</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">body</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">div </span><span style="color: rgba(255, 0, 0, 1)">id</span><span style="color: rgba(0, 0, 255, 1)">="sse"</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">a </span><span style="color: rgba(255, 0, 0, 1)">href</span><span style="color: rgba(0, 0, 255, 1)">="javascript:WebSocketTest()"</span><span style="color: rgba(0, 0, 255, 1)">></span>运行 WebSocket<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">a</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">div</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">body</span><span style="color: rgba(0, 0, 255, 1)">></span>
<span style="color: rgba(0, 0, 255, 1)"></</span><span style="color: rgba(128, 0, 0, 1)">html</span><span style="color: rgba(0, 0, 255, 1)">></span></pre>
</div>
<p> </p>
<p> </p>
<h2><strong>12.服务端的实现</strong></h2>
<p>常用的 Node 实现有以下三种。</p>
<ul>
<li>µWebSockets</li>
<li>Socket.IO</li>
<li>WebSocket-Node</li>
</ul>
<h2>13.应用场景</h2>
<p>基于<code>websocket</code>的事实通信的特点,其存在的应用场景大概有:</p>
<div>
<ul>
<li>即时聊天通信</li>
<li>多玩家游戏</li>
<li>在线协同编辑/编辑</li>
<li>实时数据流的拉取与推送</li>
<li>体育/游戏实况</li>
<li>实时地图位置</li>
<li>即时<code>Web</code>应用程序:即时<code>Web</code>应用程序使用一个<code>Web</code>套接字在客户端显示数据,这些数据由后端服务器连续发送。在<code>WebSocke</code>t中,数据被连续推送/传输到已经打开的同一连接中,这就是为什么<code>WebSocket</code>更快并提高了应用程序性能的原因。 例如在交易网站或比特币交易中,这是最不稳定的事情,它用于显示价格波动,数据被后端服务器使用Web套接字通道连续推送到客户端。</li>
<li>游戏应用程序:在游戏应用程序中,你可能会注意到,服务器会持续接收数据,而不会刷新用户界面。屏幕上的用户界面会自动刷新,而且不需要建立新的连接,因此在<code>WebSocket</code>游戏应用程序中非常有帮助。</li>
<li>聊天应用程序:聊天应用程序仅使用<code>WebSocket</code>建立一次连接,便能在订阅户之间交换,发布和广播消息。它重复使用相同的<code>WebSocket</code>连接,用于发送和接收消息以及一对一的消息传输。</li>
</ul>
</div>
<div>
<div>
<h3 data-id="heading-7">不能使用WebSocket的场景</h3>
<p> 如果我们需要通过网络传输的任何实时更新或连续数据流,则可以使用<code>WebSocket</code>。如果我们要获取旧数据,或者只想获取一次数据供应用程序使用,则应该使用<code>HTTP</code>协议,不需要很频繁或仅获取一次的数据可以通过简单的<code>HTTP</code>请求查询,因此在这种情况下最好不要使用<code>WebSocket</code>。</p>
</div>
<h2>14.websocket 断线重连</h2>
<p><span style="font-size: 1.17em">1.如何判断在线离线?</span></p>
<p> 概述:对两次请求的时间差与指定时间进行比较</p>
<div>
<div><ol>
<li>当客户端第一次发送请求至服务端时会携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果不存在就存入db或者缓存中,</li>
<li>第二次客户端定再次发送请求依旧携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果存在就把上次的时间戳拿取出来,使用当前时间戳减去上次的时间,</li>
<li>得出的毫秒秒数判断是否大于指定的时间,若小于的话就是在线,否则就是离线;</li>
</ol></div>
<h3>2.如何解决断线问题</h3>
</div>
<p>通过查阅资料了解到 nginx 代理的 websocket 转发,无消息连接会出现超时断开问题。网上资料提到解决方案两种,一种是修改nginx配置信息,第二种是websocket发送心跳包。</p>
<h3 style="margin-left: 30px">断线和重连 </h3>
<ul>
<li>
<h4>断线的可能原因1:websocket超时没有消息自动断开连接,应对措施:</h4>
</li>
</ul>
<p>这时候我们就需要知道服务端设置的超时时长是多少,在小于超时时间内发送<span style="color: rgba(0, 0, 255, 1)"><strong>心跳包</strong></span>,有两种方案:一种是客户端主动发送上行心跳包,另一种方案是服务端主动发送下行心跳包。</p>
<p>心跳包一般来说都是在逻辑层发送空的echo包来实现的。<span style="color: rgba(0, 0, 255, 1)"><strong><code>下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。</code></strong></span></p>
<div>
<div>
<p>心跳检测步骤:</p>
<ol>
<li>客户端每隔一个时间间隔发生一个探测包给服务器</li>
<li>客户端发包时启动一个超时定时器</li>
<li>服务器端接收到检测包,应该回应一个包</li>
<li>如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器</li>
<li>如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了</li>
</ol></div>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 前端解决方案:心跳检测</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> heartCheck =<span style="color: rgba(0, 0, 0, 1)"> {
timeout: </span>30000, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">30秒发一次心跳</span>
timeoutObj: <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
serverTimeoutObj: </span><span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,
reset: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
clearTimeout(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.timeoutObj);
clearTimeout(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.serverTimeoutObj);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
},
start: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(0, 0, 255, 1)">var</span> self = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.timeoutObj = setTimeout(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里发送一个心跳,后端收到后,返回一个心跳消息,</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">onmessage拿到返回的心跳就说明连接正常</span>
ws.send("ping"<span style="color: rgba(0, 0, 0, 1)">);
console.log(</span>"ping!"<span style="color: rgba(0, 0, 0, 1)">)
self.serverTimeoutObj </span>= setTimeout(<span style="color: rgba(0, 0, 255, 1)">function</span>(){<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果超过一定时间还没重置,说明后端主动断开了</span>
ws.close(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次</span>
<span style="color: rgba(0, 0, 0, 1)"> }, self.timeout);
}, </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.timeout);
}
}</span></pre>
</div>
<ul>
<li>
<h4>断线的可能原因2:websocket异常包括服务端出现中断,交互切屏等等客户端异常中断等等</h4>
</li>
</ul>
<p> 当若服务端宕机了,客户端怎么做?服务端再次上线时怎么做?</p>
<ol>
<li style="margin-left: 60px">客户端则需要断开连接,通过onclose 关闭连接</li>
<li style="margin-left: 60px">服务端再次上线时则需要清除之间存的数据,若不清除 则会造成只要请求到服务端的都会被视为离线。</li>
</ol>
<p style="margin-left: 90px">针对这种异常的中断解决方案就是处理重连,下面我们给出的重连方案是使用js库处理:引入reconnecting-websocket.min.js,ws建立链接方法使用js库api方法:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">var</span> ws = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ReconnectingWebSocket(url);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 断线重连:</span>
<span style="color: rgba(0, 0, 0, 1)">reconnectSocket(){
</span><span style="color: rgba(0, 0, 255, 1)">if</span> ('ws' <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> window) {
ws </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ReconnectingWebSocket(url);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> ('MozWebSocket' <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> window) {
ws </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MozWebSocket(url);
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
ws </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SockJS(url);
}
}</span></pre>
</div>
<p>断网监测支持使用js库:offline.min.js</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">onLineCheck(){
Offline.check();
console.log(Offline.state,</span>'---Offline.state'<span style="color: rgba(0, 0, 0, 1)">);
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span>.socketStatus,'---this.socketStatus'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.socketStatus){
console.log(</span>'网络连接已断开!'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span>(Offline.state === 'up' && websocket.reconnectAttempts ><span style="color: rgba(0, 0, 0, 1)"> websocket.maxReconnectInterval){
window.location.reload();
}
reconnectSocket();
}</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
console.log(</span>'网络连接成功!'<span style="color: rgba(0, 0, 0, 1)">);
websocket.send(</span>"heartBeat"<span style="color: rgba(0, 0, 0, 1)">);
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用:在websocket断开链接时调用网络中断监测</span>
websocket.onclose =><span style="color: rgba(0, 0, 0, 1)"> () {
onLineCheck();
};</span></pre>
</div>
<p> </p>
<blockquote>
<p>补充概念知识:</p>
<p>心跳是什么?</p>
<p>心跳是 websocket 中的一个概念,就是在 websocket 中用定时器,定时向服务器发送一个消息内容,我们约定,如果后端收到这个消息内容,就要向前端回一个消息内容。如果前端没有收到消息回复,则有可能 websocket 断掉了,那么就要进行重连操作,来保证我们的 websocket 处于连接状态。</p>
<div class="cnblogs_code">
<pre>let url = `${process.env['VUE_APP_WEBSOCKET']}/websocket`
let ws = new<span> WebSocket(url)
ws.addEventListener('open', e =><span> {
console.log('长连接连接成功'<span>)
// 执行心跳方法
dispatch('wsHeartStart'<span>)
})</span></span></span></span></pre>
</div>
<p><code>websocket</code> 链接成功以后,开启心跳方法。心跳功能的实现如下:</p>
<div class="cnblogs_code">
<pre>const state =<span> {
ws: null<span>,
// 心跳时间(s)
wsTimeout: 20<span>,
// 等待心跳响应时间(s),等待心跳的响应时间要大于心跳时间5s以上
waitHeartTime: 25<span>
}
// 开启心跳
<span>wsHeartStart({ state, dispatch }) {
timer.wsTimeoutObj = setTimeout(() =><span> {
if(state.ws && state.ws.readyState == 1<span>) {
state.ws.send(''<span>)
} else<span> {
dispatch('handlerWSError'<span>)
}
}, state.wsTimeout * 1000<span>)
timer.serverTimeoutObj = setTimeout(() =><span> {
console.log('接收心跳异常!'<span>)
dispatch('handlerWSError'<span>)
}, state.waitHeartTime * 1000<span>)
}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</div>
<p>上面这个代码片段,描述了心跳的基本意思;就是用定时器去向服务端发送一个空的字符串,我们与后端约定,当后端 <code>ws</code> 收到空字符串的时候,要回一个字符串。如果我们没有收到,则判断为 <code>接收心跳异常</code>,准备重连机制。</p>
</blockquote>
<h3> </h3>
<p> </p>
</div><br><br>
来源:https://www.cnblogs.com/meiyanstar/p/18345121
頁:
[1]