思考WebSocket和HTTP的区别以及WebSocket客户端和服务端的最简单实现传递和获取数据,见下图中变量ev.data的值。同事的研究成果通过乔普林的笔记记录如下。那么笔者心中就有一个疑问,为什么Cypress的访问方式会选择WebSocket作为与目标网站的通信技术呢?为什么不直接走HTTP协议,比如使用ES6原生支持的fetch来访问目标网站呢?要回答这个问题,我们首先需要了解WebSocket到底是什么,以及它与HTTP相比的优缺点。事实上,WebSockets可以在用户的??浏览器和服务器之间打开一个交互式通信会话,浏览器可以向服务器发送消息并接收事件驱动的响应,而无需轮询服务器以获取响应。WebSocket基于TCP连接,提供了服务器与浏览器之间的全双工通信功能,即服务器可以主动向浏览器推送数据,这在HTTP协议中是不可能的,它只支持浏览器对服务器的请求-服务器端的响应方式,即浏览器客户端要查询服务器端是否有最新的事件发生,只能使用效率低下的轮询方式。例如,当用户向服务器发送请求时,请求以HTTP或HTTPS的形式发送。服务器收到请求后,向客户端发送响应。每个请求都与相应的响应相关联。发送响应后,连接关闭。每个HTTP或HTTPS请求每次都会与服务器建立一个新的连接,在得到响应后,连接会自行终止。作者注:HTTP请求头中的Connection:keep-alive字段能否满足连接复用的需要?当启用Keep-Alive时,客户端和服务器同意为后续请求或响应保持连接打开。默认情况下,HTTP连接在数据事务结束时关闭。这意味着客户端创建一个新的连接来请求页面的每个文件,服务器在发送完数据后关闭这些TCP连接。但是,如果服务器需要同时响应多个HTTP请求并为每个新的TCP连接提供一个文件,则站点页面的加载时间将会增加。这可能会导致糟糕的用户体验。为了克服这个问题,网站所有者需要启用Keep-Alive标头来限制新连接的数量。通过打开Keep-Alive连接标头,客户端可以通过单个TCP连接下载所有内容,例如JavaScript、CSS、图像和视频,而不是为每个文件发送单独的请求。这是一张展示Keep-Alive工作原理的图片:问题:启用Keep-Alive标头字段后,HTTP连接或TCP连接是否被重用?WebSocket并没有完全推翻和重建HTTP的设计,而是在HTTP的基础上增加了一些逻辑来管理客户端和服务端的流程。这些流的内容也是HTTP请求和响应,保留了旧的语义,但编码和封装不同。了解了理论知识后,我们开始着手开发一套最简单的WebSocket服务端和客户端实现。WebSocket服务器实现varapp=require('express')();varserver=require('http').Server(app);vario=require('socket.io')(服务器);vardefaultPort=3001;varport=process.env.PORT||defaultPort;vari=0;console.log("Serverislisteningonport:"+defaultPort);server.listen(port);io.on('connection',function(socket){console.log("connectcomingfromclient:"+socket.id);socket.emit('messages_jerry',{hello:'来自服务器的世界问候!'});socket.on('messages',function(data){console.log("data从客户端收到:"+JSON.stringify(data,2,2));});});代码实现包含4个关键点:服务端监听默认的3001端口。一旦WebSocket客户端向3001端口发送连接请求,就会触发代码第12行的on监听函数,监听的事件名称为connection,然后在监听函数的实现体中,打印出id客户端连接的值。服务端收到客户端的链接后,通过第15行的emit方法向客户端发送一个messages_jerry事件和一个JSON对象作为事件payload。当第17行的messages事件的监听函数被触发时,意味着:接收到客户端发送的事件,并在监听函数中打印出客户端传过来的数据。WebSocket客户端实现//#!/usr/bin/envnodeconstio=require('socket.io-client');varsocket=io.connect('http://localhost:3001');socket.on('messages_jerry',function(data){console.log("datasentfromServer:"+JSON.stringify(data,2,2));socket.emit('messages',{my:'datasentfromClient'});});socket.on('connect',function(socket2){console.log('已建立与服务器的连接!');socket.emit('messages','客户端已与服务器建立连接');});代码关键点:客户端通过connect方法向WebSocket服务端发起连接请求。连接成功后,会触发客户端第10行的on监听函数。该函数监听connect事件,会在WebSocket连接成功建立时自动触发。客户端在第12行调用emit向服务器发送消息事件。clientmonitor由messages_jerry的monitor函数触发,表示有数据从服务端到达。使用第6行的console.log语句打印出此数据。第七行代码,客户端调用emit向服务端发送请求,通知服务端已经收到服务端发送的数据。使用命令行nodewsServer.js启动服务器,看到如下输出:打开一个新的命令行窗口,使用nodewsClient.js启动客户端,可以看到客户端打印并发送成功连接建立来自服务端的数据:切换回服务端,红色高亮显示的内容为客户端与服务端建立连接后从服务端新打印的数据:返回本文开头提出的问题:问题1为什么Cypress的访问方式会选择WebSocket作为与目标网站的通信技术呢?为什么不直接走HTTP协议,比如使用ES6原生支持的fetch来访问目标网站呢?笔者猜测,是不是因为Cypress中的一些API,比如cy.XXX,需要借助WebSocket的全双工通信特性才能完全发挥作用?问题2那么问题又来了。在我们的cy.visit('http://xxx.com')代码中,如果最后Cypress通过WebSocket协议向http://xxx.com发送数据报,但是http://xxx.com不支持WebSocket怎么办?就像本文前面介绍的例子一样,WebSocket需要客户端和服务端都支持。那么cy.visit和visit参数中指定的webSite之间是否还有中间层呢?问题3WebSocket连接、HTTP连接、TCP连接,这三者的区别和连接是什么?HTTP和WebSocket的区别(HTTP2.0)
