从搭建 WebServer 到 Express 框架的使用,我们正在逐步接近HTTP。我们今天的主题是围绕 Node 来创建一个 HTTP 客户端和 Server 端,感受一下不使用第三方框架时如何使用 HTTP。
Node.js 提供了 HTTP 模块,可以直接拿来使用,也可以作为我们学习 HTTP 协议的线索。并实现了 TCP 和 UDP。
很久以前,我比较好奇为什么客户端发出一个请求后,server 端会接收到请求呢?为什么请求需要 ip 地址和端口号呢?端口号是用来干什么的?server 端是如何发出响应的?遇到各种状态码该如何找到问题的原因?为什么抓包软件可以抓取到 HTTP 请求?HTTPS 是如何实现的?HTTP 和 TCP 有什么关系呢?socket 起到了什么作用?这些问题,面试的时候被问到的时候,我会给出一个「连我自己都不知道对错的答案」,因为网上就是这么说的,我没有实际验证过,也没看过 HTTP 是如何实现的,而 Node 对 HTTP 模块的实现无疑是为我们这一阶段的学习提供了很多有价值的资料。这一阶段我们要学习这些内容,从原理上来搞懂 HTTP。时间长,任务重,贵在坚持。
使用 Node 中的 HTTP 模块不仅可以实现一个 HTTP server,而且可以作为客户端发起 HTTP 请求,也就是使用 Node 可以同时实现客户端与 server 端,这就可以替换掉上节课程 第2天:server 的接口是如何实现的 使用的 iOS 客户端。使用 Node 来创建一个 HTTP Server。
分析一下下面的代码(server.js 文件),在 Node 中首先需要引用 HTTP 模块,然后通过 HTTP 来创建一个 server 对象,通过 server 对象便可以监听它的一系列事件,其中最常用的是 request 事件,它接收到客户端请求事件时就会执行,这时server端会根据请求对象发出的「指令」来做事情。这里,我们只处理了请求 path 为 /api/fe/list 的 request,其它请求一律都走 404。最后需要通过 listen 方法来制定一个端口号。
const http = require('http');
// 创建一个 HTTP server
const server = new http.Server();
/**
* request 事件,当客户端发起请求后会响应这个事件
* req:请求对象
* res:响应对象
* */
server.on('request', function(req, res) {
let path = req.url;
if (path.indexOf('/api/fe/list') == 0) {
// 处理请求的 path 为 /api/fe/list
res.writeHead(200, {
"Content-type" : "application/json"
});
let data = {
title: "前端小课",
des: "内容由素燕公众号发布"
};
// 最终数据需要转换成 json 字符串
res.write(JSON.stringify(data));
} else {
// 未实现,直接报 404 错误
res.writeHead(404, {
"Content-type" : "application/json"
});
let data = {
code: "404",
msg: "not found"
};
res.write(JSON.stringify(data));
}
res.end();
});
// 监听 8888 端口
server.listen(8888, function() {
console.log('Server run in: http://127.0.0.1:8888');
});
通过 node server.js 来启动上面的HTTP 服务:
http://127.0.0.1:8888/api/fe/list
http://127.0.0.1:8888/
Node 的 HTTP 实现是基于事件的,可以通过 on 方法来监听不同的事件。上面的代码中我们监听了 request 事件,它触发的时机是当客户端发起网络请求后,就会执行这个事件。它会携带请求对象 req 和响应对象 res,req 是用来获取客户端的请求数据,这样 server 可以做出正确的响应。Node 也可以充当客户端来发起网络请求。
下面的代码(client.js)创建一个 HTTP 客户端来发起网络请求,通过 http.request 方法来发起请求,通过 options 来声明请求需要的信息。
const http = require('http');
// 构建 request 对象所需要的参数
const options = {
// 请求地址 ip
host: '127.0.0.1',
// 端口号
port: 8888,
// 方法
method: 'GET',
// 请求的路径
path: '/api/fe/list',
protocol: 'http:',
// 超时时间设置 1秒
timeout: 1000
}
const req = http.request(options, function (res) {
console.log(`STATUS: ${res.statusCode}`);
console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
// 响应数据
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
// 响应结束
res.on('end', () => {
console.log('No more data in response.');
});
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.end();
通过 node client.js 执行一下客户端的代码,下面执行了 2 次请求,和在浏览器发起请求结果是一致的。在执行请求的时候需要先执行 server.js 来启动服务,然后在执行 client.js:
上面代码的 demo 可以在 https://github.com/lefex/FE 上下载。
总结
打卡互动(任选一个):
1.TCP 与 socket 有关系吗?
2. socket 是什么,为了解决什么问题?
第2天:server 的接口是如何实现的