- 在客户端和服务器进行一个http请求的时候, 在发送和返回的过程中需要建立一个TCP connection的东西
- 在HTTP中是不存在连接这样的概念,只有请求和响应,这些都是数据包,它们需要经过一个传输的通道
- 这个通道就是在TCP中创建的这么一个连接, 这个连接是可以一直保持的, HTTP请求是在这个连接的基础上去发送的
- 在TCP连接上可以发送多个HTTP请求,在不同版本中这个模式是不一样的
- 在1.0版本,这个连接是在一个HTTP请求创建的时候就创建了一个TCP连接,响应之后,连接立即关闭
- 在1.1版本,这个连接通过某种方式让连接一直保持,在一个请求响应之后不关闭,后续可以继续使用
- 保持长连接的好处是减少三次握手的开销,三次握手代表有三次网络传输:客户端发送,服务端返回,客户端再发送
- 通过三次握手创建了一个TCP连接,之后才能发送HTTP请求
- 如果保持了TCP连接,第二次HTTP请求就不会有三次握手的开销
- 在2版本中, TCP连接中的HTTP请求是可以并发的, 同一个用户对同一个服务器发起一个网络请求的时候, 只需要一个TCP连接
- HTTP的三次握手
- 1 ) 首先客户端要发起一个我要创建连接的一个数据包的请求到服务端
- 这里面有一个标志位:SYN=1,表示这是一个创建请求的数据包,SYN=1表示SYN占据了第一个标志位,下同
- 后面会有一个Seq=X,X是一个数字
- 2 ) 服务端接收到这个数据包之后, 就知道了,我有一个客户要和我创建连接了
- 之后就会开启一个TCP的socket的端口, 之后返回给客户端:SYN=1,ACK=X+1,Seq=Y 这些信息
- 3 ) 客户端接收到这些信息后,再发送给服务端:ACK=Y+1,Seq=Z
- 为什么要设计出这么一个三次握手的过程
- 防止服务端开启一些无用的连接,因为网络传输是有延迟的,传输距离远,而且可能有很多代理服务器
- 在传输过程中,客户端发起了SYN=1创建连接的请求,服务端直接创建了连接并直接响应给客户端
- 由于网络的不确定性,数据包丢失了,客户端一直没有接受到服务器的响应, 客户端可能因为超时而关闭
- 这样客户端会发起另一个创建连接的请求, 如果没有三次握手机制,服务端是不知道的, 前一个连接一直开着因此被浪费
- 所以需要三次握手来确认这个过程让客户端和服务端能够及时察觉到�因网络原因导致通信失败和资源浪费
- 抓包工具推荐:Wireshark
- Wireshark是一个非常好用的抓包工具
- 不仅能分析HTTP层面的,而且可以深入传输层和网络层
URI
- URI:Uniform Resource Identifier 统一资源标志符
- URL是用来定义一个web网站具体的某个页面,但是从HTTP和Web的角度它的定义不仅仅只是如此
- 在Web中不管是HTTP协议还是FTP协议,它们的目的就是为了找到某一个资源
- URI就是用来唯一标识互联网上的信息资源而设置的,是一个包含URL和URN的一个统一定义
URL
- URL: Uniform Resource Locator 统一资源定位器
- 示例:http://user:pass@host.com:80/path?query=string#hash
http://
这是scheme,定义用怎样的协议来访问资源- 这里的scheme有很多,比如ftp,mailto,https,ws
- 这些协议的服务方式不一样, 通过不同的协议访问服务,其解析的方式就会不一样
user:pass@
表示所访问资源需要特殊的身份权限,在web开发中基本不会这样使用, 不安全且麻烦host.com
用于定位资源所在的服务器在互联网中的位置- 定位可以是ip, 也可以是域名, 如果是域名要解析成ip才能定位到服务
:80
端口,每台服务器都有很多端口, 可以跑很多软件的web服务, 80端口是默认端口,一个端口代表一个web服务/path
: 路由,通过路由找到所需的内容, 同文件路径(面包屑), 更多时候用于辨别url所要请求的数据通过程序来判断而非直接请求的资源query=string
搜索参数#hash
用于定位某一个片段,也就是锚点,定位的工具,当然一些框架的hash会充当一部分路由功能
- 这类格式都叫做URL,如ftp协议也是
URN
- URN 永久统一资源定位符
- 一般URL所对应的资源如果被迁移了,那么很可能这个URL就会失效
- URN解决了这个问题,如果资源被迁移了,可以通过URN访问到对应的资源,即在资源移动后还能被找到
- 但是在目前没有一个成熟的使用方案和使用场景
- 客户端主动发起请求后,服务端才会给我们响应
- 格式
- a) 请求报文
- 起始行:GET /test/hi-there.txt HTTP/1.0
- 包含一个method, method有各自的语义, 可以不遵守,但最好遵守
- 包含一个url, 比如路由什么的
- 包含一个协议和版本
- 首部:Accept: text/* , Accept-Language: en, fr
- 起始行:GET /test/hi-there.txt HTTP/1.0
- b) 响应报文
- 起始行:HTTP/1.0 200 OK
- 首部:Content-type: text/plain, Content-length: 19
- 主体:Hi! I'm response body!
- a) 请求报文
- 注意: 格式中起始行和首部是分开的,并且首部和主体的区分是中间有一个空行
- 用于定义对资源的操作
- 常用的: Get, Post, Delete, Put
- 不常用的:Patch, Head等
- 同一个URL针对不同的Method来进行区分不同的功能,这样API接口的设计会更规范
- 从定义上有各自的语义,如何实现取决于开发者
- 定义服务器对请求的处理结果,这个结果是处理成功的,还是重定向的,还是请求或响应存在问题等
- 各个区间的code有各自的含义
- 100 ~ 199 代表操作要持续进行,接下去要去做一些其他的事情,请求才能继续返回给你
- 200 ~ 299 代表操作是成功的
- 300 ~ 399 代表操作要去做重定向,用别的方式去获取数据, 如:301,302永久临时重定向等
- 400 ~ 499 代表发送请求有问题,如:401权限认证问题
- 500 ~ 599 代表服务器出现的错误
- 一个好的HTTP服务是可以通过CODE来判断结果的
- 实现了一个非常好的HTTP服务是可以很直观的从CODE中看到的
- 现在很多国内WEB开发人员只用两种200和500和一些说明的数据,这种其实不是很规范
- 要尽量遵照HTTP的语义化来开发
- 使用nodejs来实现一个最简单的HTTP服务
// server.js
const http = require('http');
http.createServer((req, res) => {
console.log('req come: ', req.url);
res.end('req come: ' + req.url);
}).listen(8000,() => {
console.log('server is running');
});