问题:启用 Keep-Alive 头部字段后,重用的是 HTTP 连接,还是 TCP 连接?
WebSocket 并不是将 HTTP 的设计完全推翻重建,而是在 HTTP 的基础上增添了一些逻辑来,管理客户端和服务器端的流。这些流的内容也是 HTTP 请求和响应,保留了旧语义,只是编码和打包方式不同。
了解了理论知识后,我们动手开发一套最简单的 WebSocket 服务器端和客户端实现。
WebSocket 服务器端实现 var app = require('express')(); var server = require('http').Server(app); var io = require('socket.io')(server); var defaultPort = 3001; var port = process.env.PORT || defaultPort; var i = 0; console.log("Server is listening on port: " + defaultPort); server.listen(port); io.on('connection', function (socket) { console.log("connect comming from client: " + socket.id); socket.emit('messages_jerry', { hello: 'world greeting from Server!' }); socket.on('messages', function (data) { console.log("data received from Client:" + JSON.stringify(data,2,2)); }); });
代码实现包含了4个关键点:
- 服务器监听在默认的 3001 端口上。
- 一旦 WebSocket 客户端有发送到 3001 端口上的连接请求时,代码第 12 行的 on 监听函数触发,监听的事件名称为 connection,然后在监听函数的实现体里,打印出客户端连接的 id 值。
- 服务器端接收了客户端的链接后,向客户端通过第 15 行的 emit 方法,发送一个 messages_jerry 的事件,以及一个 JSON 对象作为事件负载。
- 第 17 行服务器端监听在 messages 事件上的监听函数触发时,说明接收到了从客户端发送过来的事件,在监听函数里打印出客户端传递过来的数据。
WebSocket 客户端实现
// #!/usr/bin/env node const io = require('socket.io-client'); var socket = io.connect('http://localhost:3001'); socket.on('messages_jerry', function (data) { console.log("data sent from Server:" + JSON.stringify(data,2,2)); socket.emit('messages', { my: 'data sent from Client' }); }); socket.on('connect', function (socket2) { console.log('Connection with Server established!'); socket.emit('messages', 'Client has established connection with Server'); });
代码的关键点:
- 客户端通过 connect 方法向 WebSocket 服务器发起连接请求
- 连接成功建立后,客户端第 10 行的 on 监听函数触发,该函数监听在 connect 事件上,会在 Web Socket 连接成功建立后自动触发。
- 客户端在第 12 行调用 emit 向服务器发送一个 messages 事件。
- 客户端监听在 messages_jerry 的监听函数触发,说明服务器端有数据到达。使用第 6 行的 console.log 语句打印出这个数据。
- 在第 7 行代码,客户端调用 emit,向服务器端发送一个请求,通知服务器自己已经收到了服务器发送过来的数据。
使用命令行 node wsServer.js 启动服务器端,看到如下输出:
新开一个命令行窗口,使用 node wsClient.js 启动客户端,能看到客户端打印出的成功建立连接,以及从服务器端发送过来的数据:
切换回服务器端,红色高亮的内容,就是客户端与服务器端建立连接之后,服务器端新打印出的数据:
回到本文开头抛出的问题:
问题1
为什么 Cypress 的 visit 方法选择了 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 之间,还存在着一个中间层?
问题3
WebSocket Connection,HTTP Connection,TCP connection,这三者的区别和联系是什么?
参考资料
Difference between HTTP and WebSocket (HTTP 2.0 )