引言
TCP是一个面向连接的协议。无论哪一方向另一方发送数据之前,都必须现在双方之间建立一条连接。本篇笔记详细讨论一个TCP连接是如何建立的以及通信结束后如何终止的。
连接的建立与终止
建立连接的三次握手:
- 客户端发送一个SYN报文指明客户打算连接服务器的端口,并发送自己的初始序号(ISN)
- 服务端发送SYN+ACK报文,设置服务器自己的初始序号ISN,并对客户的初始序号加1以对客户的SYN报文进行确认。
- 客户端将确认序号设置为服务器的ISN+1,以对服务器的SYN报文段进行确认。
终止连接的四次握手(也叫四次挥手):
- 客户端发送一个FIN,用来关闭从客户端到服务器的数据传输
- 服务器收到这个FIN,发回一个ACK,确认序号为收到的序号加1。
- 接着服务器关闭它的连接,导致它发送一个FIN
- 客户必须发回一个确认,并将确认序号设置为收到的序号加1。
最大报文段长度
最大报文段长度(MSS)表示TCP传往另一端的最大数据块的长度。当一个连接建立时,连接的双方都要通告对方各自的MSS。
最终的IP数据报要在MSS上加上20字节的TCP首部和20字节的IP首部。
最大报文段不是任何条件下都可协商。当建立一个连接时,每一方都有用于通告它期望接收的MSS选项。如果一方不接收来自另一方的MSS值,MSS就定为默认值536字节。
一般来说,如果没有分段发生,MSS越大越好(也有例外)。报文段越大,相对IP和TCP首部有更高的网络利用率。
如果目的IP是非本地的,MSS通常的默认值为536。区分地址是本地的还是非本地的很简单,如果目的IP地址的网络号与子网号和我们相同,就是本地的;如果目的IP的网络号与我们完全不同,则是非本地的;如果目的IP地址的网络号与我们相同而子网号与我们的不同,则可能是本地的,也可能是非本地的,需要系统管理员说明。
MSS让主机限制另一端发送数据报的长度,这样可以避免较小MTU网络上的主机发生分段。
TCP的半关闭
TCP连接的一端在结束它的发送后,还能接收来自另一端的数据,这就是所谓的半关闭。
- 客户端发出FIN,接着另一端对FIN回应一个ACK报文段。
- 接收半关闭的一方仍然能够发送数据,我们只显示了一个数据报文段和一个ACK报文段,但可能发送了许多数据报文段。
- 当收到半关闭的一段完成它的数据后,将发送一个FIN关闭这个方向上的连接。
- 当对第二个FIN进行确认后,这个连接便彻底关闭了。
TCP的状态变迁图
粗的实线表示正常的客户端状态变迁,粗的虚线表示正常的服务器状态变迁。
- 两个导致进入ESTABLISHED状态的变迁对应打开一个连接,两个导致从ESTABLISHED状态离开的变迁对应关闭一个连接。ESTABLISHED状态是连接双方进行双向数据传递的状态。
上图显示了正常的TCP连接的建立和终止过程。
2MSL等待状态
TIME_WAIT状态也称为2MSL状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。
MSL处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可以让TCP再次发送最后的ACK以防止这个ACK丢失。
这种等待的另一个结果是,在这个TCP连接的2MSL等待期间,定义这个连接的插口(客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。
当连接处于2MSL等待时,任何迟到的报文段将被丢弃。因为处于2MSL等待的、由该插口对定义的连接在这段时间内不能被再用。
平静时间的概念
TCP在重启动后的MSL秒内不能建立任何连接,这就是平静时间。
FIN_WAIT_2 状态
在FIN_WAIT_2状态我们已经发出了FIN,并且另一端已对它进行确认。只有当另一端向我们发送FIN来关闭另一方的连接,我们这段才会从FIN_WAIT_2状态进入TIME_WAIT状态。
这意味着我们这端可能永远保持这个状态。另一端也将处于CLOSE_WAIT状态,并一直保持这个状态直到应用层决定进行关闭。
复位报文段
TCP首部中的RST比特是用于“复位”的。无论何时一个报文段发往插口出现错误,TCP都会发出一个复位报文段。
到不存在的端口的连接请求
当连接请求达到时,目的端口没有进程正在听。对于UDP,当一个数据报到达目的端口时,该端口没在使用,ICMP端口不可达的信息。而TCP则使用复位。
异常终止一个连接
有序释放:终止一个连接的正常方式是一方发送FIN,所有排队数据都已发送后才发送FIN,正常情况下没有任何数据丢失。
异常释放:发送复位报文段而不是FIN来中途释放一个链接。
异常终止连接对应用程序的两个优点:
- 丢弃任何待发数据并立即发送复位报文段
- RST的接收方会区分另一端执行的异常关闭还是正常关闭
检测半打开连接
如果一方已经关闭或异常终止连接而另一方还不知道,我们将这样的TCP连接称为半打开。如何一端的主机异常都可能导致发生这种情况,只要不打算在半打开的连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常。
同时打开
两个应用程序彼此执行主动打开,每一方发送一个SYN,且这些SYN必须传递给对方,这需要每一方使用一个对方熟知的端口作为本地端口,这称为同时打开。
例如,主机A中的一个应用程序使用本地端口7777,并与主机B的端口8888执行主动打开。主机B中的应用程序则使用本地端口8888,并与主机A的端口7777执行主动打开。
对于同时打开TCP仅建立一条连接而不是两条连接。
一个同时打开的连接需要交换4个报文段,比正常的三次握手多一个。而且,没有任何一端称为客户或服务器,因为每一端既是客户又是服务器。
同时关闭
TCP两端同时执行主动关闭也是可能的,TCP协议也允许这样的同时关闭(simultaneous close)。
当应用层发出关闭命令时,两端均从ESTBLISHED变为FIN_WAIT_1,这将导致双方各发送一个FIN,两个FIN经过网络传送到另一端。收到FIN后,状态由FIN_WAIT_1变迁到CLOSING,并发送最后的ACK。当收到最后的ACK时,状态变化为TIME_WAIT。
TCP选项
每个选项的开始是1字节的kind字段,说明选项的类型。kind为0和1的选项只占1个字节。其他选项在kind字节后还有len字节。它说明的长度是总长度。
设置无操作选项的原因在于允许发送方填充字段为4字节的倍数。
小结
两个进程在使用TCP交换数据之前,它们之间必须建立一条连接。完成后,要关闭这个连接。我们详细介绍了如何使用三次握手建立连接和四次挥手关闭连接。
弄清楚TCP操作的关键在于它的状态变迁图。
一个TCP连接由一个4元组唯一确定:本地IP地址、本地端口号、远端IP地址和远端端口号。