TCP基础及连接与释放
概述
TCP/IP结构中,最主要的传输层协议就是传输控制协议(TCP, Transmission Control Protocol),它具有如下主要特性:
-
面向连接
在使用TCP前必须建立TCP传输连接,而且使用完后也必须释放连接。
有很多网络攻击手段就是利用这个特性实现的。
-
仅支持单播传播
每条TCP传输连接只能点对点传输,不支持多播及广播的方式传输。
-
可靠的服务
TCP连接提供的传输服务是无差错、不丢失、不重复、按序的。
-
数据段为传输单位
TCP仍然采用传统的数据段作为数据单元,但数据段大小受应用层传送的报文大小和途径网络中的MTU值大小决定,所以每次发送TCP的数据段大小是不固定的。有一个最大数据段大小(MSS, Maximum Segment Size)来指示最大大小。
-
TPDU格式只有一种
TCP的数据单元称为数据段,但也可以按OSI/RM中称为TPDU。在TCP中只有一种TPDU格式。
-
支持全双工
TCP允许通行双方在任何时候都能发送数据,因为这种连接方式在两端都有缓冲区用于临时存放数据。它也支持立即发送一个数据,也可以缓存一段时间一次性发送更多的数据(最大大小取决于MSS的值)。
-
连接基于字节流
TCP的传输并不像UDP一样是按报文为单位的,而是没有边界的,均以字节流的方式传输。
-
每次发送的数据段大小和数量都是不固定的
每次可以发送多少字节是不固定的,也不由可用缓存决定,而是根据对方给出的窗口和网络的拥塞程度决定的。
另外,在缓存中的数据过多,会对它进行分段。反之,如果缓存中数据过小,则会等待到有一定量的数据再组装一起发送。
TCP数据段格式
数据段(Segment)这个叫法来自协议数据单元的。TCP软件决定了数据段的大小,因为既可以将多次写操作汇聚到一起,也可以将一个写入操作分割。
但是仍然有两个因素决定了数据段的大小:一是TCP数据段的大小必须在IP数据报的有效载荷范围内,二是最终仍然需要符合MTU限制。
TCP通过数据段的交互来执行连接、传输、确认、差错控制、流量控制及关闭等操作。整个数据段也分为段头和数据两部分,所以也可以将其看做报文。
和其他报头类似的,段头是为了实现可靠传输加上的控制信息,而数据部分则是高层发来的用户数据。
下图是它的段头格式:
-
源端口/目的端口(Source Port/Destination Port)
表示发送方和接收方的TCP端口号,各占16位(2字节)。一个端口与主机IP地址就可以组成套接字(Socket)。
-
序号(Seq, Sequence Number)
表示数据部分第一个字节的编号,占32位(4字节)。在TCP连接中,所有传送的数据都要以字节为单位编号,其起始序号必须在连接建立时设置。
-
确认号(Ack, Acknowledgement Number)
表示期望收到下一个数据段的数据部分起始序号,占32位(4字节)。注意,确认号表示的是期望的下一个,不是已经正确接受的最后一个,这点上有点语义上的混淆,需注意。
序号和确认号共同支撑TCP中的差错控制服务。
-
数据偏移(Parity)
表示数据部分距离数据段头起始处的偏移量,以4字节为单位,占4位(1字节)。该字段用于确认数据段头的长度,因为TCP和IP数据报一样具有可选项,所以需要该字段来确认实际长度。
根据该字段的长度可以得知,数据偏移量最大为60字节,所以TCP段头最大长度也只有60字节。
-
保留(Reserved)
目前无作用,占6位。因为无作用所以设置为全0。
-
六位控制标志
-
紧急指针位(URG, Urgent Pointer)
该位为1代表有紧急数据,且仅当本位为1时后面的紧急指针字段才有意义。
-
确认位(ACK, Acknowledgement)
指示该数据段中确认号字段有效,否则应用层读取数据时会无视确认号字段。
-
推送位(PSH, Push)
指示是否立即把接收到的数据交付给应用进程,如果否,则可能会被缓存到足够数量再一次性交付。
-
重置位(RST, Reset)
指示需要重新建立当前连接,当连接出现问题期望重新建立连接时使用。
-
同步位(SYN, Synchronization)
指示该段是一个连接请求或连接确认报文,当置为1且ACK置为0时,表示请求连接,当两者都置为1时,表示确认连接。
-
结束位(FIN, Final)
指示要求释放连接,当发送端已经传输完毕或不再需要连接时置为1。注意,如果接收端此时仍未接受完毕数据可以继续接受。
-
-
窗口大小(Window)
指示发送方的窗口大小,也就是发送者当前还可以接收的最大字节数,占16位(2字节)。
-
校验和(Checksum)
对数据段头、数据、伪头部三部分进行的校验和校验码,占16位(2字节)。
伪头部包含源主机/目的主机IP地址、TCP协议号、TCP数据段长度。
-
紧急指针(URG, Urgent Pointer)
当紧急指针位为1时,该指针用于指明紧急数据在数据段中的位置,占16位(2字节)。
要注意的是,即使窗口大小为0,紧急数据仍然可以发送,因为紧急数据是不被缓存的。
-
可选(Option)
该字段最长可达40字节,它可以有任意数据,但一般有窗口缩放选项(WSopt, Window Scale Option)、最大数据段大小(MSS, Maximum Segment Size)、时间戳(Timestamp)等。
-
数据(Data)
由应用层提交的数据。
TCP连接状态转移
TCP的Socket服务原语(Primitive)只有8个,比OSI/RM结构中定义的更少,但是这些原语中可以有不同的状态。如下表所示:
状态 | 描述 |
---|---|
CLOSED | 阻塞、关闭状态,表示主机没有活动连接 |
LISTEN | 监听状态,表示主机正在等待新的连接进入 |
SYN RCVD | 主机已收到连接请求,但未确认 |
SYN SENT | 主机已经发送一个连接请求,等待对方确认 |
ESTABLISHED | 连接建立,双方进入正常数据传输状态 |
FIN WAIT 1 | (主动关闭)主机已发送关闭连接请求,等待对方确认 |
FIN WAIT 2 | (主动关闭)主机已收到对方关闭连接确认,等待对方发送关闭连接请求 |
TIMED WAIT | 完成双向传输连接关闭,等待所有分组消失 |
CLOSING | 双方同时尝试关闭连接,等待对方确认 |
CLOSE WAIT | (被动关闭)收到对方关闭连接请求,并且已确认 |
LAST ACK | (被动关闭)等待最后一个关闭连接确认,并等待所有分组消失 |
这种建立和释放过程中的状态又称为有限状态机(FSM, Finite State Machine)。
下图详细描述了通信主机在各种过程中的状态机,其中方框表示主机不同时期的状态、箭头表示状态之间的转换、注释表示状态转换进行的操作。
而粗线是客户端主动连接服务端的正常过程,其中客户端状态转移用带箭头的粗实线表示,服务端的状态转移用带箭头的粗虚线表示。
带箭头的细线表示一些不常见的事件,如复位、同时打开、同时关闭等。
TCP连接的建立
TCP是使用三次握手机制来建立连接的,其流程见下图:
简单的剖析一下这个过程:
首先服务器要经过初始化过程,从CLOSED状态顺序调用SOCKET、BIND、LISTEN、ACCEPT原语来创建Socket并进入LISTEN状态,等待客户端连接请求。
客户端也从CLOSED状态开始调用SOCKET等原语创建Socket,然后调用CONNECT原语来请求建立连接。它会主动打开端口,向服务器发送一个SYN标志为1(表示这是一个同步数据段)且有c为***的数据段到服务器,随后便进入了SYN SENT状态。
-
服务器收到该SYN数据段后会被动的打开端口,并且返回一个SYN及ACK标志为1(表示这是一个同步确认数据段)且确认号为c+1并有s为***的数据段,随后进入SYN RCVD状态。
这个过程一定要注意确认号是期望收到的下一个***,因此是c+1而不是c。
-
客户端收到来自来自服务器的SYN+ACK数据段后,向服务器发送一个ACK标志为1且确认号为s+1并有c+1为***的数据段,随后进入ESTABLISHED状态(此时已经建立单向连接)。
要注意的是,只有单独ACK标志的数据段,并不实际使用序号,当客户端实际发送数据时,第一个序号仍然为c+1。
服务器收到来自客户端的ACK数据段后,也进入ESTABLISHED状态,这时便完成了双向连接状态。
三次握手的基本流程如上所述,可以看见,序号和确认号是通信过程中非常重要的,另外,这个三次握手的流程可以协商双方的情况,如MSS的值等。
双方同时建立连接
有时候可能会出现双方同时向对方建立连接的情况,但这种情况也不会产生影响,只是状态有些变化,但最终建立的仍然是一个连接,其流程见下图:
TCP连接的释放
当TCP建立连接后,数据可以在两个方向互不干扰的传送,当应用进程再没有数据需要发送时,就可以发送关闭连接请求。但是发送请求后,关闭的是发往对方的数据流通道,本端仍然可以接收来自对方的数据。除非对方也同样的发出了关闭连接请求,这样双方的连接就会彻底关闭。
由于TCP有半关闭状态(因双端都需要各自关闭,这也是全双工的特性),所以TCP的释放是四次握手流程。它的具体流程如下图所示:
一开始,通信双方都处于ESTABLISHED状态,假定客户端认为数据已经发送完了,准备结束本次连接,则由应用进程调用CLOSE原语,向服务器发送一个FIN标志为1并有c为***的数据段,随后进入FIN WAIT 1状态。
-
服务器收到FIN数据段后,在确认没有数据待接收(如需要重传的部分,未接收完成的部分),会向客户端发送一个ACK标志为1且确认号为c+1的数据段,随后进入CLOSE WAIT状态,与此同时通知服务器上对应的应用程序,释放从客户端到服务器方向的连接,进入半关闭状态。
注意,此时服务器仍然可以向客户端发送数据。
另外,该ACK数据段同时也向客户端表示前面的数据已经全部正确接收。
当客户端收到ACK数据段后,进入FIN WAIT 2状态。继续等待服务器发送释放连接的数据段。
当服务器也没有数据要发送后,其应用进程会通知TCP**释放从服务器到客户端方向的连接,此时会向客户端发送**FIN及ACK标志为1且确认号为c+1并有s为***的确认数据段,随后进入LAST ACK状态,等待客户端的确认。
-
当客户端收到FIN+ACK数据段后,向服务器发送一个ACK标志为1且确认号为s+1并有c+1为***的数据段,随后进入TIME WAIT状态。
此时连接并没有关闭,必须等待2MSL时间才会彻底关闭进入CLOSED状态。
RFC793建议MSL为2分钟。
服务器收到来自客户端的ACK数据段后,进入CLOSED状态,彻底释放连接。
双方同时释放连接
关闭时,也有可能出现双方同时释放的情况,但并不常见,其流程如下图所示:
文中图片来自《深入理解计算机网络》