浏览器——HTTP协议

HTTP协议

  • 超文本传输协议,可以传递文本
  • 处理客户端和服务端的通信
  • http请求/http返回
  • 返回一些网页,json,xml,提交表单

纯问文本,无状态

  • 应用层用TCP/IP
  • 信息是纯文本传输
  • 无状态,每次请求独立,请求间互不影响
  • 浏览器提供了手段维护状态(Cookie,Session,Storage)

简单发展历史

  • 1990 HTTP 0.9
  • 1996 HTTP 1.0
  • 1999 HTTP 1.1
  • 2015 HTTP 2.0

设备的基本因素

  • 宽带:基础网络(线路,设备),网页大的话还是要考虑带宽的,不过现在带宽都比较大了。
  • 延迟:浏览器、DNS查询、建立连接(TCP三次握手),这些都是消耗时间的

设计考虑因素

  • 缓存
    • http1.0提供缓存机制IF-Modified-Since等基础缓存机制
    • http1.1提供E-Tag等高级缓存策略
  • 宽带优化
    • http1.1利用rang头获取文件的某个部分
    • http1.1利用长连接让多个请求在一个TCP连接上排队
    • http2.0利用多路复用技术同时传输多个请求
  • 压缩
    • 主流web服务器如ngix/express等都提供gzip压缩功能
    • http2.0采用二进制传输,头部使用HPACK算法压缩
  • HTTPS
    • 在HTTP和TCP/IP之间增加TSL/SSL层
    • 数据传输加密(非对称+对称加密)

HTTPS 安全超文本传输协议

  • 数据加密传输
    • 防止各种攻击手段(信息泄漏、篡改等)
  • SSL/TSL
    • SSL-安全套接层
    • TSL-传输层安全性协议
    • 需要在客户端安装证书

介绍一些常用的工具

命令行输入 curl https://www.baidu.com

装一个switchOmega插件,可以在chrome商店直接购买

https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif/related

简单配置一下:

选择一下代理: 

postman https://www.postman.com/downloads/

whistle 跨平台网络调试工具

安装:npm install whistle -g

启动:whistle start

访问:http://127.0.0.1:8899/

HTTP常见请求头

GET- 从服务器获取资源,

POST-在服务器创建资源,调用10次post会创建10次资源

PUT-在服务器修改资源(幂等性)调用10次同样的请求,在服务端产生的影响是一致的,如:修改订单的状态。

DELETE-在服务器删除资源

OPTION-跨域部分

TRACE-用于显示调试信息(多数网站不支持)

CONNECT-代理

PATCH-对于资源进行部分更新

常见的HTTP状态码

1XX: 提供信息

  • 100continue 客户端要往服务端发送数据,会询问下服务端,服务端如果发100 continue就继续往服务端发
  • 101 切换协议 如何说需要切换协议,会返回101,告诉浏览器切换协议

2XX 成功

  • 200-ok
  • 201-Created 已创建
  • 202 已接收,需要做轮询,创建订单,在服务端有一定的消耗,先返回一个202,需要等待结果。
  • 203 非权威内容,原始服务器的内容中间被修改过
  • 204 No Content 没有内容,像put请求可能会有,如:put请求可能会返回这样的
  • 205 Reset Content是服务端希望你重置一下内容,比如说提交表单。
  • 206 Partial Content 服务端只返回了本分内容 

3XX 重定向

  • 300 Multiple Choices 用户请求了多个选项资源(返回选项列表)
  • 301 永久转移
  • 302 Found 资源被找到(以前是临时转移)
  • 303 See Other 可以使用get方法在另一个URL找到资源
  • 304 Not Modified 
  • 305 Use Proxy 比如一些内容用的 外网没有访问权限
  • 307 Temporary Redirect 临时重定向 告诉客户端使用原请求的method重定向资源
  • 308 Permant Redirect 永久重定向 告诉客户端使用原请求的method重定向资源

301和308的区别 

共同点:资源被永久移动到新地址

差异:308是原来是什么method,那么之后会延用这个method到新地址;客户端收到301请求后,通常用户会向新地址发起GET请求

302/303/307的区别 

共同点:资源临时放到新地址

差异:

302是http1.0提出的,很多浏览器都没有遵照标准,把所有请求都重定向为GET

303 告诉客户端使用GET方法重定向资源

307告诉客户端使用原请求的method重定向资源

4XX 客户端错误

  • 400 Bad Request 请求格式错误
  • 401 Unauthorization没有授权
  • 402 Payment Required 请先付费
  • 403 Forbidden禁止访问
  • 404 Not Found 没有找到
  • 405 Method Not Allowed
  • 405 Not Acceptable 服务端可以提供的内容和客户端期待的不一样

5XX 服务端错误

500 Internal Server Error 内部服务器错误

501 Not Implemented 接口没有实现 

502 Bad Gateway 网关错误

503 Service Unavalibale 服务不可用 服务正在启动,内存用光了等等

504 Gateway Timeout 网关超时 

505 HTTP Version Not Supported 版本不支持 有的支持1.0 

Content-length

发送给接收者的Body内容长度(字节),一个byte是8bit,utf-8编码的字符1-4个字节

User-Agent

帮助区分客户端特性的字符串,操作系统,浏览器,制造商,内核类型,版本号等等

 Content-Type

帮助区分资源的媒体类型

text/html text/css application/json image/jpeg

Origin 描述请求来源地址

不含路径,可以是null scheme://host:port

Accept

代表发送端(客户端)希望接收的数据类型

*/*代表所有类型(默认)

多个类型用逗号隔开例如:text/html,application/json

Accept-Encoding: 建议服务端发送哪种编码

Accept-Language: 建议服务端传递哪种语言

Referer

告诉服务端打开当前页面的上一张页面的URL;如果是ajax请求,那么就告诉服务端发送请求的URL是什么。

Connection

决定连接是否在当前事物完成后关闭

Http1.0默认是close

Http1.1后默认是keep-alive

启动一个后端服务:

const express = require('express')

const app = express();

app.get('/greetings', (req, response) => {
  response.send('hellov')
})
app.post('/create', (req, response) => {
  response.status(201).send()
})
app.listen(3000)

访问:http://localhost:3000/greetings 

第一次访问会返回200 第二次访问是304

控制台fetch('/create', {method: 'POST'})

app.post('/create', (req, response) => {
  response.status(201).send(JSON.stringify({name: '麦乐'}))
})

但是这样是有问题的,返回内容的格式不对:

需要在这里设置一下格式: 

app.post('/create', (req, response) => {
  response.set('content-type', 'application/json')
  response.status(201).send(JSON.stringify({name: '麦乐'}))
})

传递内容:

fetch('/create', {method: 'POST', body: JSON.stringify({name: 'apple'})})

但是这么传递也是有问题的,服务端不知道我们传递的是json格式:

还需要设置一下请求头:

fetch('/create', {method: 'POST',headers: {'content-type': 'application/json'}, body: JSON.stringify({name: 'apple'})})

传递一个比较大的数据过去,看下面结果打印了三次,说明数据不是一次性传递完的。

fetch('/create', {method: 'POST', body: JSON.stringify({name: ''.padStart(100000, 'a')})})
  req.on('data', function(buffer) {
    console.log(buffer.toString('utf-8').length)
  })

可以通过下面的形式拿到所有的数据: 

app.post('/create', (req, response) => {
  let text = ''
  req.on('data', function(buffer) {
    text += buffer.toString('utf-8');
    console.log(buffer.toString('utf-8').length)
  })
  req.on('end', (error) => {
    console.log(text)
  })
  response.set('content-type', 'application/json')
  response.status(201).send(JSON.stringify({name: '麦乐'}))

})

访问301,自动跳转到def 

app.get('/301', (req, response) => {
  response.redirect(301, '/def')
  response.send('301')
})

app.get('/def', (req, response) => {
  response.send('def')
})

再次访问301,走了缓存

试一下307,307是post,def是get,这个时候重定向到def会怎样?会找不到资源:

app.post('/307', (req, response) => {
  response.redirect(307, '/def')
  response.send('307')
})

app.get('/def', (req, response) => {
  response.send('def')
})

把def改成post,再发请求,重定向成功

app.post('/def', (req, response) => {
  response.send('def')
})

500  

app.get('/abc', function() {
  throw 'error'
})

加密和HTTPS证书

SSL 安全套接字(Secure Socket Layer,SSL)协议是Web浏览器与Web服务器之间安全交换信息的协议,提供两个基本的安全服务:鉴别与保密。

TLS 是SSL的标准化后的产物

什么是加密?

就是将明文 信息变成不可读的秘文内容,只有拥有解密方法的对象才能将密文还原成加密前的内容。

在计算机中加密解密方法,可以描述为一段程序,我们称作加密/解密算法。

加密有时会对暗号,比如上面例子中每次跳过3个字符,3就是一个暗号,这个我们称作秘钥

加密和解密的暗号(秘钥)相同,我们称作对称加密。

加密和解密的暗号(秘钥)不通,我们称为非对称加密。

非对称加密:

创建者会同时创建2把钥匙,一把是私钥(自己持有),一把是公钥(外界公开)。用公钥加密只有私钥能解密,用私钥加密只有公钥能解密。

有一个公认的认证体系

非对称加密安全性更好,对称加密计算速度更快,通常会混合使用(利用非对称加密协商秘钥,然后进行对称加密)

https建立链接到工作的过程:

UDP 

不需要建立连接

封包比较小,传递速度比较快

传递的数据不保证包不丢失,丢失不重发

不保证顺序

自由度比较高,以上的方法可以自己去实现

面向消息

使用场景:DNS查询

TCP

面向流

使用场景:远程控制

HTTP2.0

多个请求多路复用:HTTP1.1可以实现一个TCP连接请求多个文件,但是文件都是排队发出,排队发回,如果一个文件时间较长,后面文件就会堵塞在等待中。HTTP2.0使用了多路复用,一个TCP连接上把多个请求打包发出,把所有信息切成小片,返回的时间是这些文件中时间最长的那一个。如果其中一个阻塞了,其它的文件不受影响。

防止队头阻塞

压缩HTTP头部

HPACk技术,编码代码内容

服务端推送

比如说请求一个index.html,如果没有服务端推送,会先返回index.htm,再等客户端请求,a.js和c.css文件,返回a.js和c.css文件,然后页面开始渲染。有服务端图推送的话,服务端会把a.js和从c.css文件随index.html一起返回,但是这样会引起其它的问题,加入一个index.html文件中有20个js文件和150个图片,服务端是不知道应该先发哪一个。

HTTP3.0

先用一张图来表示:

HTTP3.0使用UDP,使用QUIC层解决UDP的不足。

TCP connection

客户端与服务器之间数据的发送和返回的过程当中需要创建一个叫TCP connection的东西;

由于TCP不存在连接的概念,只存在请求和响应,请求和响应都是数据包,它们之间都是经过由TCP创建的一个从客户端发起,服务器接收的类似连接的通道,这个连接可以一直保持,http请求是在这个连接的基础上发送的;

在一个TCP连接上是可以发送多个http请求的,不同的版本这个模式不一样。

在HTTP/1.0中这个TCP连接是在http请求创建的时候同步创建的,http请求发送到服务器端,服务器端响应了之后,这个TCP连接就关闭了;

HTTP/1.1中可以以某种方式声明这个连接一直保持,一个请求传输完之后,另一个请求可以接着传输。这样的好处是:在创建一个TCP连接的过程中需要“三次握手”的消耗,“三次握手”代表有三次网络传输。

如果TCP连接保持,第二个请求发送就没有这“三次握手”的消耗。HTTP/2中同一个TCP连接里还可以并发地传输http请求。

TCP报文格式简介

其中比较重要的字段有:

(1)序号(sequence number):Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

(2)确认号(acknowledgement number):Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。

(3)标志位(Flags):共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:

URG:紧急指针(urgent pointer)有效。

ACK:确认序号有效。

PSH:接收方应该尽快将这个报文交给应用层。

RST:重置连接。

SYN:发起一个新连接。

FIN:释放一个连接。

需要注意的是:

不要将确认序号Ack与标志位中的ACK搞混了。确认方Ack=发起方Seq+1,两端配对。

TCP的三次握手(Three-Way Handshake)

1.”三次握手”的详解

所谓的三次握手即TCP连接的建立。这个连接必须是一方主动打开,另一方被动打开的。以下为客户端主动发起连接的图解:

握手之前主动打开连接的客户端结束CLOSED阶段,被动打开的服务器端也结束CLOSED阶段,并进入LISTEN阶段。随后开始“三次握手”:

(1)首先客户端向服务器端发送一段TCP报文,其中:

标记位为SYN,表示“请求建立新连接”;序号为Seq=X(X一般为1);随后客户端进入SYN-SENT阶段。(2)服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,其中:

标志位为SYN和ACK,表示“确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接”(即告诉客户端,服务器收到了你的数据);序号为Seq=y;确认号为Ack=x+1,表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值;随后服务器端进入SYN-RCVD阶段。(3)客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:

标志位为ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了);序号为Seq=x+1,表示收到服务器端的确认号Ack,并将其值作为自己的序号值;确认号为Ack=y+1,表示收到服务器端序号Seq,并将其值加1作为自己的确认号Ack的值;随后客户端进入ESTABLISHED阶段。服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。

在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。

此后客户端和服务器端进行正常的数据传输。这就是“三次握手”的过程。

2.“三次握手”的动态过程

3.“三次握手”的通俗理解

举个栗子:把客户端比作男孩,服务器比作女孩。用他们的交往来说明“三次握手”过程:

(1)男孩喜欢女孩,于是写了一封信告诉女孩:我爱你,请和我交往吧!;写完信之后,男孩焦急地等待,因为不知道信能否顺利传达给女孩。

(2)女孩收到男孩的情书后,心花怒放,原来我们是两情相悦呀!于是给男孩写了一封回信:我收到你的情书了,也明白了你的心意,其实,我也喜欢你!我愿意和你交往!;

写完信之后,女孩也焦急地等待,因为不知道回信能否能顺利传达给男孩。

(3)男孩收到回信之后很开心,因为发出的情书女孩收到了,并且从回信中知道了女孩喜欢自己,并且愿意和自己交往。然后男孩又写了一封信告诉女孩:你的心意和信我都收到了,谢谢你,还有我爱你!

女孩收到男孩的回信之后,也很开心,因为发出的情书男孩收到了。由此男孩女孩双方都知道了彼此的心意,之后就快乐地交流起来了~~

这就是通俗版的“三次握手”,期间一共往来了三封信也就是“三次握手”,以此确认两个方向上的数据传输通道是否正常。

4.为什么要进行第三次握手?

为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

由于网络传输是有延时的(要通过网络光纤和各种中间代理服务器),在传输的过程中,比如客户端发起了SYN=1创建连接的请求(第一次握手)。

如果服务器端就直接创建了这个连接并返回包含SYN、ACK和Seq等内容的数据包给客户端,这个数据包因为网络传输的原因丢失了,丢失之后客户端就一直没有接收到服务器返回的数据包。

客户端可能设置了一个超时时间,时间到了就关闭了连接创建的请求。再重新发出创建连接的请求,而服务器端是不知道的,如果没有第三次握手告诉服务器端客户端收的到服务器端传输的数据的话,

服务器端是不知道客户端有没有接收到服务器端返回的信息的。

这个过程可理解为:

这样没有给服务器端一个创建还是关闭连接端口的请求,服务器端的端口就一直开着,等到客户端因超时重新发出请求时,服务器就会重新开启一个端口连接。那么服务器端上没有接收到请求数据的上一个端口就一直开着,长此以往,这样的端口多了,就会造成服务器端开销的严重浪费。

还有一种情况是已经失效的客户端发出的请求信息,由于某种原因传输到了服务器端,服务器端以为是客户端发出的有效请求,接收后产生错误。

所以我们需要“第三次握手”来确认这个过程,让客户端和服务器端能够及时地察觉到因为网络等一些问题导致的连接创建失败,这样服务器端的端口就可以关闭了不用一直等待。

也可以这样理解:“第三次握手”是客户端向服务器端发送数据,这个数据就是要告诉服务器,客户端有没有收到服务器“第二次握手”时传过去的数据。若发送的这个数据是“收到了”的信息,接收后服务器就正常建立TCP连接,否则建立TCP连接失败,服务器关闭连接端口。由此减少服务器开销和接收到失效请求发生的错误。

5.抓包验证

下面是用抓包工具抓到的一些数据包,可用来分析TCP的三次握手:

图中显示的就是完整的TCP连接的”三次握手”过程。在52528 -> 80中,52528是本地(客户端)端口,80是服务器的端口。80端口和52528端口之间的三次来回就是"三次握手"过程。

注意到”第一次握手”客户端发送的TCP报文中以[SYN]作为标志位,并且客户端序号Seq=0;

接下来”第二次握手”服务器返回的TCP报文中以[SYN,ACK]作为标志位;并且服务器端序号Seq=0;确认号Ack=1(“第一次握手”中客户端序号Seq的值+1);

最后”第三次握手”客户端再向服务器端发送的TCP报文中以[ACK]作为标志位;

其中客户端序号Seq=1(“第二次握手”中服务器端确认号Ack的值);确认号Ack=1(“第二次握手”中服务器端序号Seq的值+1)。

这就完成了”三次握手”的过程,符合前面分析的结果。

TCP的四次挥手(Four-Way Wavehand)

1、前言

对于"三次握手"我们耳熟能详,因为其相对的简单。但是,我们却不常听见“四次挥手”,就算听过也未必能详细地说明白它的具体过程。下面就为大家详尽,直观,完整地介绍“四次挥手”的过程。

2、“四次挥手”的详解

所谓的四次挥手即TCP连接的释放(解除)。连接的释放必须是一方主动释放,另一方被动释放。以下为客户端主动发起释放连接的图解:

挥手之前主动释放连接的客户端结束ESTABLISHED阶段。随后开始“四次挥手”:

(1)首先客户端想要释放连接,向服务器端发送一段TCP报文,其中:

标记位为FIN,表示“请求释放连接“;序号为Seq=U;随后客户端进入FIN-WAIT-1阶段,即半关闭阶段。并且停止在客户端到服务器端方向上发送数据,但是客户端仍然能接收从服务器端传输过来的数据。注意:这里不发送的是正常连接时传输的数据(非确认报文),而不是一切数据,所以客户端仍然能发送ACK确认报文。

(2)服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,随后服务器端结束ESTABLISHED阶段,进入CLOSE-WAIT阶段(半关闭状态)并返回一段TCP报文,其中:

标记位为ACK,表示“接收到客户端发送的释放连接的请求”;序号为Seq=V;确认号为Ack=U+1,表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值;随后服务器端开始准备释放服务器端到客户端方向上的连接。客户端收到从服务器端发出的TCP报文之后,确认了服务器收到了客户端发出的释放连接请求,随后客户端结束FIN-WAIT-1阶段,进入FIN-WAIT-2阶段

前"两次挥手"既让服务器端知道了客户端想要释放连接,也让客户端知道了服务器端了解了自己想要释放连接的请求。于是,可以确认关闭客户端到服务器端方向上的连接了

(3)服务器端自从发出ACK确认报文之后,经过CLOSED-WAIT阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文,其中:

标记位为FIN,ACK,表示“已经准备好释放连接了”。注意:这里的ACK并不是确认收到服务器端报文的确认报文。序号为Seq=W;确认号为Ack=U+1;表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值。随后服务器端结束CLOSE-WAIT阶段,进入LAST-ACK阶段。并且停止在服务器端到客户端的方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。

(4)客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文,其中:

标记位为ACK,表示“接收到服务器准备好释放连接的信号”。序号为Seq=U+1;表示是在收到了服务器端报文的基础上,将其确认号Ack值作为本段报文序号的值。确认号为Ack=W+1;表示是在收到了服务器端报文的基础上,将其序号Seq值作为本段报文确认号的值。随后客户端开始在TIME-WAIT阶段等待2MSL

为什么要客户端要等待2MSL呢?见后文。

服务器端收到从客户端发出的TCP报文之后结束LAST-ACK阶段,进入CLOSED阶段。由此正式确认关闭服务器端到客户端方向上的连接。

客户端等待完2MSL之后,结束TIME-WAIT阶段,进入CLOSED阶段,由此完成“四次挥手”。

后“两次挥手”既让客户端知道了服务器端准备好释放连接了,也让服务器端知道了客户端了解了自己准备好释放连接了。于是,可以确认关闭服务器端到客户端方向上的连接了,由此完成“四次挥手”。

与“三次挥手”一样,在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性,一旦出现某一方发出的TCP报文丢失,便无法继续"挥手",以此确保了"四次挥手"的顺利完成。

3、“四次挥手”的通俗理解

举个栗子:把客户端比作男孩,服务器比作女孩。通过他们的分手来说明“四次挥手”过程。

"第一次挥手":日久见人心,男孩发现女孩变成了自己讨厌的样子,忍无可忍,于是决定分手,随即写了一封信告诉女孩。“第二次挥手”:女孩收到信之后,知道了男孩要和自己分手,怒火中烧,心中暗骂:你算什么东西,当初你可不是这个样子的!于是立马给男孩写了一封回信:分手就分手,给我点时间,我要把你的东西整理好,全部还给你!男孩收到女孩的第一封信之后,明白了女孩知道自己要和她分手。随后等待女孩把自己的东西收拾好。“第三次挥手”:过了几天,女孩把男孩送的东西都整理好了,于是再次写信给男孩:你的东西我整理好了,快把它们拿走,从此你我恩断义绝!“第四次挥手”:男孩收到女孩第二封信之后,知道了女孩收拾好东西了,可以正式分手了,于是再次写信告诉女孩:我知道了,这就去拿回来!这里双方都有各自的坚持。女孩自发出第二封信开始,限定一天内收不到男孩回信,就会再发一封信催促男孩来取东西!男孩自发出第二封信开始,限定两天内没有再次收到女孩的信就认为,女孩收到了自己的第二封信;若两天内再次收到女孩的来信,就认为自己的第二封信女孩没收到,需要再写一封信,再等两天…..

倘若双方信都能正常收到,最少只用四封信就能彻底分手!这就是“四次挥手”。

4.为什么“握手”是三次,“挥手”却要四次?

TCP建立连接时之所以只需要"三次握手",是因为在第二次"握手"过程中,服务器端发送给客户端的TCP报文是以SYN与ACK作为标志位的。SYN是请求连接标志,表示服务器端同意建立连接;ACK是确认报文,表示告诉客户端,服务器端收到了它的请求报文。

即SYN建立连接报文与ACK确认接收报文是在同一次"握手"当中传输的,所以"三次握手"不多也不少,正好让双方明确彼此信息互通。

TCP释放连接时之所以需要“四次挥手”,是因为FIN释放连接报文与ACK确认接收报文是分别由第二次和第三次"握手"传输的。为何建立连接时一起传输,释放连接时却要分开传输?

建立连接时,被动方服务器端结束CLOSED阶段进入“握手”阶段并不需要任何准备,可以直接返回SYN和ACK报文,开始建立连接。释放连接时,被动方服务器,突然收到主动方客户端释放连接的请求时并不能立即释放连接,因为还有必要的数据需要处理,所以服务器先返回ACK确认收到报文,经过CLOSE-WAIT阶段准备好释放连接之后,才能返回FIN释放连接报文。

所以是“三次握手”,“四次挥手”。

5.为什么客户端在TIME-WAIT阶段要等2MSL?

为的是确认服务器端是否收到客户端发出的ACK确认报文

当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。

服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文;

如果客户端在2MSL内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的ACK确认报文。客户端再次向服务器端发出ACK确认报文,计时器重置,重新开始2MSL的计时;否则客户端在2MSL内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。

所以,客户端要经历时长为2SML的TIME-WAIT阶段;这也是为什么客户端比服务器端晚进入CLOSED阶段的原因

6.抓包验证

图中显示的就是完整的TCP连接释放的”四次挥手”过程。在 80 -> 55389 中,假设80是本地(客户端)端口,55389是服务器端口。80端口与55389之间的四次来回就是"四次挥手"过程。

”第一次挥手”客户端发送的FIN请求释放连接报文以[FIN,ACK]作为标志位,其中报文序号Seq=2445;确认号Ack=558;注意:这里与“第三次握手”的ACK并不是表示确认的ACK报文。”第二次挥手”服务器端返回的ACK确认报文以[ACK]作为标志位;其中报文序号Seq=558;确认号Ack=2246;”第三次挥手”服务器端继续返回的FIN同意释放连接报文以[FIN,ACK]作为标志位;其中报文序号Seq=558;确认号Ack=2246;”第四次挥手”客户端发出的ACK确认接收报文以[ACK]作为标志位;其中报文序号Seq=2446;确认号Ack=559。后一次“挥手”传输报文中的序号Seq值等于前一次"握手"传输报文中的确认号Ack值;后一次“挥手”传输报文中的确认号Ack值等于前一次"握手"传输报文中的序号Seq值;

故这是连续的“四次挥手”过程,与前面的分析相符。

原文:https://www.cnblogs.com/AhuntSun-blog/p/12028636.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值