TCP提供一种面向连接的、可靠的基于流的服务。
面向连接:在彼此交换数据之前必须先建立一个TCP连接,双方互相确认,仅有两方彼此通信。
可靠:数据被分割成TCP认为最适合发送的数据块;TCP发出一个段后启动一个定时器,超时重传;TCP收到另一端的数据后,将回复一个确认;TCP将保持它首部和数据的校验和,以检测数据在传输过程中的任何变化;必要时TCP将对收到的数据进行重新排序;TCP还能提供流量控制。
基于流的服务:应用程序对数据的发送和接受是没有边界限制的,发送段指定的写操作和接受段执行的读操作的次数之间没有任何的数量关系。
提供全双工通信。数据在两个方向上独立的进行传输,连接的每一端必须保持每个方向上的输出数据序号。
TCP首部
通常是20个字节。
发送端和目的端的端口号:用于标识发端和收端的应用进程。一个IP地址和一个端口号称为一个socket,插口对(包含源端IP、源端口、目的IP、目的端口)可唯一确定一个TCP连接。
32位序号:用来标识从TCP发端向TCP收端发送的数据字节流,表示在这个报文段中第一个数据字节,TCP用序号对每个字节进行计数。用来解决网络包乱序问题。
32位确认序号:希望对端发送的数据字节,只有ACK标志位为1时,确认序号字段才有效。确认方ack=发送方seq+1。用来解决不丢包问题。
4位首部长度:TCP首部除固定长度外,还有可选项,因此TCP首部长度是可变的。UDP就没有,UDP的长度是固定的。
标志比特位:
URG:紧急指针有效
ACK:确认序号有效
PSH:接收方应该尽快将这个报文段交给应用层
RST:重置连接
SYN:同步序号用来发起一个连接
FIN:发端完成发送任务,释放连接
窗口大小:这个值是接受端正期望接收的字节,16位所以窗口大小最大为65535字节。著名的滑动窗口,解决流量控制。
校验和:这是一个强制性字段,一定是由发端计算和存储,并由收端进行验证。
紧急指针:只有当URG为1时才有效,是发送端向另一端发送紧急数据的一种方式。
问题一:为什么TCP首部中要把端口号放入最开始的4个字节?
答:在ICMP的差错报文中,要包含IP首部后面的8个字节的内容,而这里面有TCP首部中的源端口号和目的端口号。当TCP收到ICMP差错报文时需要用这两个端口来确定是哪条连接出了差错。
问题二:TCP提供了一种字节流服务,而收发双方都不保持记录的边界,应用程序应该如何提供他们自己的记录标识呢?
答:很多应用使用回车和换行来标记每个应用记录的结束,另外可以在记录前加上记录的字节计数等等手段。
TCP连接的建立
建立连接协议
(1)客户端将SYN标志置为1,发送一个SYN段指明客户打算连接的服务器的端口,以及客户的初始序号ISN。客户端进入SYN_SENT状态
(2)服务端发回服务器的初始序号的SYN报文段作为应答。同时将确认序号设置为客户的ISN+1,对客户的SYN报文段进行确认。一个SYN占用一个序号,服务端进入SYN_RCVD状态
(3)客户将确认序号设置为服务器的ISN+1以对服务器的SYN报文段进行确认。
初始序号是如何确定的?
当一端发送一个SYN报文段时,为连接选择一个初始序号。
那么初始序号是怎么来的呢?ISN随时间而变化,因此每个连接都将有不同的ISN。ISN可以看作是一个32位的计数器,每4ms加1。
TCP连接的终止
终止一个连接需要四次握手,这是有TCP的半关闭造成的。TCP连接是全双工的,因此每个方向都必须单独地进行关闭。
(1)首先进行关闭的一方发送一个FIN执行主动关闭,进入FIN_WAIT_1状态,另一端执行被动关闭。
(2)收到FIN后,服务器发回一个ACK,确认序号为收到的序号+1(一个FIN占用一个序号),服务器端进入CLOSE_WAIT状态。
(3)TCP服务器向应用程序传送一个文件结束符,接着这个服务器程序就关闭它的连接,导致它的TCP端发送一个FIN,服务器端进入LAST_ACK状态。
(4)收到FIN后,客户端进入TIME_WAIT状态,接着客户必须发回一个确认,并将确认序号设置为收到的序号加1,服务器端进入CLOSE状态。
问题一:为什么建立连接是三次握手,而关闭连接是四次挥手呢?
因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放到一个报文里发送给客户端了。
而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接受数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,在发FIN来关闭连接。因此己方ACK和FIN一般都会分开发送。
问题二:为什么服务器不将对客户FIN的ACK与自己的FIN合并,从而将报文段数减少为3个?
首先,服务器对客户的FIN确认一般不会被延迟,而是在FIN到达后立即发送。应用进程需要一些时间来接受EOF,告诉它的TCP关闭它这一端的连接。
第二,服务器收到客户的FIN后,并不一定要关闭它这一端的连接。
最大报文段长度(MSS)
表示TCP传往另一端的最大块数据的长度,当一个连接建立时,连接的双方都要通告各自的MSS。MSS选项只能出现在SYN报文段中。
如果一方不接收来自另一方的MSS值,则MSS就定为默认值536字节(这个默认值允许20字节的IP首部和20字节的TCP首部以适合576字节IP数据报)。
当TCP发送一个SYN时,或者是因为一个本地应用进程想发起一个连接,或者是因为另一端的主机收到了一个连接请求,它能将MSS值设置为外出接口上的MTU长度减去固定的IP首部和TCP首部长度。对于一个以太网,MSS值可达1460字节。如果目的IP地址为“非本地的(nonlocal)”,MSS通常的默认值为536。
MSS让主机限制另一端发送数据包的长度,加上主机也能控制它发送数据报的长度,这将使以较小的MTU连接到一个网络上的主机避免分段。
TCP的半关闭
何为半关闭? TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力,这就是所谓的半关闭。
只有少数的应用程序使用它,如果应用程序不调用close而调用shutdown,且第二个参数值为1,则插口的API支持半关闭。
为什么要有半关闭?
有些情况下,需要一些技术让客户通知服务器,客户端已经完成了它的数据传送,但仍要接受来自服务器的数据。
TCP的状态变迁图
- 粗的实线箭头表示正常的客户端状态变迁;细的虚线箭头表示正常的服务器状态变迁。
-
- 两个导致进入ESTABLISH-ED状态的变迁对应打开一个连接,而两个导致从ESTABLISHED状态离开的变迁主动打开对应关闭一个连接。
左下角4个状态放在一个虚线框内,并标为“主动关闭”。其他两个状态(CLOSE_WAIT和LAST_ACK)也用虚线框住,并标为“被动关闭”。
TIME_WAIT状态
也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间。
对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。目的:可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时重发最后的FIN,一来一回正好2个MSL);有足够时间让这个连接不会跟后面的连接混在一起。这个TCP连接在2MSL等待期间,定义这个连接的插口不能再被利用,这个连接只能在2MSL结束后才能被再次使用。
一个插口对(本地IP、本地端口、远端IP、远端端口)在它处于2MSL等待时,将不能再被使用。
在连接处于2MSL等待时,任何迟到的报文段将被丢弃,因为处于2MSL等待的、由该插口对定义的连接在这段时间内不能被再用。客户执行主动关闭并进入TIME_WAIT是正常的,服务器通常执行被动关闭,不会进入TIME_WAIT状态。因为服务器使用熟知端口,所以如果我们终止一个已经建立连接的服务器程序,并试图重启这个服务器程序,服务器程序将不能把它的这个熟知端口赋值给它的端点。在重启服务器程序前,需要在1~4分钟。
需要注意:一个连接只有经过了已建立状态才能进入TIME_WAIT状态。
问题一:处于TIME_WAIT状态的主机收到使其进入此状态的重复的FIN时所发生的情况?
重复的FIN会得到确认,2MSL定时器重新开始
问题二:处于TIME_WAIT状态的主机收到一个RST时所发生的情况?
这会引起过早地终止,这叫做TIME_WAIT断开。会存在潜在的问题,RFC提出简单的修改是TIME_WAIT状态时忽略RST段。
FIN_WAIT_2状态
我们发出了FIN,并且另一端也对它进行了确认,这时就处于FIN_WAIT_2状态。如果这一端不实行半关闭,那么只有当另一端的进程完成这个关闭,这端才会从FIN_WAIT_2状态进入TIME_WAIT状态。
这意味着,我们这端可能永远保持FIN_WAIT_2状态,另一端也将处于CLOSE_WAIT状态,并一直保持直到应用层决定进行关闭。为了防止这种在这个状态无限的等待,设置了一个定时器,如果这个连接空闲10分钟75秒,TCP将进入CLOSED状态。
复位报文段
RST比特是用于“复位”的。一般情况下,无论何时一个报文段发往基准的连接(由一个插口对指明的连接)出现错误,TCP都会发出一个复位报文段。下面列几个常见情况:
到不存在的端口的连接请求
当连接请求到达时,目的端口没有进程在监听。UDP产生一个ICMP端口不可达的信息,而TCP则使用复位。异常终止一个连接
终止一个连接的正常方式是一方发送FIN,也有可能发送一个复位报文段而不是FIN来中途释放一个连接,这称为异常连接。
异常终止一个连接对应用程序来说有两个优点:(1)丢弃任何待发数据并立即发送复位报文段;(2)RST的接收方会区分另一端执行的是异常关闭还是正常关闭,应用程序使用的API必须提供产生异常关闭而不是正常关闭的手段。RST报文段中包含一个序号和确认序号,另一端根本不会进行确认,将终止该连接,并通知应用层进行连接复位。如下图:收到RST时它产生一个差错:连接被对方复位。
检测半打开连接
如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的TCP连接称为半打开的。
任何一端的主机异常都可能导致这种情况,只要不打算在半打开连接上传输数据,仍处于连接状态的一方就不会检测到另一方已经出现异常。举个例子,当服务器主机出现异常,服务器主机重启后,将丢失复位前连接的所有信息,因此它不知道数据报文段中提到的连接,它会发送复位作为应答。
同时打开
两个应用程序同时彼此执行主动打开的情况也是有可能的。每一方必须发送一个SYN,且这些SYN必须传递给对方,这需要每一方使用一个对方熟知的端口作为本地端口。例如主机A中的一个应用程序使用本地端口7777,并与主机B的端口8888执行主动打开。主机B中的应用程序则使用本地端口8888,并与主机A的端口7777执行主动打开。
TCP特意设计可以处理同时打开,对于同时打开它仅建立一条连接而不是两条连接。
两端几乎同时发送SYN,并进入SYN_SENT状态,当每一端收到SYN时,状态变为SYN_RCVD,同时它们都再发SYN并对收到的SYN进行确认。当双方都收到SYN及相应的ACK后,状态变迁为ESTABLISHED。
可以看到一个同时打开的连接需要交换4个报文段,比正常的三次握手多一个。
同时关闭
双方都执行主动关闭也是可能的,TCP协议允许这样的同时关闭。
双方各发送一个FIN,两个FIN经过网络传送后分别到达另一端,收到FIN后,状态由FIN_WAIT_1变迁到CLOSING,并发送最后的ACK,当收到ACK时,状态变化为TIME_WAIT。
同时关闭与正常关闭使用的段交换数目相同,都是四次。
下一篇:计算机网络之TCP的超时和重传