前言
相信大家对http都不陌生,我们平时用的网络是在tcp/ip协议族的基础上运作的,http是属于内部的一个子集。tcp/ip协议分别有四层:应用层(http)、传输层(tcp)、网络层(ip)、数据链路层(网络等),简要说一下大概的流程:
- 客户端发起一个http的请求(http请求报文)
- 传输层收到http请求报文数据后进行分割,当然是为了传输方便,然后在上面打上序号和端口。
- 网络层接收后添加上MAC地址然后转发给下一层链路层
-
对应的接收服务器链路层收到数据后就会解析,然后就是网络层》传输层》应用层一步一步解析。
下面一张图就非常直观了。
ok,了解大概流程后,我们继续,这节主要说的是应用层http,当然本人也是菜鸟,只是想分享交流一下,不足的请指出。
了解 HTTP
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)
- 请求的一方叫客户端,响应的一方叫服务器端
- 通过请求和响应达成通信
- HTTP是一种不保存状态的协议
HTTP的历史(了解一下哈~):
HTTP/0.9 --1990年问世,那时的HTTP并没有作为正式的标准被建立,只支持GET方法,不支持MIM类型,很快被HTTP/1.0取代
HTTP/1.0 --HTTP正式作为标准被公布是在1996年的5月,版本被命名为HTTP/1.0,并记载于RFC1945。虽然说是初期标准,但该协议标准至今仍被使用在服务器端。
HTTP/1.1 --1997年公布的HTTP/1.1是目前主流的HTTP协议版本。之前的标准是RFC2068,之后又发布了修订版RFC2616。
HTTP/2 --2015年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。
HTTP之请求报文&相应报文
请求报文
请求报文的组成部分由请求行、请求头、请求体三部分组成。
请求行
请求行由请求方法、URL 、协议/版本组成。
- 这里说一下请求方法,http请求中我们最常用的就是POST和 GET了,下面列出方法:
(1)GET:获取资源
(2)POST:传输实体
(3)PUT:传输文件。PUT方法自身不带验证机制,任何人都可以上传文件,存在安全性问题,因此一般的Web网站不使用该方法。若配合Web应用程序的验证机制或架构设计采用REST(表征状态转移)标准的同类Web网站,就可能会开放使用PUT方法。
(4)HEAD:获得报文首部
(5)DELETE:删除文件。特点同PUT方法。
(6)OPTIONS:询问支持的方法。服务器不一定支持所有HTTP协议的方法,所以可以通过OPTIONS请求查看服务器支持的HTTP方法有哪些。
(7)TRACE:追踪路径。用来确认连接过程中发生的一系列操作,但该方法易引起XST(跨站追踪)攻击,通常是不会用到的。
(8)CONNECT:要求用隧道协议连接代理。
请求头
- 通用首部(General Header)
- 请求首部(Request Header)
- 响应首部(Response Header)
- 实体首部(Entity Header Fields)
请求实体
也就是我们平时的请求数据,通常get请求数据为空。
响应报文
一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。
HTTP响应由三个部分组成,分别是:状态行、消息报头、响应正文。
这里我们主要说一下状态码:
200 请求已正常处理
204 请求资源成功,但没有资源可返回
206 部分资源的请求
301 资源的URI已更新,你也更新下你的书签引用吧
302 资源的URI已临时定位到其他位置了,姑且算你已经知道这个情况了
303 资源的URI已更新,你是否能临时按新的URI访问
304 资源已找到,但未符合条件请求......304虽然被划分在3XX类别中,但是和重定向没有关系。
307 临时重定向,和302有着相同的含义,307会遵照标准,不会从POST变成GET。
400 服务器无法理解这个请求
401 需要认证的请求,且认证失败时,返回该状态码
403 服务器拒绝该请求,可能是客户端未获得文件系统的访问授权,也可能是访问权限出现了某些问题。
404 服务器上没有访问的资源
500 服务器端在执行请求时发生了错误。
503 服务器暂时处于超负载或正在进行停机维修,现在无法处理请求
我们常用的状态有:
- 200 OK //客户端请求成功
- 400 Bad Request //客户端请求有语法错误,不能被服务器所理解
- 403 Forbidden //服务器收到请求,但是拒绝提供服务
- 404 Not Found //请求资源不存在,eg:输入了错误的URL
- 500 Internal Server Error //服务器发生不可预期的错误
- 503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
举栗子说明一下
我们可以敲命令验证一下:
curl -v www.baidu.com
红色区域为请求报文
- 红色区域第一行 GET 为请求行
- 其他行 各种首部字段
- 因为是get方法所以没有 请求主体
蓝色区域为相应报文
- 蓝色区域第一行 状态行
- 蓝色区域第一行到第10行为各种首部字段
- 空白行 html部分为 响应正文
下面是请求报文和响应报文的结构:
node创建HTTP服务器
首先要说明一下,http服务器是继承自tcp服务器 http协议是应用层协议,是基于TCP的。
let http = require('http');
//req 流对象 是可读流
//res 是一个可写流 write
let server = http.createServer();
let url = require('url');
//当客户端连接上服务器之后执行回调
server.on('connection', function (socket) {
console.log('客户端连接 ');
});
/**
> POST / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.53.0
> Accept: *
> Content-Length: 9
> Content-Type: application/x-www-form-urlencoded
>
} [9 bytes data]
*/
server.on('close', function (req, res) {
console.log('服务器关闭 ');
});
server.on('error', function (err) {
console.log('服务器错误 ');
});
server.listen(8080, function () {
console.log('server started at http://localhost:8080');
});
当客户端连接上来之后先触发connection事件,然后可以多次发送请求,每次请求都会触发request事件。流程就是服务器监听客户端的请求,当有请求到来的时候执行回调
HTTP请求
//req代表客户端的连接,server服务器把客户端的请求信息进行解析,然后放在req上面
//res代表响应,如果希望向客户端回应消息,需要通过 res
server.on('request', function (req, res) {
console.log(req.method);//获取请求方法名
let { pathname, query } = url.parse(req.url, true);
console.log(pathname);
console.log(query);
console.log(req.url);//获取请求路径
console.log(req.headers);//请求头对象
let result = [];
req.on('data', function (data) {
result.push(data);
});
req.on('end', function () {
let r = Buffer.concat(result);//请求体
console.log(r.toString());
//如果进行响应
res.end(r);
})
});
HTTP响应
let http = require('http');
//如何向客户端写入响应信息 */
/**
HTTP/1.1 200 OK 响应行
Date: Fri, 02 Feb 2018 13:57:46 GMT 响应头
Connection: keep-alive
Content-Length: 9
name=dafei 响应体
Transfer-Encoding: chunked 分块传输
*/
let server = http.createServer(function (req, res) {
console.log('request');
//在同一个方法里设置状态码,原因短语,响应头
res.setHeader('Content-Type', 'text/html');
console.log('headersSent1', res.headersSent);//响应头是否已经发送过了
res.writeHead(200, {
"Content-Type": "text/html;charset=utf8"
})
console.log('headersSent2', res.headersSent);//响应头是否已经发送过了
res.end('byebye');
// res.statusCode = 404;//设置响应码
// res.sendDate = false;//Date响应头默认会设置,如果真的不想要,可以设置为false
// res.setHeader('Content-Type', 'text/html;charset=utf8');//设置响应头
// console.log('getHeader1', res.getHeader('Content-Type'));//获取响应头
// res.removeHeader('Content-Type');//删除响应头
// console.log('getHeader2', res.getHeader('Content-Type'));//获取响应头
// res.write('hello');
// res.write('world');
// res.end();
//res.write('byebye');//write after end 在可写流结束之后再次写入
});
server.on('connection', function (socket) {
console.log('connection');
socket.on('end', function () {
console.log('连接end');
});
socket.on('close', function () {
console.log('连接close');
});
});
server.listen(8080);
writeHead一旦调用会立刻向客户端发送,setHeader.
当调用writeHead 或者调用write方法的时候才会向客户端发响应头,200是HTTP状态码,表示成功处理了此次请求, 当然也可以设置不同的响应码,用statusCode, response.end()表示的就是响应事件传输数据结束。
下一节有时间我将浅谈TCP/IP,关于TCP数据包封装、TCP的三次握手、TCP的四次挥手、TCP和UDP区别等,感谢你的关注,喜欢记得点赞哈