Node.js是一个服务器平台。 服务器的主要任务是尽可能快而有效地处理来自客户端(尤其是来自浏览器)的请求。 我们今天发布的Node.js教程的译文的第八篇是关于HTTP和WebSocket的。

发出HTTP请求时会发生什么?
让我们谈谈浏览器如何使用HTTP / 1.1协议向服务器发出请求。
如果您曾经在IT领域接受过采访,那么您可能会被问到在浏览器的地址栏中键入内容并按Enter时会发生什么。 也许这是在此类采访中出现的最受欢迎的问题之一。 任何提出此类问题的人都想知道您是否可以解释一些非常简单的概念,并了解您是否了解Internet的原理。
这个问题涉及许多技术,要了解其一般原理,就意味着要了解如何构建人类有史以来最复杂的系统之一,该系统涵盖整个世界。
▍HTTP协议
现代浏览器能够将输入其地址栏中的真实URL与搜索查询区分开,通常使用默认搜索引擎进行处理。 我们将讨论URL。 如果在浏览器栏中输入站点地址(例如
flaviocopes.com
,则浏览器会基于HTTP协议将用于与指定资源交换数据的假设,将该地址转换为
http://flaviocopes.com
形式。 请注意,在Windows上,我们要讨论的内容可能与macOS和Linux上的有所不同。
▍DNS查找阶段
因此,浏览器开始从用户请求的地址下载数据,然后执行DNS查找操作(DNS查找)以找出相应服务器的IP地址。 在地址栏中输入的资源的符号名称对人们来说很方便,但是Internet设备暗示可以使用IP地址在计算机之间交换数据,IP地址是222.324.3.1(对于IPv4)这样的数字集。
首先,找出服务器的IP地址,浏览器会查看本地DNS缓存,以查看是否最近执行了类似的步骤。 例如,在Chrome浏览器中,有一种便捷的方法可以通过在地址栏中输入以下地址来查看DNS缓存:
chrome://net-internals/#dns
。
如果在缓存中找不到任何内容,则浏览器将使用POSIX
gethostbyname
系统调用来查找服务器的IP地址。
▍gethostbyname函数
gethostbyname
函数首先检查
hosts
,该
hosts
在macOS或Linux上可以在
/etc/hosts
上找到,以便确定是否可以通过查找服务器地址来解析本地服务器信息。
如果本地解决请求服务器IP地址的请求失败,则系统会向DNS服务器执行请求。 此类服务器的地址存储在系统设置中。
以下是一些流行的DNS服务器:
- 8.8.8.8:Google DNS服务器。
- 1.1.1.1:CloudFlare DNS服务器。
大多数人使用其提供商提供的DNS服务器。 浏览器使用UDP协议执行DNS查询。
TCP和UDP是计算机网络中使用的两个基本协议。 它们位于相同的概念级别,但是TCP是面向连接的协议,并且对于UDP消息的交换(UDP消息的处理在系统上造成很小的额外负担),不需要建立连接的过程。 我们不会确切讨论如何通过UDP交换数据。
与我们感兴趣的域名相对应的IP地址可能在DNS服务器的缓存中。 如果不是这种情况,他将联系根DNS服务器。 根DNS服务器系统由13个服务器组成,整个Internet的运行都依赖于这13个服务器。
应该注意的是,根DNS服务器不知道世界上所有现有域名和IP地址之间的对应关系。 但是类似的服务器知道诸如.com,.it,.pizza等域的顶级DNS服务器的地址。
收到请求后,根DNS服务器会将其重定向到顶级域的DNS服务器,再到所谓的TLD服务器(从顶级域)。
假设浏览器正在寻找
flaviocopes.com
服务器的IP地址。 转到根DNS服务器,浏览器将从中接收.com区域的TLD服务器地址。 现在,此地址将存储在缓存中,因此,如果您需要从.com区域中查找另一个URL的IP地址,则无需再次与根DNS服务器联系。
TLD服务器具有名称服务器(名称服务器,NS)的IP地址,借助它们,您可以从我们拥有的URL中找到IP地址。 NS服务器从哪里获得此信息? 事实是,如果您购买域名,域名注册商会将有关该域名的数据发送到名称服务器。 例如,当更改主机时,将执行类似的过程。
有问题的服务器通常由托管提供商拥有。 通常,为了防止故障,将创建多个此类服务器。 例如,它们可能具有以下地址:
- ns1.dreamhost.com
- ns2.dreamhost.com
- ns3.dreamhost.com
为了通过URL查找IP地址,最后,他们转向此类服务器。 它们存储有关IP地址的实际数据。
现在,在设法找出在浏览器地址栏中输入的URL的IP地址之后,我们继续进行下一步。
▍建立TCP连接
获悉服务器的IP地址后,客户端可以启动与其的TCP连接。 在建立TCP连接的过程中,客户端和服务器之间会相互传输一些服务数据,然后它们可以交换信息。 这意味着在建立连接之后,客户端将能够向服务器发送请求。
request发送要求
请求是根据使用的协议规则构造的文本片段。 它包括三个部分:
查询字串
查询字符串是包含以下信息的单个文本字符串:
例如,它可能看起来像这样:
GET / HTTP/1.1
请求标头
请求标头由一组
:
。 有2个必填的标头字段,其中一个是
Host
,第二个是
Connection
。 其余字段是可选的。
标题可能如下所示:
Host: flaviocopes.com Connection: close
“
Host
字段指示浏览器感兴趣的域名。 设置为
close
的
Connection
字段表示客户端和服务器之间的连接不需要保持打开状态。
其他常用的请求标头包括以下内容:
Origin
Accept
Accept-Encoding
Cookie
Cache-Control
Dnt
实际上,还有更多。
请求标头以空字符串结尾。
请求正文
请求主体是可选的;在GET请求中不使用。 该请求正文可用于POST请求以及其他请求中。 例如,它可能包含JSON格式的数据。
由于现在我们正在讨论GET请求,因此请求正文将为空,我们将不再使用它。
▍答案
服务器接收到客户端发送的请求后,将对其进行处理并向客户端发送响应。
响应以状态码和相应的消息开始。 如果请求成功,则响应的开始将如下所示:
200 OK
如果出了点问题,可能还有其他代码。 例如,以下内容:
404 Not Found
403 Forbidden
301 Moved Permanently
500 Internal Server Error
304 Not Modified
401 Unauthorized
此外,响应包含HTTP标头列表和响应的主体(由于请求是由浏览器执行的,因此将为HTML代码)。
HTML解析
浏览器收到服务器的响应(服务器的主体包含HTML代码)后,便开始解析它,并对形成页面所需的每个资源重复上述过程。 这些资源包括,例如:
- CSS文件。
- 图片
- 网页图标(图标)。
- JavaScript文件。
浏览器如何精确显示页面并不适用于我们的对话。 这里让我们感兴趣的主要事情是,以上请求和接收数据的过程不仅用于HTML代码,而且还用于使用HTTP协议从服务器传输到浏览器的任何其他对象。
关于使用Node.js创建简单的服务器
现在,在检查了浏览器和服务器之间的交互过程之后,您可以从本系列材料的
第一部分中的“ First Node.js应用程序”部分重新了解一下,其中我们描述了一个简单服务器的代码。
使用Node.js发出HTTP请求
要使用Node.js执行HTTP请求,请使用适当的
模块 。 下面的示例使用
https模块。 事实是,在现代条件下,只要有可能,就必须使用HTTPS协议。
▍执行GET请求
这是一个使用Node.js执行GET请求的示例:
const https = require('https') const options = { hostname: 'flaviocopes.com', port: 443, path: '/todos', method: 'GET' } const req = https.request(options, (res) => { console.log(`statusCode: ${res.statusCode}`) res.on('data', (d) => { process.stdout.write(d) }) }) req.on('error', (error) => { console.error(error) }) req.end()
▍POST请求执行
以下是从Node.js发出POST请求的方法:
const https = require('https') const data = JSON.stringify({ todo: 'Buy the milk' }) const options = { hostname: 'flaviocopes.com', port: 443, path: '/todos', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': data.length } } const req = https.request(options, (res) => { console.log(`statusCode: ${res.statusCode}`) res.on('data', (d) => { process.stdout.write(d) }) }) req.on('error', (error) => { console.error(error) }) req.write(data) req.end()
▍PUT和DELETE查询
这样的请求的执行看起来与POST请求的执行相同。 除了此类操作的语义内容之外,主要区别在于
options
对象的
method
属性的值。
using使用Axios库在Node.js中执行HTTP请求
Axios是一个非常流行的JavaScript库,它可以在浏览器(包括从IE8开始的所有现代浏览器和IE)中以及可用于执行HTTP请求的Node.js环境中运行。
该库基于Promise,与标准机制(尤其是API Fetch)相比,具有一些优势。 其优点包括:
- 支持较旧的浏览器(您需要使用polyfill才能使用Fetch)。
- 能够中断请求。
- 支持设置请求超时。
- 内置防御CSRF攻击的功能。
- 支持上传数据,并提供有关此过程进度的信息。
- 支持JSON数据转换。
- Node.js的工作
安装方式
您可以使用npm安装Axios:
npm install axios
使用纱线可以达到相同的效果:
yarn add axios
您可以使用
unpkg.com
将库连接到页面:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
Axios API
您可以使用
axios
对象发出HTTP请求:
axios({ url: 'https://dog.ceo/api/breeds/list/all', method: 'get', data: { foo: 'bar' } })
但是使用特殊方法通常更方便:
这类似于jQuery使用
$.get()
和
$.post()
而不是
$.ajax()
$.post()
。
Axios提供了用于执行其他类型的HTTP请求的单独方法,这些方法不像GET和POST那样流行,但仍被使用:
axios.delete()
axios.put()
axios.patch()
axios.options()
该库提供了一种用于执行请求的方法,该请求旨在仅接收HTTP头,而没有响应主体:
GET请求
Axios使用现代的异步/等待语法很方便使用。 以下为Node.js设计的代码示例使用该库从
Dog API加载狗品种列表。 这里应用了
axios.get()
方法,并对岩石计数:
const axios = require('axios') const getBreeds = async () => { try { return await axios.get('https://dog.ceo/api/breeds/list/all') } catch (error) { console.error(error) } } const countBreeds = async () => { const breeds = await getBreeds() if (breeds.data.message) { console.log(`Got ${Object.entries(breeds.data.message).length} breeds`) } } countBreeds()
可以不使用async / await而是应用promise来重写相同的内容:
const axios = require('axios') const getBreeds = () => { try { return axios.get('https://dog.ceo/api/breeds/list/all') } catch (error) { console.error(error) } } const countBreeds = async () => { const breeds = getBreeds() .then(response => { if (response.data.message) { console.log( `Got ${Object.entries(response.data.message).length} breeds` ) } }) .catch(error => { console.log(error) }) } countBreeds()
在GET请求中使用参数
GET请求的URL中可能包含如下所示的参数:
https:
使用Axios时,可以这样进行查询:
axios.get('https:
通过在具有参数的对象中设置
params
属性,可以达到相同的效果:
axios.get('https://site.com/', { params: { foo: 'bar' } })
POST请求
发出POST请求与发出GET请求非常相似,但是在这里,使用
axios.get()
方法代替
axios.post()
方法:
axios.post('https:
作为第二个参数,
post
方法接受带有请求参数的对象:
axios.post('https://site.com/', { foo: 'bar' })
在Node.js中使用WebSocket协议
WebSocket是HTTP的替代方法,可用于组织Web应用程序中的数据交换。 该协议允许您在客户端和服务器之间创建长期的双向通信通道。 建立连接后,通信通道保持打开状态,这使应用程序处于非常快速的连接状态,其特点是等待时间短且系统上的附加负载很小。
所有现代浏览器均支持WebSocket协议。
▍HTTP差异
HTTP和WebSocket是非常不同的协议,它们使用不同的方法交换数据。 HTTP基于“请求-响应”模型:请求后,服务器会将一些数据发送到客户端。 对于WebSocket,一切安排都不同。 即:
- 服务器可以主动向客户端发送消息,而无需等待客户端的请求。
- 客户端和服务器可以同时交换数据。
- 在发送消息时,将使用极少量的服务数据。 这尤其导致数据传输的低等待时间。
WebSocket协议非常适合通过长时间开放的通道进行实时通信。 反过来,HTTP非常适合组织客户端发起的偶尔的通信会话。 同时,应注意的是,从编程的角度来看,使用HTTP协议实现数据交换要比使用WebSocket协议容易得多。
Socket WebSocket协议的受保护版本
WebSocket协议有一个不安全的版本(
ws://
URI方案),就安全性而言,它类似于
http://
协议。 应该避免使用
ws://
,而是使用协议的安全版本
wss://
。
▍创建WebSocket连接
要创建WebSocket连接,您需要使用适当的
构造函数 :
const url = 'wss://myserver.com/something' const connection = new WebSocket(url)
建立成功的连接后,将引发
open
事件。 您可以通过为
connection
对象的
onopen
属性分配回调函数来组织此事件:
connection.onopen = () => {
要处理错误,请使用
onerror
事件处理程序:
connection.onerror = error => { console.log(`WebSocket error: ${error}`) }
data将数据发送到服务器
打开与服务器的WebSocket连接后,可以向其发送数据。 例如,可以在onopen
onopen
完成此操作:
connection.onopen = () => { connection.send('hey') }
from从服务器获取数据
要从服务器接收使用WebSocket协议发送的数据,可以分配onmessage
onmessage
,当接收到
message
事件时将调用该
onmessage
:
connection.onmessage = e => { console.log(e.data) }
Node在Node.js环境中实现WebSocket服务器
为了在Node.js环境中实现WebSocket服务器,可以使用流行的
ws库。 我们将使用它进行服务器开发,但它适用于创建客户端以及组织两个服务器之间的交互。
通过首先初始化项目来安装此库:
yarn init yarn add ws
我们需要编写的WebSocket服务器代码非常紧凑:
constWebSocket = require('ws') const wss = newWebSocket.Server({ port: 8080 }) wss.on('connection', ws => { ws.on('message', message => { console.log(`Received message => ${message}`) }) ws.send('ho!') })
在这里,我们创建一个新服务器,该服务器在标准端口8080上侦听WebSocket协议并描述一个回调,该回调在建立连接后将向客户端发送一个
ho!
消息
ho!
并将从客户端收到的消息打印到控制台。
这是 WebSocket服务器
的工作示例,
这是可以与其交互的客户端。
总结
今天,我们讨论了Node.js平台支持的网络机制,并与浏览器中使用的类似机制进行了比较。 我们的下一个主题将是处理文件。
亲爱的读者们! 您是否在Web应用程序中使用WebSocket协议,该Web应用程序的服务器端是使用Node.js创建的?