不能引入websocket_WebSocket:将桌面敏捷性引入Web应用程序

不能引入websocket

当今的Web应用程序已在我们个人和企业生活的各个方面变得至关重要。 它们涵盖了社交媒体网络,在线购物,业务应用程序以及家用电器配置器。 尽管这种增长Swift,但Web应用程序的用户体验尚未达到本机和桌面应用程序所享有的水平。 这主要归因于Web应用程序依赖单向HTTP协议的方式。 WebSocket改变了这一点–它为浏览器和服务器交互引入了新的基础元素,为创建提供真正交互体验的应用程序创建了急需的基础。

最初,Web技术基于HTTP协议,HTTP协议是一种简单的请求-协议的响应类型。 所有请求均由客户端发出。 在开发人员开始构建客户端发起的通信受到严重限制的Web应用程序之前,该框架就足够了。 提出了几种解决方法,但是它们仍基于HTTP协议,并采用了轮询或长轮询方法(例如Comet)。 Comet释放了请求服务线程,以防止服务器资源耗尽。 由于轮询机制不可靠,因此在2007年提出了一种名为WebSocket的全双工通信。将原始提议变为标准花了4年的时间。 但是,尽管它是一个标准,但仅达到了非常有限的用途。 本文介绍了阻碍WebSocket采用的两个主要原因,并提出了一个开发人员可以用来快速利用WebSocket潜力并显着丰富应用程序经验的设计框架。

缺乏WebSocket采用的第一个原因是对应用程序服务器和浏览器的支持有限。 但是,对于新一代的应用程序服务器和浏览器,此问题已得到明显解决。 第二个也是更重要的原因是,要充分发挥WebSocket的潜力,就需要进行大量的Web应用程序重新设计。 重新设计涉及从请求的基本原语-响应更复杂的双向消息传递原语。 重新设计应用程序通常是一个代价高昂的过程,供应商看不到采用这种方法的明显好处。

我们将从对WebSocket的简短解释开始,然后介绍使用WebSocket重建应用程序的方法,最后给出一个简单的示例来说明要点。

WebSocket简而言之

WebSocket是基于TCP / IP协议的成帧协议。 它是由客户端使用对服务器的特殊HTTP请求启动的,并且在初始握手之后,客户端和服务器可以相互之间自由和异步地发送帧。 框架有两种类型:控制和数据。 最小控制帧大小为2个字节,而客户端的数据帧以6个字节开始,而服务器的数据帧以2个字节开始。 数据框可以是文本或二进制。 文本框架采用UTF-8编码。 帧可以分块; 意味着可以将大数据集分成几帧。 WebSocket不会引入与框架相关的任何标识。 因此,不允许混合不同消息的帧。 大消息的一系列中间帧中只能出现控制帧。 可以在基本框架的顶部定义更复杂的协议。 例如,一帧可以携带校验和或其序列号

WebSocket的API

WebSocket与任何特定的编程语言,系统或操作系统都不相关。 大多数流行的编程语言都可以使用实现,许多浏览器都支持它。 尽管针对不同平台和语言有许多标准,但本文仅关注JavaScript HTML5和Java(J2EE)WebSocket支持。 在浏览器端有两种实施标准,最新的标准是Hixie-76和HyBi-17(后来称为IETF RFC 6455)。 HyBi看起来更高级,当前所有现代浏览器都支持。 基于Java的实现在服务器端最为流行。 WebSocket的一些早期实现后来被转换为JSR356。JSR代表Java Specification Request。 规范要求的引入有助于使任何进一步的实现保持一致且易于使用。 它还消除了开发人员对特定供应商的依赖。 尽管JSR 356允许访问某些servlet对象,但它与servlet规范是分开的。 JSR 356作为客户端涵盖了WebSocket连接的服务器端。 进一步的讨论集中在结合浏览器JavaScript使用服务器端。 JSR356当前是J2EE 7的一部分。所有流行的开源Java应用服务器(例如Tomcat,Jetty,Glassfish和TJWS)都支持它。 大约有20多种针对Java的独立WebSocket服务器解决方案,其中一些也对JSR356友好。 Oracle和IBM的商业应用服务器支持WebSocket作为J2EE 7的一部分。

如我所说,WebSocket是一种消息传递协议。 API提供了在通信的两端发送和接收消息的方法。 没有经典的订户发布者关系。 仅将两种类型的消息视为文本和二进制消息。 但是,在主类型的消息处理程序中,消息的任何逻辑分隔都是可能的。 Java提供了一种处理由部分组成的分块消息的方法。 JavaScript没有提供控制级别。 如前所述,WebSocket是一种非常通用的协议。 可以在握手期间指定所需的逻辑子协议。 当不同的系统可以验证连接的系统的功能以支持某些逻辑子协议和扩展时,它简化了使用WebSocket进行系统集成的过程。 WebSocket框架格式允许在其顶部使用可协商的扩展名。 这意味着通常可以为帧提供更多的信息,并且可以引入更多不同的帧类型。

浏览器JavaScript

因为WebSocket协议握手是客户端发起的,所以JavaScript包含封装所有WebSocket操作的WebSocket接口。

接口是标准的[1],在IDL中定义如下

[Constructor(in DOMString url, in optional DOMString protocols)]
[Constructor(in DOMString url, in optional DOMString[] protocols)]
interface WebSocket {
  readonly attribute DOMString url;

  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSING = 2;
  const unsigned short CLOSED = 3;
  readonly attribute unsigned short readyState;
  readonly attribute unsigned long bufferedAmount;

  // networking
           attribute Function onopen;
           attribute Function onmessage;
           attribute Function onerror;
           attribute Function onclose;
  readonly attribute DOMString protocol;
  void send(in DOMString data);
  void close();
};
WebSocket implements EventTarget;

WebSocket的构造方法WebSocket具有两个参数:

  1. WebSocket URL
  2. 可选的子协议数组或元素

WebSocket URL以“ ws”开头,表示它是WebSocket协议,其余与指定主机,端口,路径和查询的HTTP协议URL相同。 协议名称中添加了额外的“ s”,以指示需要使用安全连接。

可以指定四个消息处理程序:onopen,onmessage,onclose和onerror。 send方法必须用于发送消息,而close方法必须用于关闭连接。 没有像connect这样的方法,因此客户端必须侦听onopen消息以确认已建立连接,只有在此之后才能使用发送操作。 另一个选择是轮询WebSocket对象的readyState属性,但是不建议这样做。 显然,onmessage处理程序中始终允许发送操作。 发送操作是由浏览器异步执行的,这意味着JavaScript可以立即获得控制权,而不必等到实际将消息传递给接收者时再进行控制。 接收文本消息和二进制消息没有区别,因此必须在onmessage处理程序的事件数据参数中检查消息类型。 WebSocket API公开了几个用于获取状态,二进制消息格式和其他目的的属性。 供应商特定的实现可能包含更多属性,因此请查阅您的浏览器文档以获取详细信息。

Java WebSocket

Java JSR356定义了Java WebSocket通信API的公共(客户端)和服务器部分。 Java实现指定端点和服务器端点对象,类似于JavaScript中的WebSocket。 注释用于将某个Java类标记为端点对象。 通过使用注释OnOpen,OnMessage,OnError和OnClose指定事件处理程序。 重要的Session对象可以在所有类型的处理程序中用作参数。 会话可以访问发送消息的功能,还可以保持WebSocket连接关联的状态属性。 使用同步和异步机制可以发送消息。 可以为两种类型的发送配置超时。 通过指定解码器,可以将二进制和文本数据自动转换为任何Java对象。 编码器允许通过WebSocket发送任意Java对象。 在一个WebSocket URL路径的范围内,只能为文本和二进制消息类型指定一个消息处理程序。 尽管仍然可以通过程序进行组织,但没有消息链。 Java API很简单。 它提供了一个配置对象,该对象可以进行自定义并影响初始握手过程,并决定是否支持子协议,版本,并提供对重要Servlet API对象的访问。 除了基于注释的部署外,还可以通过编程方式建立端点。

重新思考Web应用程序

WebSockets对于诸如以下类型的应用程序的开发是很自然的:

  1. 具有实时玩家协作的游戏
  2. 实时监控系统
  3. 需要用户协作(例如聊天,可共享文档编辑等)的系统。

但是,WebSocket可以应用到传统的Web应用程序中,具有一定的优势。

大多数Web应用程序都是根据请求-响应范例设计的。 尽管AJAX允许异步,但是在继续执行下一步之前,仍然有人为地等待响应。 由于WebSocket连接仅建立一次,因此消除了为每个数据交换重新建立连接的需要,并消除了在顺序通信中发送冗余HTTP标头的麻烦。 这些好处对于SSL类型的连接特别重要,因为在这种情况下,初始连接握手是一项昂贵的操作。 浏览器WebSocket发送确实是异步的,并且Java服务器端代码可以发送消息而无需等待请求时。 这种发送消息的自由性可能要求某些簿记组织保持应用程序状态一致。 也可以使用WebSocket模仿请求响应范例。 但是,它可以减少WebSocket作为真正的异步双向消息传递系统的主要好处。 上面所有这些都鼓励开发人员在某些情况下重新考虑应用程序设计。

考虑一个生成具有多个区域的复杂用户界面的应用程序,需要大量的服务器工作来生成其内容。 传统的基于AJAX的实现可以使用惰性机制来渲染发出内容请求调用的区域。 但是,在使用WebSocket的情况下,服务器可以在准备就绪时在浏览器中传递内容,而无需响应特定的AJAX请求。 AJAX请求的缺点是由于浏览器的请求序列化,它们的服务器端处理可能无法达到最佳顺序。 让服务器决定内容计算的最佳方式可以改善Web应用程序的整体响应能力。

有效使用WebSocket所需的注意事项很少。 由于可以随时断开网络连接,从而阻止了数据的传递,因此对于关键数据可能需要进行一些额外的簿记。 通常,每个收到的消息必须提供足够的信息以标识如何处理它。 无法获取请求消息的人的信息:是客户端请求消息还是服务器要更新某些消息。 使用WebSocket时,可以重新考虑Web应用程序的设计。 JavaScript代码功能可以迁移到服务器。 例如,用户输入可以立即发送到服务器进行处理。 当JavaScript能力不足时,它可以帮助实现复杂的数据验证。 此时,用户输入也可以存储在后端。 它可以消除从浏览器向服务器提交的最终数据,并消除额外的数据验证,因为数据已经在存储时进行了验证。 服务器端代码角色的这种增加可以认为是从胖Web客户端到瘦Web客户端的又一次转变。

使用WebSocket应该注意什么

使用WebSocket在Web应用程序开发中带来了特殊的挑战。 WebSocket会话与HTTP会话没有关系,尽管它可以用于类似目的。 某些公共数据可以与会话相关联,因此所有消息处理都可以依赖于会话中维护的某些状态和数据。 WebSocket会话可以超时,就像HTTP会话基于空闲(不活动)间隔一样。 但是,某些系统可以自动继续发送ping控制消息,从而无法明确检测到超时。 JSR356建议将HTTP会话超时与WebSocket会话同步。 如果HTTP会话超时,则还必须关闭在其范围内创建的所有WebSocket连接。 但是,可以在不建立任何HTTP会话的情况下设计Web应用程序,或者可以通过与HTTP会话分离JavaScript来管理会话超时,因此无法可靠地传播此机制。

另一个特定的浏览器可以使用连接池并重用连接来访问相同的网站,从而可以序列化某些处理。 如果浏览器还为WebSocket连接应用了一个池,则可能会受到严重限制,因为如果没有任何关闭WebSocket连接的机制,它将永远保持活动状态,并且其他尝试创建新连接的尝试将被锁定。 因此,最佳实践建议是仅使用一个WebSocket连接。

浏览器无法缓存通过WebSocket传输的数据。 因此,使用WebSocket传输浏览器的可缓存资源不是一种有效的方法。

WebSocket与RESTFul

网上有很多关于RESTful和WebSockets的讨论。[2] 但是,大多数讨论都比较了苹果和橙子。 REST被定义为代表性状态传输,并且在大多数情况下依赖于基础HTTP。 这意味着REST是一种请求-响应协议。 REST从未被标准化,因此在一定范围内,大多数通过HTTP进行的通信都可以称为REST。 REST通常与映射到相应的HTTP方法PUT,GET,DELETE的Create,Read,Update和Delete操作(CRUD)相关联。 WebSocket处理消息传递,因此没有一个RPC的作用域。 REST通信数据格式通常限于JSON和请求参数,而WebSocket消息有效负载可以是任何内容,包括纯二进制数据。

当然,WebSocket可以用于与REST类似的目的,但是在大多数情况下,它似乎是虚假的。 如前所述,在使用WebSocket的情况下必须采用不同的设计原则。 下表提供了两者之间的主要区别。

WebSocket

休息

标准化的

支持的

异步消息

请求–响应(同步)

下划线框架

下划线HTTP(GET,PUT,DELETE)

子协议

可发现的操作

二进制和文本

主要是JSON

并发双向更新

主要是CRUD

文件上传示例

此示例演示如何使用WebSocket将文件上传到服务器。 定义服务器端点是一个好的开始

@ServerEndpoint("/upload/{file}")
public class UploadServer {

定义了两个消息处理程序,一个用于接收上载文件的二进制数据,另一个用于命令界面。 由于WebSocket允许分隔文本消息和二进制消息,因此无需额外的精力来定义这两个处理程序。 面向OnMessage处理程序的命令定义为

@OnMessage
	public void processCmd(CMD cmd, Session ses) {

CMD class is defined as

static class CMD {
		public int cmd;
		public String data;
	}

必须指定解码器以将文本消息转换为CMD对象。 定义为

公共静态类CmdDecoder实现Decoder.Text <CMD> {

文本消息编码为JSON并没有任何要求,但是此特定示例使用JSON。 大文件的上传以块为单位进行,以减少内存占用。 无法在浏览器中利用WebSocket的部分框架,因此,使用完整框架来模拟块发送。 由于浏览器异步发送所有消息,因此服务器实际接收到完整文件时没有任何指示。 命令界面用于以下目的

  1. 通知服务器上载过程将开始并指定上载文件的名称
  2. 通知服务器已发送完整文件
  3. 向客户端确认文件已成功存储

相同的CMD对象可用于所有这些目的。 入站命令按以下方式处理

@OnMessage
	public void processCmd(CMD cmd, Session ses) {
		switch (cmd.cmd) {
		case 1: // start
			fileName = cmd.data;
			break;
		case 2: // finish
			close(ses);
			cmd.cmd = 3;
			ses.getAsyncRemote().sendObject(cmd);
			break;
		}
	}

该实现假定浏览器将序列化所有发送消息的活动,并且将按照发送顺序接收所有消息。 但是,如果客户端可以引入某种并行性,则必须在每个消息都带有特定id的地方使用更复杂的实现。 另一个解决方案是为每个接收到的块发送一个确认,尽管这种方法会杀死所有WebSocket的好处。 由于CMD对象用于向客户端发送消息,因此必须提供编码器,如下所示:

public static class CmdEncoder implements Encoder.Text<CMD> {

解码器和编码器都必须在ServerEndpoint注释中指定为

@ServerEndpoint(value = "/upload/{file}", decoders = UploadServer.CmdDecoder.class, encoders=UploadServer.CmdEncoder.class)
public class UploadServer {

二进制消息处理程序定义为

@OnMessage
	public void savePart(byte[] part, Session ses) {
		if (uploadFile == null) {
			if (fileName != null)
				try {
					uploadFile = new RandomAccessFile(fileName, "rw");
				} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return;
				}
		}
		if (uploadFile != null)
		try {
			uploadFile.write(part);
			System.err.printf("Stored part of %db%n", part.length);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

可以添加OnClose处理程序,以在异常关闭连接的情况下删除不完整的文件。

客户端实现利用HTML5 Workers。 不幸的是,Firefox并未在工作线程中实现克隆File对象,因此可以使用Internet Explorer或Chrome测试此示例。如果该解决方案的浏览器可移植性很重要,则无需使用任何工作程序即可将基于工作线程的解决方案转换为JavaScript代码一位工人。 在这种情况下,由于未使用单独的线程(工作程序),因此可能会观察到某些性能下降。 工作代码是:

var files = [];
var endPoint = "ws" + (self.location.protocol == "https:" ? "s" : "") + "://"
		+ self.location.hostname
		+ (self.location.port ? ":" + self.location.port : "")
		+ "/echoserver/upload/*";
var socket;
var ready;
function upload(blobOrFile) {
	if (ready)
		socket.send(blobOrFile);
}

function openSocket() {
	socket = new WebSocket(endPoint);
	
	socket.onmessage = function(event) {
		self.postMessage(JSON.parse(event.data));
	};

	socket.onclose = function(event) {
		ready = false;
	};

	socket.onopen = function() {
		ready = true;
		process();
	};
}

function process() {
	while (files.length > 0) {
		var blob = files.shift();
		socket.send(JSON.stringify({
			"cmd" : 1,
			"data" : blob.name
		}));
		const
		BYTES_PER_CHUNK = 1024 * 1024 * 2;
		// 1MB chunk sizes.
		const
		SIZE = blob.size;

		var start = 0;
		var end = BYTES_PER_CHUNK;

		while (start < SIZE) {

			if ('mozSlice' in blob) {
				var chunk = blob.mozSlice(start, end);
			} else if ('slice' in blob) {
					var chunk = blob.slice(start, end);
			} else {
				var chunk = blob.webkitSlice(start, end);
			}

			upload(chunk);

			start = end;
			end = start + BYTES_PER_CHUNK;
		}
		socket.send(JSON.stringify({
			"cmd" : 2,
			"data" : blob.name
		}));
		//self.postMessage(blob.name + " Uploaded Succesfully");
	}
}

self.onmessage = function(e) {
	for (var j = 0; j < e.data.files.length; j++)
		files.push(e.data.files[j]);

	//self.postMessage("Job size: "+files.length);
			
	if (ready) {
		process();
	} else
		openSocket();
}

方便地,JavaScript代码还使用消息传递机制与工作人员进行通信。 当用户在浏览器中提供上传文件时,他们会收到消息通知工作人员。 它处理批处理中的第一个文件。 该过程将文件拆分为片(块),然后通过WebSocket逐一发送它们。 最后发送cmd = 2的命令消息。 命令消息处理程序将消息重新发布到主JavaScript,以通知已完成所提供文件的上传。 尝试发送许多大文件时,代码会给浏览器施加压力。 因此,仅当收到有关上传完成的消息时,才能重新组织代码以发送下一个文件。 修改留给读者练习。 附录1提供了示例的完整源代码。

参考资料

  1. W3C候选推荐书2012年9月20日
  2. REST与WebSocket
  3. WebSocket与REST?
  4. REST与WebSocket比较和基准

附录

有关此示例以及其他示例的完整代码,请参见此处

翻译自: https://www.infoq.com/articles/websocket-desktop-agility-web-applications/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

不能引入websocket

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值