上述 TCP 的数据结构图对于后面理解 HTTP 的交互流程非常重要,我们要记住 5 个关键的位置:
SYN:建立连接标识 ACK:响应标识 FIN:断开连接标识 seq:seq number,发送序号 ack:ack number,响应序号
服务端应用启动后,会在指定端口监听客户端的连接请求,当客户端尝试创建一个到服务端指定端口的 TCP 连接,服务端收到请求后接受数据并处理完业务后,会向客户端作出响应,客户端收到响应后接受响应数据,然后断开连接,一个完整的请求流程就完成了。这样的一个完整的 TCP 的生命周期会经历以下 4 个步骤:
1,建立 TCP 连接,3 次握手
客户端发送SYN, seq=x,进入 SYN_SEND 状态
服务端回应SYN, ACK, seq=y, ack=x 1,进入 SYN_RCVD 状态
客户端回应ACK, seq=x 1, ack=y 1,进入 ESTABLISHED 状态,服务端收到后进入 ESTABLISHED 状态 2,进行数据传输
客户端发送ACK, seq=x 1, ack=y 1, len=m
服务端回应ACK, seq=y 1, ack=x m 1, len=n
客户端回应ACK, seq=x m 1, ack=y n 1
3,断开 TCP 连接, 4 次挥手
主机 A 发送FIN, ACK, seq=x m 1, ack=y n 1,进入 FNI_WAIT_1 状态
主机 B 回应ACK, seq=y n 1, ack=x m 1,进入 close_WAIT 状态,主机 A 收到后 进入 FIN_WAIT_2 状态
主机 B 发送FIN, ACK, seq=y n 1, ack=x m 1,进入 LAST_ACK 状态
主机 A 回应ACk, seq=x m 1, ack=y n 1,进入 TIME_WAIT 状态,等待主机 B 可能要求重传 ACK 包,主机 B 收到后关闭连接,进入 CLOSED 状态或者要求主机 A 重传 ACK,客户端在一定的时间内没收到主机 B 重传 ACK 包的要求后,断开连接进入 CLOSED 状态
为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假设网络是不可靠的,一切都可能发生,比如有可能最后一个ACK丢失。所以TIME_WAIT状态是用来重发可能丢失的ACK报文。
客户端与服务端建立连接、传输数据和断开连接等全靠这几个标识,比如 SYN 也可以被用来作为 DOS 攻击的一个手段,FIN 可以用来扫描服务端指定端口。
HTTP 的数据结构Socket 是 TCP/IP 的可编程 API,HTTP 的可编程 API 的实现要依赖 Socket。HTTP 是超文本传输协议,HTTP 的头和数据看起来更加直观,在大多数情况下,它们都是字符或者字符串,所以对于大多数人来说理解 HTTP 的头和数据格式显得很简单。确实,HTTP 的数据格式理解起来非常容易,上部分是头,下部分是身体。
HTTP 的请求时的数据结构和响应时的数据结构整体上是一样的,但是有一些细微的区别,我们先来看一下 HTTP 请求时的数据结构:
HTTP 响应时的数据结构:
现在我们使用谷歌浏览器请求某度,按下F12,来对比理解上述结构图,下面是请求某度