简单了解 node http 模块
文章记录了对http 模块的简单使用与理解。
- http 服务端
- http 客户端
- 总结
1. http 服务端
先写个小例子
服务端:
let http = require('http') let server = http.createServer((req, res) => { let buf = [] req.on('data', data => { buf.push(data) }) req.on('end', () => { let str = Buffer.concat(buf).toString() res.end(str) }) }) server.listen(8080, () => { console.log('server start') })
大概代码执行流程:
http.createServer() 返回新建的 http.Server 实例。http.Server 继承自net.Server。requestListener 是一个自动添加到到 'request' 事件的函数。
'request' 事件 每次请求的时候都会触发。http服务是以request为单位进行服务的
http.IncomingMessage 对象,它可用于访问本次,请求的请求方法、消息头、以及数据(图中将,请求头与数据拆分开)。
http.ServerResponse 对象,服务端通过http.ServerResponse 实例,来给客户端(数据请求方)返回本次数据。包括响应头,响应体(内部通过socket来发送信息)。
http服务端:在TCP模块套接字 socket 上,接受的数据解析出请求报文头和报文主体,发送的响应报文头和报文主体组装成数据发送出去。
1.1 请求:
请求报文由以下组成
- 报文首部:请求方法、请求 URI、协议版本、可选的请求首部字段等
- 空行
- 报文主体:内容实体构成数据。
node http 模块 在TCP连接的读操作上,将数据解析成(以空行分割,报文头和报文体):
报文头部分:
req.method
req.httpVersionMajor
req.httpVersionMinor
req.httpVersion
req.upgrade
...
req.headers = {
Content-Length: 15
Content-Type: application/x-www-form-urlencoded
... ...
}
//
报文主体部分:
一个可读流对象req,可以继续报文主体数据的读取
1.2响应
服务端通过http.ServerResponse 实例,来向客户端(数据请求方)返回本次请求数据。包括响应头,响应体(内部通过socket来发送信息)。
node http 模块 在TCP连接的写操作上,将数据写缓存拼接(以空行拼接,报文头和报文体)成:
//中间有个空行 "HTTP/1.1 200 OK Date: Sat, 30 Nov 2019 05:10:05 GMT Connection: close Content-Length: 25 " + 请求体部分数据或全部数据
通过的方法
// 具体使用参考文档 // 设置头部,头部并没有发送 response.setHeader(name, value) // 设置头部,向请求发送响应头,此方法只能在消息上调用一次,并且必须在调用 response.end() 之前调用。 response.writeHead() // 如果调用此方法并且尚未调用 response.writeHead(),则将切换到隐式响应头模式并刷新隐式响应头。 // 这会发送一块响应主体。 可以多次调用该方法以提供连续的响应主体片段。 response.write() // 此方法向服务器发出信号,表明已发送所有响应头和主体,该服务器应该视为此消息已完成。 必须在每个响应上调用此 response.end() 方法。 response.end()
2. http 客户端
客户端向 HTTP服务器发起请求:
let http = require('http') let options = { host: 'localhost', port: 8080, method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' } } // 请求并没有 发出 req 是个可写流 let req = http.request(options) req.on('response', res => { console.log(res.headers) let buf = [] res.on('data', data => { buf.push(data) }) res.on('end', () => { console.log(Buffer.concat(buf).toString()) }) }) // write 向请求体写数据 req.write('name=luoxiaobu&title=http') // 实际的请求头将会与第一个数据块一起发送,或者当调用 request.end() 时发送。 req.end()
大概代码执行流程:
http.request() 返回 http.ClientRequest 类的实例。http.ClientRequest 内部创建了一个socket来发起请求。
ClientRequest 实例是可以看做可写流。如果需要使用 POST 请求上传文件,则写入到 ClientRequest 对象。
response事件 每次服务器端有数据返回响应时都会触发。
http.IncomingMessage 对象,它可用于访问本次服务器端返回的,响应状态、消息头、以及数据。
http客户端:在TCP模块套接字 socket 上,请求报文头和报文主体组装成数据发送出去,接受的数据解析出响应报文头和报文主体,。
2.1 请求:
node http 模块 在TCP连接的写操作上,将数据写缓存拼接(以空行拼接,报文头和报文体)成:
//中间有个空行 "POST / HTTP/1.1 content-type: application/x-www-form-urlencoded Host: localhost:8080 Connection: close Transfer-Encoding: chunked " + 请求体部分数据或全部数据
通过的方法
// 具体使用参考文档 const postData = querystring.stringify({ 'msg': '你好世界' }); const options = { hostname: 'localhost:8080', path: '/upload', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // 内部会处理 options 解析出头部 const req = http.request(options, (res) => { }); // 这会发送一块响应主体。 可以多次调用该方法以提供连续的响应主体片段。 response.write() // 此方法向服务器发出信号,表明已发送所有响应头和主体,该服务器应该视为此消息已完成。 必须在每个响应上调用此 response.end() 方法。 response.end()
2.2 响应
响应报文图:
响应报文由以下组成
- 报文首部:协议版本、状态码(表示请求成功或失败的数字代码)、用以解释状态码的原因短语、可选的响应首部字段
- 空行
- 报文主体:实体主体构成。
node http 模块 在TCP连接的读操作上,将数据解析成:
报文头部分:
res.statusCode = statusCode;
res.statusMessage = statusMessage;
res.httpVersionMajor
res.httpVersionMinor
res.httpVersion
res.upgrade
...
res.headers = {
Content-Length: 15
Content-Type: application/x-www-form-urlencoded
... ...
}
//
报文主体部分:
一个可读流对象,可以继续报文主体数据的读取
3. 总结
文章记录了对http 模块的简单使用与理解,要深入理解http 模块,还需多看文档,代码实践。
文中例子比较粗糙,理解不准确之处,还请教正。
参考资料:<<图解HTTP>>