WebSockets - 快速指南


WebSockets - 概述

从字面上看,握手可以定义为两个人握紧并握手,以象征问候、祝贺、同意或告别。在计算机科学中,握手是确保服务器与其客户端同步的过程。握手是Web Socket协议的基本概念。

下图显示了服务器与各种客户端的握手 -

服务器

Web 套接字 – 定义

Web 套接字被定义为服务器和客户端之间的双向通信,这意味着双方同时进行通信和交换数据。

Web Sockets 的关键点是真正的并发性性能优化,从而产生响应更快、更丰富的 Web 应用程序。

Web Socket 协议描述

该协议从头开始定义了全双工通信。Web 套接字在将桌面丰富的功能引入 Web 浏览器方面向前迈出了一步。它代表了客户端/服务器 Web 技术中期待已久的一次演变。

Web 套接字的主要特点如下:

  • Web 套接字协议正在标准化,这意味着借助该协议,Web 服务器和客户端之间的实时通信成为可能。

  • Web 套接字正在转变为客户端和服务器之间实时通信的跨平台标准。

  • 该标准支持新型应用程序。实时网络应用程序的企业可以借助这项技术来加速。

  • Web Socket 的最大优点是它通过单个 TCP 连接提供双向通信(全双工)。

网址

HTTP 有自己的一组架构,例如 http 和 https。Web 套接字协议在其 URL 模式中也定义了类似的模式。

下图显示了令牌中的 Web Socket URL。

协议

浏览器支持

Web Socket 协议的最新规范被定义为RFC 6455——一个提议的标准。

RFC 6455受多种浏览器支持,例如 Internet Explorer、Mozilla Firefox、Google Chrome、Safari 和 Opera。

WebSockets - 双工通信

在深入了解 Web 套接字的需求之前,有必要了解一下用于服务器和客户端之间的双工通信的现有技术。它们如下 -

  • 轮询
  • 长轮询
  • 流媒体
  • 回发和 AJAX
  • HTML5

轮询

轮询可以定义为一种方法,无论传输中存在什么数据,它都执行周期性请求。周期性请求以同步方式发送。客户端以指定的时间间隔向服务器发出周期性请求。服务器的响应包括可用数据或一些警告消息。

长轮询

长轮询,顾名思义,包括与轮询类似的技术。客户端和服务器保持连接处于活动状态,直到获取某些数据或发生超时。如果由于某种原因连接丢失,客户端可以重新开始并执行顺序请求。

长轮询只不过是轮询过程的性能改进,但持续的请求可能会减慢该过程。

流媒体

它被认为是实时数据传输的最佳选择。服务器保持与客户端的连接打开和活动,直到获取所需的数据为止。在这种情况下,连接被称为无限期打开。流媒体包含 HTTP 标头,这会增加文件大小并增加延迟。这可以被认为是一个主要缺点。

AJAX

AJAX 基于 Javascript 的XmlHttpRequest对象。它是异步 Javascript 和 XML 的缩写形式。XmlHttpRequest对象允许执行 Javascript,而无需重新加载整个网页。AJAX 仅发送和接收网页的一部分。

使用XmlHttpRequest对象进行 AJAX 调用的代码片段如下 -

var xhttp;

if (window.XMLHttpRequest) {
   xhttp = new XMLHttpRequest();
} else {
   // code for IE6, IE5
   xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}

与Web Sockets相比,AJAX的主要缺点是 -

  • 它们发送 HTTP 标头,这使得总大小更大。
  • 通信是半双工的。
  • Web 服务器消耗更多资源。

HTML5

HTML5 是用于开发和设计 Web 应用程序的强大框架。主要支柱包括标记、CSS3Javascript API。

下图显示了 HTML5 组件 -

HTML5

下面给出的代码片段描述了 HTML5 及其文档类型的声明。

<!DOCTYPE html>

为什么我们需要 Web 套接字?

互联网被认为是超文本标记语言(HTML)页面的集合,这些页面相互链接以形成概念性的信息网络。随着时间的推移,静态资源的数量不断增加,项目也越来越丰富,例如图像,并开始成为网络结构的一部分。

服务器技术的进步允许动态服务器页面 - 其内容是基于查询生成的页面。

很快,对更多动态网页的需求导致了动态超文本标记语言 (DHTML) 的出现。这一切都归功于 JavaScript。在接下来的几年里,我们看到了跨框架通信,试图避免页面重新加载,然后在框架内进行HTTP 轮询。

然而,这些解决方案都没有为服务器和客户端之间的实时双向通信提供真正标准化的跨浏览器解决方案。

这就产生了对 Web Sockets 协议的需求。它催生了全双工通信,为所有网络浏览器带来了丰富的桌面功能。

WebSockets - 功能

Web Socket 代表了 Web 通信历史上的一次重大升级。在它出现之前,Web 客户端和服务器之间的所有通信仅依赖于 HTTP。

Web Socket 有助于实现持久全双工连接的动态流。全双工是指两端以相当快的速度进行通信。

它被称为游戏规则改变者,因为它能够有效地克服现有协议的所有缺点。

面向开发人员和架构师的 Web Socket

Web Socket 对于开发人员和架构师的重要性 -

  • Web Socket 是一个独立的基于 TCP 的协议,但它旨在支持传统上仅在纯 TCP 连接之上运行的任何其他协议。

  • Web Socket 是一个传输层,任何其他协议都可以在其上运行。Web Socket API 支持定义子协议的能力:可以解释特定协议的协议库。

  • 此类协议的示例包括 XMPP、STOMP 和 AMQP。开发人员不再需要考虑 HTTP 请求-响应范例。

  • 浏览器端的唯一要求是运行一个可以解释 Web Socket 握手、建立和维护 Web Socket 连接的 JavaScript 库。

  • 在服务器端,行业标准是使用在 TCP 之上运行的现有协议库并利用 Web Socket 网关。

下图描述了 Web Sockets 的功能 -

网络

Web Socket 连接通过 HTTP 发起;HTTP 服务器通常将 Web Socket 握手解释为升级请求。

Web Sockets 既可以作为现有 HTTP 环境的补充附加组件,也可以提供添加 Web 功能所需的基础设施。它依赖于更先进的全双工协议,允许数据在客户端和服务器之间双向流动。

Web Socket 的功能

Web 套接字提供 Web 服务器和客户端之间的连接,以便双方都可以开始发送数据。

建立 Web Socket 连接的步骤如下 -

  • 客户端通过称为 Web Socket 握手的过程建立连接。

  • 该过程从客户端向服务器发送常规 HTTP 请求开始。

  • 请求升级标头。在这个请求中,它通知服务器该请求是针对Web Socket连接的。

  • Web Socket URL 使用ws方案。它们还用于安全 Web Socket 连接,相当于 HTTP。

初始请求标头的一个简单示例如下 -

GET ws://websocket.example.com/ HTTP/1.1
Origin: http://example.com
Connection: Upgrade
Host: websocket.example.com
Upgrade: websocket

WebSockets - 实施

Web Sockets 不仅在网络中发挥着关键作用,而且在移动行业中也发挥着关键作用。下面给出了 Web Sockets 的重要性。

  • Web Sockets 顾名思义,与网络相关。Web 由一系列针对某些浏览器的技术组成;它是一个适用于大量设备的广泛通信平台,包括台式电脑、笔记本电脑、平板电脑和智能手机。

  • 利用 Web Sockets 的 HTML5 应用程序可在任何支持 HTML5 的 Web 浏览器上运行。

  • 主流操作系统都支持Web Socket。移动行业的所有主要参与者都在自己的本机应用程序中提供 Web Socket API。

  • Web 套接字据说是全双工通信。Web Sockets 方法非常适合某些类别的 Web 应用程序,例如聊天室,其中客户端和服务器的更新是同时共享的。

网络套接字

Web Sockets 是 HTML5 规范的一部分,允许网页和远程主机之间进行全双工通信。该协议旨在实现以下好处,可以将其视为关键点 -

  • 通过单个连接(而不是两个)使用全双工来减少不必要的网络流量和延迟。

  • 通过代理和防火墙进行流式传输,同时支持上下游通信。

WebSockets - 事件和操作

为了实现客户端之间的通信,需要初始化客户端到服务器的连接。为了初始化连接,需要使用远程或本地服务器的 URL 创建 Javascript 对象。

var socket = new WebSocket(“ ws://echo.websocket.org ”);

上面提到的URL是一个公共地址,可以用于测试和实验。websocket.org 服务器在收到消息并将其发送回客户端时始终处于运行状态。

这是确保应用程序正常运行的最重要的步骤。

Web Sockets – 事件

有四个主要的 Web Socket API事件-

  • 打开
  • 信息
  • 关闭
  • 错误

每个事件都是通过分别实现onopen、onmessageoncloseonerror 等函数来处理的。也可以借助addEventListener方法来实现。

事件和功能的简要概述如下 -

打开

一旦客户端和服务器之间建立了连接,Web Socket 实例就会触发 open 事件。这被称为客户端和服务器之间的初始握手。连接建立后引发的事件称为onopen

信息

消息事件通常在服务器发送一些数据时发生。服务器发送到客户端的消息可以包括纯文本消息、二进制数据或图像。每当发送数据时,就会触发onmessage函数。

关闭

Close事件标志着服务器和客户端之间的通信结束。借助onclose事件可以关闭连接。通过onclose事件标记通信结束后,服务器和客户端之间将无法再传输任何消息。由于连接不良也可能导致活动结束。

错误

错误标记是在通信过程中发生的某些错误。它是借助onerror事件进行标记的。Onerror后始终会终止连接。每个事件的详细描述将在后续章节中讨论。

Web 套接字 – 操作

事件通常在某些事情发生时被触发。另一方面,当用户希望发生某些事情时,就会采取行动。操作是通过用户使用函数的显式调用来执行的。

Web Socket 协议支持两个主要操作,即 -

  • 发送( )
  • 关闭( )

发送 ( )

对于与服务器的某些通信,通常首选此操作,其中包括发送消息,其中包括文本文件、二进制数据或图像。

借助 send() 操作发送的聊天消息如下 -

// get text view and button for submitting the message
var textsend = document.getElementById(“text-view”);
var submitMsg = document.getElementById(“tsend-button”);

//Handling the click event
submitMsg.onclick = function ( ) {
   // Send the data
   socket.send( textsend.value);
}

注意- 仅当连接打开时才可以发送消息。

关闭 ( )

此方法代表告别握手。它会完全终止连接,并且在重新建立连接之前无法传输任何数据。

var textsend = document.getElementById(“text-view”);
var buttonStop = document.getElementById(“stop-button”);

//Handling the click event
buttonStop.onclick = function ( ) {
   // Close the connection if open
   if (socket.readyState === WebSocket.OPEN){
      socket.close( );
   }
}

还可以借助以下代码片段故意关闭连接 -

socket.close(1000,”Deliberate Connection”);

WebSockets - 打开连接

一旦客户端和服务器之间建立了连接,Web Socket 实例就会触发 open 事件。这被称为客户端和服务器之间的初始握手。

连接建立后引发的事件称为onopen。创建 Web Socket 连接非常简单。您所要做的就是调用WebSocket 构造函数并传入服务器的 URL。

以下代码用于创建 Web Socket 连接 -

// Create a new WebSocket.
var socket = new WebSocket('ws://echo.websocket.org');

建立连接后,将在您的 Web Socket 实例上触发 open 事件。

onopen是指客户端和服务器之间的初始握手,这导致了第一次交易,并且 Web 应用程序准备好传输数据。

以下代码片段描述了打开 Web Socket 协议的连接 -

socket.onopen = function(event) {
   console.log(“Connection established”);
   // Display user friendly messages for the successful establishment of connection
   var.label = document.getElementById(“status”);
   label.innerHTML = ”Connection established”;
}

向等待建立 Web Socket 连接的用户提供适当的反馈是一个很好的做法。然而,人们总是注意到 Web Socket 连接相对较快。

所建立的 Web Socket 连接的演示记录在给定的 URL 中 - https://www.websocket.org/echo.html

连接建立和对用户的响应的快照如下所示 -

快照

建立开放状态允许全双工通信和消息传输,直到连接终止。

例子

构建客户端 HTML5 文件。

<!DOCTYPE html>
<html>
   <meta charset = "utf-8" />
   <title>WebSocket Test</title>

   <script language = "javascript" type = "text/javascript">
      var wsUri = "ws://echo.websocket.org/";
      var output;
	
      function init() {
         output = document.getElementById("output");
         testWebSocket();
      }
	
      function testWebSocket() {
         websocket = new WebSocket(wsUri);
			
         websocket.onopen = function(evt) {
            onOpen(evt)
         };
      }
	
      function onOpen(evt) {
         writeToScreen("CONNECTED");
      }
	
      window.addEventListener("load", init, false);
   
   </script>

   <h2>WebSocket Test</h2>
   <div id = "output"></div>

</html>

输出如下 -

连接的

上面的 HTML5 和 JavaScript 文件显示了 Web Socket 的两个事件的实现,即 -

  • onLoad有助于创建 JavaScript 对象和初始化连接。

  • onOpen与服务器建立连接并发送状态。

WebSockets - 处理错误

一旦客户端和服务器之间建立了连接,Web Socket 实例就会触发一个open事件。错误是由于通信过程中发生的错误而产生的。它是借助onerror事件进行标记的。Onerror后始终会终止连接。

当通信之间发生错误时会触发onerror事件。onerror事件之后是连接终止,这是一个关闭事件。

一个好的做法是始终通知用户意外错误并尝试重新连接它们。

socket.onclose = function(event) {
   console.log("Error occurred.");
	
   // Inform the user about the error.
   var label = document.getElementById("status-label");
   label.innerHTML = "Error: " + event;
}

当涉及到错误处理时,您必须考虑内部和外部参数。

  • 内部参数包括由于代码中的错误或意外的用户Behave而可能生成的错误。

  • 外部错误与应用程序无关;相反,它们与无法控制的参数有关。最重要的一项是网络连接。

  • 任何交互式双向 Web 应用程序都需要有效的 Internet 连接。

检查网络可用性

想象一下,您的用户正在享受您的网络应用程序,但网络连接在他们执行任务的过程中突然变得无响应。在现代本机桌面和移动应用程序中,检查网络可用性是一项常见任务。

最常见的方法是简单地向应该启动的网站(例如,http://www.google.com)发出 HTTP 请求。如果请求成功,桌面或移动设备就知道存在活动连接。类似地,HTML 有XMLHttpRequest来确定网络可用性。

然而,HTML5 让它变得更加容易,并引入了一种检查浏览器是否可以接受 Web 响应的方法。这是通过导航器对象实现的 -

if (navigator.onLine) {
   alert("You are Online");
}else {
   alert("You are Offline");
}

离线模式意味着设备未连接或用户已从浏览器工具栏选择离线模式。

以下是如何在发生 WebSocket 关闭事件时通知用户网络不可用并尝试重新连接 -

socket.onclose = function (event) {
   // Connection closed.
   // Firstly, check the reason.
	
   if (event.code != 1000) {
      // Error code 1000 means that the connection was closed normally.
      // Try to reconnect.
		
      if (!navigator.onLine) {
         alert("You are offline. Please connect to the Internet and try again.");
      }
   }
}

接收错误消息的演示

以下程序解释了如何使用 Web Sockets 显示错误消息 -

<!DOCTYPE html>
<html>
   <meta charset = "utf-8" />
   <title>WebSocket Test</title>

   <script language = "javascript" type = "text/javascript">
      var wsUri = "ws://echo.websocket.org/";
      var output;
		
      function init() {
         output = document.getElementById("output");
         testWebSocket();
      }
		
      function testWebSocket() {
         websocket = new WebSocket(wsUri);
			
         websocket.onopen = function(evt) {
            onOpen(evt)
         };
			
         websocket.onclose = function(evt) {
            onClose(evt)
         };
			
         websocket.onerror = function(evt) {
            onError(evt)
         };
      }
		
      function onOpen(evt) {
         writeToScreen("CONNECTED");
         doSend("WebSocket rocks");
      }
		
      function onClose(evt) {
         writeToScreen("DISCONNECTED");
      }
		
      function onError(evt) {
         writeToScreen('<span style = "color: red;">ERROR:</span> ' + evt.data);
      } 
		
      function doSend(message) {
         writeToScreen("SENT: " + message); websocket.send(message);
      }
		
      function writeToScreen(message) {
         var pre = document.createElement("p"); 
         pre.style.wordWrap = "break-word"; 
         pre.innerHTML = message; output.appendChild(pre);
      }
		
      window.addEventListener("load", init, false);
   </script>
	
   <h2>WebSocket Test</h2>
   <div id = "output"></div>
	
</html>

输出如下 -

已断开连接

WebSockets - 发送和接收消息

Message事件通常在服务器发送一些数据发生。服务器发送到客户端的消息可以包括纯文本消息、二进制数据或图像。每当发送数据时,就会触发onmessage函数。

该事件充当服务器的客户端耳朵。每当服务器发送数据时,onmessage事件就会被触发。

下面的代码片段描述了打开Web Socket协议的连接。

connection.onmessage = function(e){
   var server_message = e.data;
   console.log(server_message);
}

还需要考虑可以借助 Web Sockets 传输哪些类型的数据。Web 套接字协议支持文本和二进制数据。就 Javascript 而言,文本指的是字符串,而二进制数据则表示为ArrayBuffer

Web 套接字一次仅支持一种二进制格式。二进制数据的声明明确如下 -

socket.binaryType = ”arrayBuffer”;
socket.binaryType = ”blob”;

弦乐

字符串被认为是有用的,可以处理人类可读的格式,例如 XML 和 JSON。每当引发onmessage事件时,客户端都需要检查数据类型并采取相应的操作。

用于确定数据类型为 String 的代码片段如下 -

socket.onmessage = function(event){

   if(typeOf event.data === String ) {
      console.log(“Received data string”);
   }
}

JSON(JavaScript 对象表示法)

它是一种轻量级格式,用于在计算机之间传输人类可读的数据。JSON 的结构由键值对组成。

例子

{
   name: “James Devilson”,
   message: “Hello World!”
}

以下代码显示了如何处理 JSON 对象并提取其属性 -

socket.onmessage = function(event) {
   if(typeOf event.data === String ){
      //create a JSON object
      var jsonObject = JSON.parse(event.data);
      var username = jsonObject.name;
      var message = jsonObject.message;
		
      console.log(“Received data string”);
   }
}

XML

尽管技术因浏览器而异,但 XML 解析并不困难。最好的方法是使用第三方库(如 jQuery)进行解析。

在 XML 和 JSON 中,服务器以字符串形式响应,并在客户端进行解析。

数组缓冲区

它由结构化的二进制数据组成。所附位按顺序给出,以便可以轻松跟踪位置。ArrayBuffers 可以方便地存储图像文件。

使用 ArrayBuffers 接收数据相当简单。使用运算符instanceOf代替等于运算符。

以下代码显示了如何处理和接收 ArrayBuffer 对象 -

socket.onmessage = function(event) {
   if(event.data instanceof ArrayBuffer ){
      var buffer = event.data;
      console.log(“Received arraybuffer”);
   }
}

演示应用程序

以下程序代码显示了如何使用 Web Sockets 发送和接收消息。

<!DOCTYPE html>
<html>
   <meta charset = "utf-8" />
   <title>WebSocket Test</title>

   <script language = "javascript" type = "text/javascript">
      var wsUri = "ws://echo.websocket.org/";
      var output;
		
      function init() {
         output = document.getElementById("output");
         testWebSocket();
      }
		
      function testWebSocket() {
         websocket = new WebSocket(wsUri);
			
         websocket.onopen = function(evt) {
            onOpen(evt)
         };
		
         websocket.onmessage = function(evt) {
            onMessage(evt)
         };
		
         websocket.onerror = function(evt) {
            onError(evt)
         };
      }
		
      function onOpen(evt) {
         writeToScreen("CONNECTED");
         doSend("WebSocket rocks");
      }
		
      function onMessage(evt) {
         writeToScreen('<span style = "color: blue;">RESPONSE: ' +
            evt.data+'</span>'); websocket.close();
      }

      function onError(evt) {
         writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
      }
		
      function doSend(message) {
         writeToScreen("SENT: " + message); websocket.send(message);
      }
		
      function writeToScreen(message) {
         var pre = document.createElement("p"); 
         pre.style.wordWrap = "break-word"; 
         pre.innerHTML = message; output.appendChild(pre);
      }
		
      window.addEventListener("load", init, false);
		
   </script>
	
   <h2>WebSocket Test</h2>
   <div id = "output"></div> 
	
</html>

输出如下所示。

WebSocket 岩石

WebSockets - 关闭连接

Close事件标志着服务器和客户端之间通信的结束。借助onclose事件可以关闭连接。通过onclose事件标记通信结束后,服务器和客户端之间将无法再传输任何消息。由于连接不良也可能导致事件关闭。

close ()方法代表告别握手。它终止连接,除非连接再次打开,否则无法交换数据。

与前面的示例类似,当用户单击第二个按钮时,我们调用close()方法。

var textView = document.getElementById("text-view");
var buttonStop = document.getElementById("stop-button");

buttonStop.onclick = function() {
   // Close the connection, if open.
   if (socket.readyState === WebSocket.OPEN) {
      socket.close();
   }
}

也可以传递我们之前提到的代码和原因参数,如下所示。

socket.close(1000, "Deliberate disconnection");

以下代码完整概述了如何关闭或断开 Web Socket 连接 -

<!DOCTYPE html>
<html>
   <meta charset = "utf-8" />
   <title>WebSocket Test</title>

   <script language = "javascript" type = "text/javascript">
      var wsUri = "ws://echo.websocket.org/";
      var output;
	
      function init() {
         output = document.getElementById("output");
         testWebSocket();
      }
	
      function testWebSocket() {
         websocket = new WebSocket(wsUri);
		
         websocket.onopen = function(evt) {
            onOpen(evt)
         };
		
         websocket.onclose = function(evt) {
            onClose(evt)
         };
		
         websocket.onmessage = function(evt) {
            onMessage(evt)
         };
		
         websocket.onerror = function(evt) {
            onError(evt)
         };
      }
	
      function onOpen(evt) {
         writeToScreen("CONNECTED");
         doSend("WebSocket rocks");
      }
	
      function onClose(evt) {
         writeToScreen("DISCONNECTED");
      }
	
      function onMessage(evt) {
         writeToScreen('<span style = "color: blue;">RESPONSE: ' + 
            evt.data+'</span>'); websocket.close();
      }
	
      function onError(evt) {
         writeToScreen('<span style = "color: red;">ERROR:</span> '
            + evt.data);
      } 
	
      function doSend(message) {
         writeToScreen("SENT: " + message); websocket.send(message);
      }
	
      function writeToScreen(message) {
         var pre = document.createElement("p"); 
         pre.style.wordWrap = "break-word"; 
         pre.innerHTML = message; 
         output.appendChild(pre);
      }
	
      window.addEventListener("load", init, false);
   </script>
	
   <h2>WebSocket Test</h2>
   <div id = "output"></div>
	
</html>

输出如下 -

WebSocket 已断开

WebSockets - 工作服务器

Web Socket 服务器是一个简单的程序,它能够处理 Web Socket 事件和操作。它通常向 Web Socket 客户端 API 公开类似的方法,并且大多数编程语言都提供实现。下图说明了 Web Socket 服务器和 Web Socket 客户端之间的通信过程,重点介绍了触发的事件和操作。

下图显示了 Web Socket 服务器和客户端事件触发 -

服务器客户端

连接到网络服务器

Web Socket 服务器的工作方式与 Web Socket 客户端类似。它响应事件并在必要时执行操作。无论使用哪种编程语言,每个 Web Socket 服务器都会执行一些特定的操作。

它被初始化为 Web Socket 地址。它处理OnOpen、OnCloseOnMessage事件,并向客户端发送消息。

创建 Web Socket 服务器实例

每个 Web Socket 服务器都需要有效的主机和端口。在服务器中创建 Web Socket 实例的示例如下 -

var server = new WebSocketServer("ws://localhost:8181");

任何有效的 URL 都可以与之前未使用过的端口规范一起使用。保留已连接客户端的记录非常有用,因为它提供了不同数据的详细信息或向每个客户端发送不同的消息。

Fleck 表示具有IwebSocketConnection接口的传入连接(客户端)。每当有人连接或断开我们的服务时,就可以创建或更新空列表。

var clients = new List<IWebSocketConnection>();

之后,我们可以调用Start方法并等待客户端连接。启动后,服务器能够接受传入连接。在 Fleck 中,Start 方法需要一个参数,该参数指示引发事件的套接字 -

server.Start(socket) =>
{
});

打开事件

OnOpen事件确定新客户端已请求访问并执行初始握手应将客户端添加到列表中,并且可能应存储与其相关的信息,例如 IP 地址。Fleck 为我们提供了此类信息以及连接的唯一标识符。

server.Start(socket) ⇒ {

   socket.OnOpen = () ⇒ {
      // Add the incoming connection to our list.
      clients.Add(socket);
   }
	
   // Handle the other events here...
});

关闭事件

每当客户端断开连接时都会引发OnClose事件。该客户端将从列表中删除,并通知其余客户端有关断开连接的信息。

socket.OnClose = () ⇒ {
   // Remove the disconnected client from the list.
   clients.Remove(socket);
};

消息事件

当客户端向服务器发送数据时,将引发OnMessage事件。在这个事件处理程序中,传入的消息可以传输到客户端,或者可能只选择其中的一些。

过程很简单。请注意,此处理程序采用名为message 的字符串作为参数 -

socket.OnMessage = () ⇒ {
   // Display the message on the console.
   Console.WriteLine(message);
};

发送()方法

Send ()方法只是将所需的消息传输到指定的客户端。使用 Send(),可以跨客户端存储文本或二进制数据。

OnMessage事件的工作原理如下 -

socket.OnMessage = () ⇒ {
   foreach (var client in clients) {
      // Send the message to everyone!
      // Also, send the client connection's unique identifier in order
      // to recognize who is who.
      client.Send(client.ConnectionInfo.Id + " says: " + message);
   }
};

WebSockets-API

API – 定义

API是Application Program Interface的缩写,是一组用于构建软件应用程序的例程、协议和工具。

一些重要的功能是 -

  • API 指定软件组件应如何交互以及在对图形用户界面 (GUI) 组件进行编程时应使用 API。

  • 良好的 API 可以通过提供所有构建块来更轻松地开发程序。

  • REST 通常通过 HTTP 运行,常用于移动应用程序、社交网站、混搭工具和自动化业务流程。

  • REST 风格强调通过有限数量的操作(动词)来增强客户端和服务之间的交互。

  • 通过分配资源提供灵活性;他们自己独特的统一资源标识符(URI)。

  • REST 避免了歧义,因为每个动词都有特定的含义(GET、POST、PUT 和 DELETE)

Web Socket 的优点

Web Socket 解决了 REST 或 HTTP 的一些问题 -

双向

HTTP 是一种单向协议,始终由客户端发起请求。服务器处理并返回响应,然后客户端使用它。Web Socket 是一种双向协议,没有预定义的消息模式(例如请求/响应)。客户端或服务器都可以向对方发送消息。

全双工

HTTP 允许请求消息从客户端发送到服务器,然后服务器向客户端发送响应消息。在给定时间,要么客户端正在与服务器通信,要么服务器正在与客户端通信。Web Socket 允许客户端和服务器彼此独立地进行通信。

单个 TCP 连接

通常,新的 TCP 连接是针对 HTTP 请求发起的,并在收到响应后终止。需要为另一个 HTTP 请求/响应建立新的 TCP 连接。对于 Web Socket,HTTP 连接使用标准 HTTP 升级机制进行升级,并且客户端和服务器在 Web Socket 连接的生命周期内通过同一 TCP 连接进行通信。

下图显示了在恒定负载大小下处理 N 条消息所需的时间(以毫秒为单位)。

单连接

这是提供该图的原始数据 -

恒定有效载荷

上面给出的图表显示,REST 开销随着消息数量的增加而增加。这是事实,因为需要启动和终止许多 TCP 连接,并且需要发送和接收许多 HTTP 标头。

最后一列特别显示了满足 REST 请求的时间量的倍增因子。

第二张图显示了通过改变负载大小来处理固定数量的消息所花费的时间。

Websocket 休息

这是提供该图的原始数据 -

常数

该图显示,处理 REST 端点的请求/响应的增量成本很小,大部分时间都花在连接启动/终止和遵守 HTTP 语义上。

结论

Web Socket 是一种低级协议。一切,包括简单的请求/响应设计模式、如何创建/更新/删除资源需求、状态代码等都构建在其之上。所有这些都针对 HTTP 进行了明确定义。

Web Socket 是有状态协议,而 HTTP 是无状态协议。Web Socket 连接可以在单个服务器上垂直扩展,而 HTTP 可以水平扩展。有一些用于 Web Socket 水平扩展的专有解决方案,但它们并不基于标准。HTTP 还具有许多其他优点,例如缓存、路由和多路复用。所有这些都需要在 Web Socket 之上定义。

WebSockets - JavaScript 应用程序

以下程序代码描述了使用 JavaScript 和 Web Socket 协议的聊天应用程序的工作原理。

<!DOCTYPE html>
<html lang = "en">

   <head>
      <meta charset = utf-8>
      <title>HTML5 Chat</title>
		
      <body>
		
         <section id = "wrapper">
			
            <header>
               <h1>HTML5 Chat</h1>
            </header>
				
            <style>
               #chat { width: 97%; }
               .message { font-weight: bold; }
               .message:before { content: ' '; color: #bbb; font-size: 14px; }
					
               #log {
                  overflow: auto;
                  max-height: 300px;
                  list-style: none;
                  padding: 0;
               }
					
               #log li {
                  border-top: 1px solid #ccc;
                  margin: 0;
                  padding: 10px 0;
               }
					
               body {
                  font: normal 16px/20px "Helvetica Neue", Helvetica, sans-serif;
                  background: rgb(237, 237, 236);
                  margin: 0;
                  margin-top: 40px;
                  padding: 0;
               }
					
               section, header {
                  display: block;
               }
					
               #wrapper {
                  width: 600px;
                  margin: 0 auto;
                  background: #fff;
                  border-radius: 10px;
                  border-top: 1px solid #fff;
                  padding-bottom: 16px;
               }
					
               h1 {
                  padding-top: 10px;
               }
					
               h2 {
                  font-size: 100%;
                  font-style: italic;
               }
					
               header, article > * {
                  margin: 20px;
               }
					
               #status {
                  padding: 5px;
                  color: #fff;
                  background: #ccc;
               }
					
               #status.fail {
                  background: #c00;
               }
					
               #status.success {
                  background: #0c0;
               }
					
               #status.offline {
                  background: #c00;
               }
					
               #status.online {
                  background: #0c0;
               }
					
               #html5badge {
                  margin-left: -30px;
                  border: 0;
               }
					
               #html5badge img {
                  border: 0;
               }
            </style>
				
            <article>
				
               <form onsubmit = "addMessage(); return false;">
                  <input type = "text" id = "chat" placeholder = "type and press 
                  enter to chat" />
               </form>
					
               <p id = "status">Not connected</p>
               <p>Users connected: <span id = "connected">0
                  </span></p>
               <ul id = "log"></ul>
					
            </article>
				
            <script>
               connected = document.getElementById("connected");
               log = document.getElementById("log");
               chat = document.getElementById("chat");
               form = chat.form;
               state = document.getElementById("status");
					
               if (window.WebSocket === undefined) {
                  state.innerHTML = "sockets not supported";
                  state.className = "fail";
               }else {
                  if (typeof String.prototype.startsWith != "function") {
                     String.prototype.startsWith = function (str) {
                        return this.indexOf(str) == 0;
                     };
                  }
						
                  window.addEventListener("load", onLoad, false);
               }
					
               function onLoad() {
                  var wsUri = "ws://127.0.0.1:7777";
                  websocket = new WebSocket(wsUri);
                  websocket.onopen = function(evt) { onOpen(evt) };
                  websocket.onclose = function(evt) { onClose(evt) };
                  websocket.onmessage = function(evt) { onMessage(evt) };
                  websocket.onerror = function(evt) { onError(evt) };
               }
					
               function onOpen(evt) {
                  state.className = "success";
                  state.innerHTML = "Connected to server";
               }
					
               function onClose(evt) {
                  state.className = "fail";
                  state.innerHTML = "Not connected";
                  connected.innerHTML = "0";
               }
					
               function onMessage(evt) {
                  // There are two types of messages:
                  // 1. a chat participant message itself
                  // 2. a message with a number of connected chat participants
                  var message = evt.data;
						
                  if (message.startsWith("log:")) {
                     message = message.slice("log:".length);
                     log.innerHTML = '<li class = "message">' + 
                        message + "</li>" + log.innerHTML;
                  }else if (message.startsWith("connected:")) {
                     message = message.slice("connected:".length);
                     connected.innerHTML = message;
                  }
               }
					
               function onError(evt) {
                  state.className = "fail";
                  state.innerHTML = "Communication error";
               }
					
               function addMessage() {
                  var message = chat.value;
                  chat.value = "";
                  websocket.send(message);
               }
					
            </script>
				
         </section>
			
      </body>
		
   </head>	
	
</html>

下面讨论聊天应用程序的主要功能和输出 -

要进行测试,请打开两个支持 Web Socket 的窗口,在上面键入消息并按回车键。这将启用聊天应用程序的功能。

如果未建立连接,则输出如下所示。

HTML5 聊天

成功聊天通信的输出如下所示。

浏览器支持

WebSockets - 与服务器通信

Web 主要是围绕 HTTP 的请求/响应范例构建的。客户端加载一个网页,然后什么也不会发生,直到用户单击下一页。2005 年左右,AJAX 开始让 Web 感觉更加动态。尽管如此,所有 HTTP 通信均由客户端控制,这需要用户交互或定期轮询以从服务器加载新数据。

使服务器能够在知道有新数据可用时立即将数据发送到客户端的技术已经存在相当长一段时间了。它们的名称包括“Push”“Comet”

通过长轮询,客户端会打开一个到服务器的 HTTP 连接,该连接将保持打开状态直到发送响应。每当服务器实际有新数据时,它就会发送响应。长轮询和其他技术效果很好。然而,所有这些都有一个问题,即它们带有 HTTP 开销,这并不使它们非常适合低延迟应用程序。例如,浏览器中的多人射击游戏或任何其他具有实时组件的在线游戏。

将套接字引入网络

Web Socket 规范定义了在 Web 浏览器和服务器之间建立“套接字”连接的 API。通俗地说,客户端和服务器之间存在持久连接,双方可以随时开始发送数据。

可以使用构造函数简单地打开 Web 套接字连接 -

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

ws是 WebSocket 连接的新 URL 架构。还有wss,用于安全 WebSocket 连接,就像https用于安全 HTTP 连接一样。

将一些事件处理程序立即附加到连接可以让您知道连接何时打开、何时收到传入消息或是否出现错误。

第二个参数接受可选的子协议。它可以是一个字符串或字符串数​​组。每个字符串应代表一个子协议名称,服务器仅接受数组中传递的子协议之一。可以通过访问 WebSocket 对象的协议属性来确定接受的子协议。

// When the connection is open, send some data to the server
connection.onopen = function () {
   connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
   console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
   console.log('Server: ' + e.data);
};

与服务器通信

一旦我们与服务器建立了连接(当 open 事件被触发时),我们就可以开始使用连接对象上的 send(您的消息)方法向服务器发送数据。它过去只支持字符串,但在最新的规范中,它现在也可以发送二进制消息。要发送二进制数据,请使用 Blob 或 ArrayBuffer 对象。

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);

for (var i = 0; i < img.data.length; i++) {
   binary[i] = img.data[i];
}

connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type = "file"]').files[0];
connection.send(file);

同样,服务器可能随时向我们发送消息。每当发生这种情况时,就会触发 onmessage 回调。回调接收一个事件对象,并且可以通过data属性访问实际消息。

在最新规范中,WebSocket 还可以接收二进制消息。可以以 Blob 或 ArrayBuffer 格式接收二进制帧。要指定接收到的二进制文件的格式,请将 WebSocket 对象的 binaryType 属性设置为“blob”或“arraybuffer”。默认格式是“blob”。

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
   console.log(e.data.byteLength); // ArrayBuffer object if binary
};

WebSocket 的另一个新添加的功能是扩展。使用扩展,可以发送压缩、复用等帧。

// Determining accepted extensions
console.log(connection.extensions);

跨源通信

作为一种现代协议,跨域通信被直接嵌入到 WebSocket 中。WebSocket 支持任何域中各方之间的通信。服务器决定是向所有客户端提供其服务,还是仅向驻留在一组明确定义的域中的客户端提供服务。

代理服务器

每项新技术都会带来一系列新问题。就 WebSocket 而言,它与代理服务器兼容,代理服务器在大多数公司网络中调解 HTTP 连接。WebSocket 协议使用 HTTP 升级系统(通常用于 HTTP/SSL)将 HTTP 连接“升级”为 WebSocket 连接。有些代理服务器不喜欢这样,会断开连接。因此,即使给定客户端使用 WebSocket 协议,也可能无法建立连接。这使得下一节变得更加重要:)

服务器端

使用 WebSocket 为服务器端应用程序创建了一种全新的使用模式。虽然 LAMP 等传统服务器堆栈是围绕 HTTP 请求/响应周期设计的,但它们通常不能很好地处理大量打开的 WebSocket 连接。同时保持大量连接打开需要一个以低性能成本获得高并发的架构。

WebSockets - 安全

协议的设计应出于安全考虑。WebSocket 是一个全新的协议,并非所有 Web 浏览器都能正确实现它。例如,其中一些仍然允许混合 HTTP 和 WS,尽管规范暗示相反。在本章中,我们将讨论用户应该注意的一些常见安全攻击。

拒绝服务

拒绝服务 (DoS) 攻击试图使请求的用户无法使用计算机或网络资源。假设有人向 Web 服务器发出无限数量的请求,且没有时间间隔或时间间隔很小。服务器无法处理每个连接,并且会停止响应或响应速度太慢。这可以称为拒绝服务攻击。

拒绝服务对于最终用户来说非常令人沮丧,他们甚至无法加载网页。

DoS 攻击甚至可以应用于点对点通信,迫使 P2P 网络的客户端同时连接到受害 Web 服务器。

中间人

让我们通过一个例子来理解这一点。

假设一个人A通过 IM 客户端与他的朋友B聊天。有些第三者想要查看您交换的消息。因此,他与这两个人建立了独立的联系。他还向人A和他的朋友B发送消息,作为你们沟通的隐形中间人。这称为中间人攻击。

对于未加密的连接,中间人攻击更容易,因为入侵者可以直接读取包。当连接被加密时,信息必须由攻击者解密,这可能太困难了。

从技术角度来看,攻击者拦截公钥消息交换并发送消息,同时用自己的密钥替换所请求的密钥。显然,使攻击者的工作变得困难的一个可靠策略是将 SSH 与 WebSocket 结合使用。

大多数情况下,在交换关键数据时,更喜欢使用 WSS 安全连接,而不是未加密的 WS。

跨站脚本攻击

跨站脚本 (XSS) 是一种漏洞,攻击者可利用该漏洞将客户端脚本注入网页或应用程序中。攻击者可以使用您的应用程序中心发送 HTML 或 Javascript 代码,并让该代码在客户端计算机上执行。

WebSocket 本机防御机制

默认情况下,WebSocket 协议被设计为安全的。在现实世界中,用户可能会遇到由于浏览器实现不佳而可能出现的各种问题。随着时间的推移,浏览器供应商会立即修复任何问题。

当使用通过 SSH(或 TLS)的安全 WebSocket 连接时,会添加额外的安全层。

在 WebSocket 世界中,主要关注的是安全连接的性能。虽然上面仍然有一个额外的 TLS 层,但协议本身包含针对此类使用的优化,此外,WSS 通过代理可以更加流畅地工作。

客户端到服务器屏蔽

WebSocket 服务器和 WebSocket 客户端之间传输的每条消息都包含一个特定的密钥,称为屏蔽密钥,它允许任何符合 WebSocket 的中介机构取消屏蔽并检查消息。如果中介不符合 WebSocket 标准,则消息不会受到影响。实现 WebSocket 协议的浏览器处理屏蔽。

安全工具箱

最后,可以提供有用的工具来调查 WebSocket 客户端和服务器之间的信息流、分析交换的数据并识别可能的风险。

浏览器开发者工具

Chrome、Firefox 和 Opera 在开发人员支持方面都是出色的浏览器。他们的内置工具帮助我们确定客户端交互和资源的几乎所有方面。它对于安全目的发挥着巨大的作用。

WebSockets - 移动应用程序

WebSocket,顾名思义,就是使用网络的东西。网络通常与浏览器页面交织在一起,因为这是在线显示数据的主要方式。然而,非浏览器程序也使用在线数据传输。

iPhone(最初)和 iPad(后来)的发布引入了一个全新的网络互连世界,而无需使用网络浏览器。相反,新的智能手机和平板电脑设备利用本机应用程序的强大功能来提供独特的用户体验。

为什么移动很重要?

目前,活跃的智能手机数量已达 10 亿部。也就是说,您的应用程序有数百万潜在客户。这些人使用手机完成日常任务、上网、交流或购物。

智能手机已成为应用程序的代名词。如今,用户可以想到任何用途的应用程序。大多数应用程序连接到互联网以检索数据、进行交易、收集新闻等。

如果能够使用现有的 WebSocket 知识并开发一个在智能手机或平板设备上本地运行的 WebSocket 客户端,那就太好了。

本机移动应用程序与移动网站

嗯,这是一个常见的冲突,像往常一样,答案取决于目标受众的需求。如果用户熟悉现代设计趋势,那么现在必须设计一个响应灵敏且适合移动设备的网站。然而,最终用户必须确保真正重要的内容可以通过智能手机访问,就像通过经典桌面浏览器访问一样。

当然,WebSocket Web 应用程序可以在任何兼容 HTML5 的浏览器上运行,包括移动浏览器,例如 iOS 版 Safari 和移动版 Chrome。因此,不用担心与智能手机的兼容性问题。

先决条件

为了开发智能手机应用程序,需要安装开发工具和SDK。

手机

WebSocket 可以充当通用集线器,用于在连接的移动和平板电脑客户端之间传输消息。我们可以实现一个本机 iOS 应用程序,它与 WebSocket 服务器通信,就像 HTML5 JavaScript 客户端一样。