本帖最后由 KIKIRO 于 2022-9-23 15:15 编辑
TCP协议的报文格式 在TCP/IP协议栈中,IP协议层只关心如何使数据能够跨越本地网络边界的问题,而不关心数据如何传输。整体TCP/IP协议栈,共同配合一起解决数据如何通过许许多多个点对点通路,顺利传输到达目的地。一个点对点通路被称为一“跳”(hop),通过TCP/IP协议栈,网络成员能够在许多“跳”的基础上建立相互的数据通路。
传输层TCP协议提供了一种面向连接的、可靠的字节流服务,其数据帧格式,大致如下图所示: file:///C:/Users/23530/AppData/Local/Temp/msohtmlclip1/01/clip_image002.png
图:传输层TCP协议的数据帧格式
一个传输层TCP协议的数据帧,大致包含以下字段:
(一)源端口号 源端口号表示报文的发送端口,占16位。源端口和源IP地址组合起来,可以标识报文的发送地址。
(二)目的端口号 目的端口号表示报文的接收端口,占16位。目的端口和目的IP地址相结合,可以标识报文的接收地址。
TCP协议是基于IP协议的基础上传输的,TCP报文中的源端口号+源IP,与TCP报文中的目的端口号+目的IP一起,组合起来唯一性的确定一条TCP连接。
(三)序号(Sequence Number) TCP传输过程中,在发送端出的字节流中,传输报文中的数据部分的每一个字节都有它的编号。序号(Sequence Number)占32位,发起方发送数据时,都需要标记序号。
序号(Sequence Number)的语义与SYN控制标志(Control Bits)的值有关。根据控制标志(ControlBits)中的SYN是否为1,序号(Sequence Number)表达不同的含义:
(1)当SYN = 1时,当前为连接建立阶段,此时的序号为初始序号ISN((Initial Sequence Number),通过算法来随机生成序号;
(2)当SYN = 0时在数据传输正式开始时,第一个报文的序号为 ISN + 1,后面的报文的序号,为前一个报文的SN值+TCP报文的净荷字节数(不包含TCP头)。比如,如果发送端发送的一个TCP帧的净荷为12byte,序号为5,则发送端接着发送的下一个数据包的时候,序号的值应该设置为5+12=17。
在数据传输过程中,TCP协议通过序号(Sequence Number)对上层提供有序的数据流。发送端可以用序号来跟踪发送的数据量;接收端可以用序号识别出重复接收到的TCP包,从而丢弃重复包;对于乱序的数据包,接收端也可以依靠序号对其进行排序。
(四)确认序号(Acknowledgment Number) 确认序号(Acknowledgment Number)标识了报文接收端期望接收的字节序列。如果设置了ACK控制位,确认序号的值表示一个准备接收的包的序列码,注意,它所指向的是准备接收的包,也就是下一个期望接收的包的序列码。
举个例子,假设发送端(如Client)发送3个净荷为1000byte、起始SN序号为1的数据包给Server服务端,Server每收到一个包之后,需要回复一个ACK响应确认数据包给Client。ACK响应数据包的ACK Number值,为每个Client包的为SN+包净荷,既表示Server已经确认收到的字节数,还表示期望接收到的下一个Client发送包的SN序号,具体的ACK值如下图左边的正常传输部分所示。 file:///C:/Users/23530/AppData/Local/Temp/msohtmlclip1/01/clip_image004.png
图:传输过程的确认序号(Acknowledgment Number)值示例图
在上图的左边部分,Server第1个ACK包的ACK Number值为1001,是通过Client第1个包的SN+包净荷=1+1000计算得到,表示期望第2个Client包的SN序号为1001;Server第2个ACK包的ACK Number值为2001,为Client第2个包的SN+包净荷=2001,表示期望第3个Server包的SN为2001,以此类推。
如果发生错误,假设Server在处理Client的第二个发送包异常,Server仍然回复一个ACK Number值为1001的确认包,则Client的第二个数据包需要重复发送,具体的ACK值如上图右边的正常传输部分所示。
只有控制标志的ACK标志为1时,数据帧中的确认序号ACK Number才有效。TCP协议规定,连接建立后,所有发送的报文的ACK必须为1,也就是建立连接后,所有报文的确认序号有效。如果是SYN类型的报文,其ACK标志为0,故没有确认序号。
(五)头部长度 该字段占用4位,用来表示TCP报文首部的长度,单位是4bit位。其值所表示的并不是字节数,而是头部的所含有的32bit的数目(或者倍数),或者4个字节的倍数,所以TCP头部最多可以有60字节(4*15=60)。没有任何选项字段的TCP头部长度为20字节,所以其头部长度为5,可以通过20/4=5计算得到。
(六)预留6位 头部长度后面预留的字段长度为6位,作为保留字段,暂时没有什么用处。
(七)控制标志 控制标志(ControlBits)共6个bit位,具体的标志位为:URG、ACK、PSH、RST、SYN、FIN。6个标志位的说明,如下表所示。 file:///C:/Users/23530/AppData/Local/Temp/msohtmlclip1/01/clip_image006.png 表:TCP报文控制标志(ControlBits)说明
标志位 说明 URG 占1位,表示紧急指针字段有效。URG位指示报文段里的上层实体(数据)标记为“紧急”数据。当URG=1时,其后的紧急指针指示紧急数据在当前数据段中的位置(相对于当前序列号的字节偏移量),TCP接收方必须通知上层实体。 ACK 占1位,置位ACK=1表示确认号字段有效;TCP协议规定,接建立后所有发送的报文的ACK必须为1;当ACK=0时,表示该数据段不包含确认信息。当ACK=1时,表示该报文段包括一个对已被成功接收报文段的确认序号AcknowledgmentNumber,该序号同时也是下一个报文的预期序号。 PSH 占1位,表示当前报文需要请求推(push)操作;当PSH=1时,接收方在收到数据后立即将数据交给上层,而不是直到整个缓冲区满。 RST 占1位,置位RST=1表示复位TCP连接;用于重置一个已经混乱的连接,也可用于拒绝一个无效的数据段或者拒绝一个连接请求。如果数据段被设置了RST位,说明报文发送方有问题发生。 SYN 占1位,在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1。 综合一下,SYN置1就表示这是一个连接请求或连接接受报文。 FIN 占1位,用于在释放TCP连接时,标识发送方比特流结束,用来释放一个连接。当 FIN = 1时,表明此报文的发送方的数据已经发送完毕,并要求释放连接。 在连接建立的三次握手过程中,若只是单个SYN置位,表示的只是建立连接请求。如果SYN和ACK同时置位为1,表示的建立连接之后的响应。
(八)窗口大小: 长度为16位,共2个字节。此字段用来进行流量控制。流量控制的单位为字节数,这个值是本端期望一次接收的字节数。
(九)校验和: 长度为16位,共2个字节。对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,接收端用于对收到的数据包进行验证。
(十)紧急指针: 长度为16位,2个字节。它是一个偏移量,和SN序号值相加表示紧急数据最后一个字节的序号。
以上十项内容是TCP报文首部必须的字段,也称固有字段,长度为20个字节。接下来是TCP报文的可选项和填充部分。
(十一)可选项和填充部分 可选项和填充部分的长度为4n字节(n是整数),该部分是根据需要而增加的选项。如果不足4n字节,要加填充位,使得选项长度为32位(4字节)的整数倍,具体的做法是在这个字段中加入额外的零,以确保TCP头是32位(4字节)的整数倍。
最常见的选项字段是MSS(MaximumSegment Size最长报文大小),每个连接方通常都在通信的第一个报文段(SYN标志为1的那个段)中指明这个选项字段,表示当前连接方所能接受的最大报文段的长度。
由于可选项和填充部分不是必须的,所以TCP报文首部最小长度为20个字节。
至此,TCP报文首部的字段,就全部介绍完了。TCP报文首部的后面,接着的是数据部分,不过数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有TCP首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据,比如在处理超时的过程中,也会发送不带任何数据的报文段。
总体来说,TCP协议的可靠性,主要通过以下几点来保障:
(1)应用数据分割成TCP认为最适合发送的数据块。这部分是通过MSS(最大数据包长度)选项来控制的,通常这种机制也被称为一种协商机制,MSS规定了TCP传往另一端的最大数据块的长度。值得注意的是,MSS只能出现在SYN报文段中,若一方不接收来自另一方的MSS值,则MSS就定为536字节。一般来讲,MSS值还是越大越好,这样可以提高网络的利用率。
(2)重传机制。设置定时器,等待确认包,如果定时器超时还没有收到确认包,则报文重传。
(3)对首部和数据进行校验。
(4)接收端对收到的数据进行排序,然后交给应用层。
(5)接收端丢弃重复的数据。
(6)TCP还提供流量控制,主要是通过滑动窗口来实现流量控制。
至此TCP协议的数据帧格式介绍完了。接下来开始为大家重点介绍:TCP传输层的三次握手建立连接,四次挥手释放连接。
TCP的三次握手 TCP连接的建立时,双方需要经过三次握手,而断开连接时,双方需要经过四次分手,那么,其三次握手和四次分手分别做了什么呢?又是如何进行的呢?
通常情况下,建立连接的双方,由一端打开一个监听套接字(ServerSocket)来监听来自请求方的TCP(Socket)连接,当服务器端监听开始时,必须做好准备接受外来的连接,在Java中该操作通过创建一个ServerSocket服务监听套接字实例来完成,此操作会调用底层操作系统(如Linux)的C代码中三个函数socket()、bind()、listen() 来完成。开始监听之后,服务器端就做好接受外来连接的准备,如果监听到建立新连接的请求,会开启一个传输套接字,称之为被动打开(Passive Open)。
一段简单的服务端监听新连接请求,并且被动打开(Passive Open)传输套接字的Java示例代码,具体如下:
file:///C:/Users/23530/AppData/Local/Temp/msohtmlclip1/01/clip_image008.png 客户端在发起连接建立时,Java代码通过创建Socket实例,调用底层的connect(…)方法,主动打开(Active Open)Socket连接。套接字监听方在收到请求之后,监听方和发起方(客户端)之间就会建立一条的连接通道,该通道由双方IP和双方端口所唯一确定。
一段简单的客户端连接主动打开(Active Open)的Java示例代码,具体如下:
file:///C:/Users/23530/AppData/Local/Temp/msohtmlclip1/01/clip_image010.png 三次握手过程 TCP连接的建立时,双方需要经过三次握手,具体过程如下:
(1)第一次握手:Client进入SYN_SENT状态,发送一个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产生的一个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带一个MSS(最大报文段长度)可选项的值,表示客户端发送出去的最大数据块的长度。
(2)第二次握手:Server端在收到SYN帧之后,会进入SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要目的在于通知Client,Server端已经收到SYN消息,现在需要进行确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端生成的SN序号;SYN+ACK帧的MSS(最大报文段长度)表示的是Server端的最大数据块长度。
(3)第三次握手:Client在收到Server的第二次握手SYN+ACK确认帧之后,首先将自己的状态会从SYN_SENT变成ESTABLISHED,表示自己方向的连接通道已经建立成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Server端的SN序列号+1。还有一种情况,Client可能会将ACK帧和第一帧要发送的数据,合并到一起发送给Server端。
(4)Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进入ESTABLISHED状态,至此,Server方向的通道连接建立成功,Server可以发送数据给Client,TCP的全双工连接建立完成。
三次握手的图解 三次握手的交互过程,具体如下图所示: file:///C:/Users/23530/AppData/Local/Temp/msohtmlclip1/01/clip_image012.png
图:TCP建立的连接时三次握手示意图
Client和Server完成了三次握手后,双方就进入了数据传输的阶段。数据传输完成后,连接将断开,连接断开的过程需要经历四次挥手。
TCP的四次挥手 业务数据通信完成之后,TCP连接开始断开(或者拆接)的过程,在这个过程中连接的每个端的都能独立地、主动的发起,断开的过程TCP协议使用了四路挥手操作。
四次挥手具体过程 四次挥手具体过程,具体如下:
(1)第一次挥手:主动断开方(可以是客户端,也可以是服务器端),向对方发送一个FIN结束请求报文,此报文的FIN位被设置为1,并且正确设置Sequence Number(序列号)和Acknowledgment Number(确认号)。发送完成后,主动断开方进入FIN_WAIT_1状态,这表示主动断开方没有业务数据要发送给对方,准备关闭SOCKET连接了。
(2)第二次挥手:正常情况下,在收到了主动断开方发送的FIN断开请求报文后,被动断开方会发送一个ACK响应报文,报文的Acknowledgment Number(确认号)值为断开请求报文的Sequence Number (序列号)加1,该ACK确认报文的含义是:“我同意你的连接断开请求”。之后,被动断开方就进入了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知高层的应用进程,对方向本地方向的连接已经关闭,对方已经没有数据要发送了,若本地还要发送数据给对方,对方依然会接受。被动断开方的CLOSE-WAIT(关闭等待)还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
主动断开方在收到了ACK报文后,由FIN_WAIT_1转换成FIN_WAIT_2状态。
(3)第三次挥手:在发送完成ACK报文后,被动断开方还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截止后,被动断开方会向主动断开方发送一个FIN+ACK结束响应报文,表示被动断开方的数据都发送完了,然后,被动断开方进入LAST_ACK状态。
(4)第四次挥手:主动断开方收在到FIN+ACK断开响应报文后,还需要进行最后的确认,向被动断开方发送一个ACK确认报文,然后,自己就进入TIME_WAIT状态,等待超时后最终关闭连接。处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,如果期间没有收到其他报文,则证明对方已正常关闭,主动断开方的连接最终关闭。
被动断开方在收到主动断开方的最后的ACK报文以后,最终关闭了连接,自己啥也不管了。
四次挥手图解 四次挥手的全部交互过程,具体如下图所示: file:///C:/Users/23530/AppData/Local/Temp/msohtmlclip1/01/clip_image014.png
图:TCP建立的连接时四次挥手的示意图
处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,才真正关闭连接通道,其等待的时间为什么是2MSL呢?
2MSL翻译过来就是两倍的MSL。MSL全称为Maximum Segment Lifetime,指的是一个TCP报文片段在网络中最大的存活时间,具体来说,2MSL对应于一次消息的来回(一个发送和一个回复)所需的最大时间。如果直到2MSL,主动断开方都没有再一次收到对方的报文(如FIN报文),则可以推断ACK已经被对方成功接收,此时,主动断开方将最终结束自己的TCP连接。所以,TCP的TIME_WAIT状态也称为2MSL等待状态。
有关MSL的具体的时间长度,在RFC1122协议中推荐为2分钟。在SICS(瑞典计算机科学院)开发的一个小型开源的TCP/IP协议栈——LwIP开源协议栈中MSL默认为1分钟。在源自Berkeley的TCP协议栈实现中MSL默认长度为30秒。总体来说,TIME_WAIT(2MSL)等待状态的时间长度,一般维持在1-4分钟之间。
通过三次握手建立连接和四次挥手拆除连接,一次TCP的连接建立及拆除,至少进行7次通信,可见其成本是很高的。 ———————————————— 版权声明:本文为CSDN博主「架构师-尼恩」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 |