TCP协议的3次握手与4次挥手过程【深度详解】

一、前沿

  尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务。TCP提供一种面向连接的、可靠的字节流服务。面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据之前必须先建立一个TCP连接。这一过程与打电话很相似,先拨号振铃,等待对方摘机说“喂”,然后才说明是谁。

  本文将分别讲解经典的TCP协议建立连接(所谓的“3次握手”)和断开连接(所谓的“4次挥手”)的过程。

  更多资料请查阅《TCP/IP 详解》这本书,地址是:http://www.52im.net/topic-tcpipvol1.html

二、TCP报文格式

TCP/IP协议的详细信息参看《TCP/IP 协议详解》中有关TCP格式的章节(点此查看《TCP/IP详解 在线版》)。

下面是TCP报文格式图:

上图中有几个字段需要重点介绍下:

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

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

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

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

  (B)ACK:确认序号有效。
       (C)PSH:接收方应该尽快将这个报文交给应用层。
       (D)RST:重置连接。
       (E)SYN:发起一个新连接。
       (F)FIN:释放一个连接。


从输入域名到最后呈现经历的过程:

 

域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户 --> 四次挥手结束

 

 

1.域名解析

 

首先浏览器会解析 www.zipackage.com 这个域名对应的IP地址。

 

① 浏览器 会首先搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存),看自身的缓存中是否有www.zipackage.com 对应的条目,而且没有过期,如果有且没有过期则解析到此结束。

 

② 如果浏览器自身的缓存里面没有找到对应的条目,那么浏览器会搜索操作系统自身的DNS缓存,如果找到且没有过期则停止搜索解析到此结束.

 

③ 如果在Windows系统的DNS缓存也没有找到,那么尝试读取hosts文件(位于C:\Windows\System32\drivers\etc),看看这里面有没有该域名对应的IP地址,如果有则解析成功。

 

④ 如果在hosts文件中也没有找到对应的条目,浏览器就会发起一个DNS的系统调用,就会向本地配置的首选DNS服务器(一般是电信运营商提供的)发起域名解析请求(通过的是UDP协议向DNS的53端口发起请求,这个请求是递归的请求,也就是运营商的DNS服务器必须得提供给我们该域名的IP地址),运营商的DNS服务器首先查找自身的缓存,找到对应的条目,且没有过期,则解析成功。如果没有找到对应的条目,则有运营商的DNS代我们的浏览器发起迭代DNS解析请求,它首先是会找根域的DNS的IP地址(这个DNS服务器都内置13台根域的DNS的IP地址),找到根域的DNS地址,就会向其发起请求(请问www.zipackage.com这个域名的IP地址是多少啊?),根域发现这是一个顶级域com域的一个域名,于是就告诉运营商的DNS我不知道这个域名的IP地址,但是我知道com域的IP地址,你去找它去,于是运营商的DNS就得到了com域的IP地址,又向com域的IP地址发起了请求(请问www.zipackage.com这个域名的IP地址是多少?),com域这台服务器告诉运营商的DNS我不知道www.zipackage.com这个域名的IP地址,但是我知道zipackage.com这个域的DNS地址,你去找它去,于是运营商的DNS又向zipackage.com这个域名的DNS地址(这个一般就是由域名注册商提供的,像万网,新网等)发起请求(请问www.zipackage.com这个域名的IP地址是多少?),这个时候zipackage.com域的DNS服务器一查,诶,果真在我这里,于是就把找到的结果发送给运营商的DNS服务器,这个时候运营商的DNS服务器就拿到了www.zipackage.com这个域名对应的IP地址,并返回给Windows系统内核,内核又把结果返回给浏览器,终于浏览器拿到了www.zipackage.com 对应的IP地址,该进行一步的动作了。

 

2.发起TCP的3次握手

 

拿到域名对应的IP地址之后,User-Agent(一般是指浏览器)会以一个随机端口(1024 < 端口 < 65535)向服务器的WEB程序(常用的有httpd,nginx等)80端口发起TCP的连接请求。这个连接请求(原始的http请求经过TCP/IP4层模型的层层封包)到达服务器端后(这中间通过各种路由设备,局域网内除外),进入到网卡,然后是进入到内核的TCP/IP协议栈(用于识别该连接请求,解封包,一层一层的剥开),还有可能要经过Netfilter防火墙(属于内核的模块)的过滤,最终到达WEB程序。

 

 

1) Client首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示Client自己的初始序号(seq = 0 就代表这是第0号帧),这时候Client进入syn_sent状态,表示客户端等待服务器的回复

 

2) Server监听到连接请求报文后,如同意建立连接,则向Client发送确认。TCP报文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是x+1,同时表明x为止的所有数据都已正确收到(ack=1其实是ack=0+1,也就是期望客户端的第1个帧),seq = y 表示Server 自己的初始序号(seq=0就代表这是服务器这边发出的第0号帧)。这时服务器进入syn_rcvd,表示服务器已经收到Client的连接请求,等待client的确认。

 

3) Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 表示确认号ack= y + 1 有效(代表期望收到服务器的第1个帧),Client自己的序号seq= x + 1(表示这就是我的第1个帧,相对于第0个帧来说的),一旦收到Client的确认之后,这个TCP连接就进入Established状态,就可以发起http请求了。

 

 

3.建立TCP连接后发起http请求

 

进过TCP3次握手之后,浏览器发起了http的请求(第4帧),使用的http的方法 GET 方法,协议是HTTP/1.1

 

 

 

 

4.服务器端响应http请求,浏览器得到html代码

 

服务器端WEB程序接收到http请求以后,就开始处理该请求,处理之后就返回给浏览器html文件。

 

 

 

 

前面3个tcp包为3次握手的过程,主机向服务器发送一个http应用请求,服务器收到请求后,返回一个tcp确认帧(第5帧),接着发送一个http应答给主机(载有实际数据,第6,7帧,由于数据较大,分成多个包传输),主机收到服务器的http应答数据后,又发送一个tcp确认帧(第8帧),确认收到了数据,反复进行传输,应答,直到所有数据传输完成(6到18帧)。

 

第4号包是http请求包,第19号包是http响应包

 

 

 

 

以下是数据传输包含的内容:

 

 

5. 浏览器解析html代码,并请求html代码中的资源

 

浏览器拿到index.html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载(会使用多线程下载,每个浏览器的线程数不一样),这个时候就用上keep-alive特性了,建立一次HTTP连接,可以请求多个资源,下载资源的顺序就是按照代码里的顺序。

 

浏览器在请求静态资源时(在未过期的情况下),向服务器端发起一个http请求(询问自从上一次修改时间到现在有没有对资源进行修改),如果服务器端返回304状态码(告诉浏览器服务器端没有修改),那么浏览器会直接读取本地的该资源的缓存文件。

 

 

 

PS:HTTP1.0和HTTP1.1的区别

 

在HTTP1.0协议中,客户端与web服务器建立连接后,只能获得一个web资源。

 

HTTP1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源。

 

 

PS:一个web页面中,使用img标签引用了三幅图片,当客户端访问服务器中的这个web页面时,客户端总共会访问几次服务器,即向服务器发送了几次HTTP请求。

 

6.浏览器对页面进行渲染呈现给用户

 

浏览器利用自己内部的工作机制,把请求到的静态资源和html代码进行渲染,渲染之后呈现给用户。

 

 

 

7.传输完成,断开四次挥手

 

 

 

 

 

断开连接端可以是Client端,也可以是Server端。假设Client端发起中断连接请求:

 

第一次挥手:客户端先发送FIN报文(第24帧),用来关闭主动方到被动关闭方的数据传送,也就是客户端告诉服务器:我已经不会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,客户端依然会重发这些数据),但此时客户端还可以接受数据。

 

第二次挥手:Server端接到FIN报文后,但是如果还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以服务器端先发送ACK(第25帧),告诉Client端:请求已经收到了,但是我还没准备好,请继续等待停止的消息。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。

 

第三次挥手:当Server端确定数据已发送完成,则向Client端发送FIN报文(第26帧),告诉Client端:服务器这边数据发完了,准备好关闭连接了。

 

第四次挥手:Client端收到FIN报文后,就知道可以关闭连接了,但是他还是不相信网络,所以发送ACK后进入TIME_WAIT状态(第27帧), Server端收到ACK后,就知道可以断开连接了。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,最后,Client端也可以关闭连接了至此,TCP连接就已经完全关闭了!

 

 

 

下图是个完整的过程,便于理解和记忆。

 

 

posted on 2017-08-10 23:34  仲达超  阅读(411)  评论(0编辑  收藏  举报

导航