TCP连接的建立和终止
引言
TCP是一个面向连接的协议。无论是哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。本章讨论一个TCP连接的如何建立和如何终止。
UDP是无连接服务,面向数据报的服务,通信前无需任何预先的握手
连接的建立和终止
tcpdump的输出
这7个TCP报文段仅包含TCP首部。1-3表示3次tcp握手。 4-7表示4次tcp挥手
建立连接协议
- 客户端发送带有SYN标志、ISN(initial sequence number)序列号(1415531521)的报文段1给服务端。
- 服务端接收后,发送确认报文段,带有接收客户端报文段序列号(1415531521)+1的确认序列号和服务端ISN的序列号(1823083521)和ACK标志给客户端。SYN标志占用一个序号
- 客户端接收后,发送确认报文,带有ACK标志,带有接收服务端的报文序列号(1823083521)+1的确认序列号。(ACK不占用序号,只发送TCP首部的确认序列号)
这三个报文段完成连接的建立。这个过程也称为三次握手
发送第一个SYN的一端将执行主动打开(active open)。接收这个SYN并发回下一个SYN的另一端执行被动打开(passive open)。后续介绍都执行主动打开
当一端为建立连接而发送它的SYN时,它会为连接选择一个初始序号。ISN随着时间而变化,因此每个连接都将具有不同的ISN。RFC指出ISN可以看作是一个32比特的计数器,每4ms加1.这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,导致出错
连接终止协议
终止一个连接要经过4次挥手。这是由于TCP的半关闭总成的。TCP连接时全双工,因此每一方向必须单独地进行关闭。当一方完成数据发送任务后就发送一个FIN来终止这个方向的连接。当一端收到一个FIN,它必须通知应用层另一端已经终止了这个方向上的数据传送。发送FIN通常是应用层进行关闭的结果。
收到FIN意味着这个方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据-利用半关闭的应用
首先进行关闭的叫主动关闭,还有一方叫被动关闭。
- 应用程序关闭时,发送FIN标志,下一个序列号(比如说是5)到服务器(一般是客户端主动关闭)。FIN标志将占用一个序列号
- 服务器收到FIN标志后,发送确认报文,带ACK标志,确认客户端的序列号(6)。不占用序列号
- 服务器在处理完后(比如发送完正在给客户端的数据后),再发送FIN报文,带有FIN标志,自己的下一个序列号(比如说19182),确认客户端的序列号(6)。FIN占用一个序列号
- 客户端收到FIN标志后,发送确认报文,带ACK标志,确认服务端的序列号(19183)。不占用序列号
连接建立的超时
当个服务端没有处于正常状态。客户端会尝试多次发送SYN报文,带有超时时限。
服务类型字段
符合【tos 0x10】。这是IP数据报内的服务类型(TOS)字段。表示最小时延
最大报文段长度
最大报文段长度(MSS)表示TCP传往另一端的最大块数据的长度。当一个连接建立时,连接的双方都要通告各自的MSS。
每一方都有用于通告它期望接收的MSS选项。如果一方不接收来自另一方的MSS值,则MSS就定为默认值536字节(这个默认值允许20字节的IP首部和20字节的TCP首部以适合576字节IP数据报)。MSS选项只能出现在SYN报文段中。
如果没有分段发生,MSS越大越好,提供网络利用率。
当要一个本地应用进程要发起一个连接时,或者收到一个连接请求时,往往设置为外出接口上的MTU长度减去固定的IP首部和TCP首部长度。对于以太网,MSS可达1460字节。
在发往“非本地网络”的连接时,设置的MSS往往是536,避免IP数据分组。但是因为中间网络中还是有可能低于536的MTU导致分组。因此只有使用路径上MTU发现机制(24.2节)是唯一的解决办法(不一定啊)。
TCP的半关闭
TCP提供了连接的一段在结束它的发送后还能接收来自另一端数据的能力。这就是半关闭。很少使用
为了使用这个特性,编程接口必须为应用程序提供一种方式来说明“我已经完成了数据传送,因此发送一个文件结束(FIN)给另一端,但我还想接收另一端发来的数据,直到它给我发来文件结束(FIN)”
应用程序需关闭时,调用shutdown而不是close就可以利用这一特性
sun % rsh bsdi sort < datafile
datafile作为标准输入,通过rsh客户端发送到sort服务器,datafile传送完毕后,执行这个TCP连接的半关闭。接着sort服务器在它的连接上收到一个文件结束符,对数据进行排序。rsh等待服务器返回数据。
这是典型的半关闭的使用
TCP的状态变迁图
客户端建立连接时的状态 转化
- 客户端发送SYN标志的报文段后,进入SYN_SEND状态
- 收到服务端的SYN标志,并且发送完ACK后,进入到ESTABLISHED状态
服务端建立连接时的状态转化
- 服务端从(关机重启)closed状态到监听连接完成(listen)
- 收到客户端的SYN,并发送自己SYN、ACK给服务端完成后,进入SYN收到状态(已称为半连接状态)
- 收到客户端的对SYN的ACK后进入到ESTABLISHED
客户端断开连接时的状态转化
- 发送FIN标志的报文段后,进入FIN_WAIT1状态
- 收到ACK,但是没有FIN标志,进入FIN_WAIT2状态
- 收到带有FIN、ACK标志的报文段,并发送完成对FIN的ACK后,进入TIME_WAIT状态(2MSL超时)
- 再次收到带有FIN.ACK的标志报文段,表示发送的ACK未到服务端,重发。重新进入TIME_WAIT状态(2MSL超时),超时后进入CLOSED状态
服务端断开连接时的状态转换
- 收到FIN标志的,并发送完成ACK后,进入到CLOSE_WAIT状态
- 应用程序结束对该连接的传送数据后,发送FIN、ACK(与上面ACK的序号相同)标志的报文段,进入LAST_WAIT状态
- 收到客户端对FIN的ACK后,进入CLOSED状态。如果未收到则重发FIN.ACK标志的报文段
2msl等待状态
TIME_WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段最大生命周期(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间
RFC指出MSL为2分钟,实现中常用值是30s,1分钟,2分钟
设置2msl的意义是,客户端进入TIME_WAIT前发送的ACK报文段如果没收到,这一期间最多占用1MSL时间,服务端会再次发送FIN标志的报文段,又最多占用1MSL时间,因此采用2MSL等待时间的意思是,让客户端两个报文有足够的时间通信完成。
RFC规定在TIME_WAIT状态中的接口不能被使用,但是部分系统实现中任然可以再次使用这些端口。这样新的程序后旧的程序无法区分
平静时间的概念
RFC指出TCP在重启后的MSL秒内不能建立任何连接。这就称为平静时间。极少有系统遵从这一规定,因为重启往往大于这个MSL秒
处于2MSL的超时等待插口的主机出现故障,在MSL秒重新启动,并立即使用故障前仍处于2msl的端口建立连接,那么之前的报文会错误的认为是新连接的报文。平静时间用来解决这个问题
FIN_WAIT2状态
客户端在FIN_WAIT2状态中,服务端在CLOSE_WAIT状态,等待应用程序全关闭发送FIN报文段。但是服务端的应用有可能一直不发送FIN报文段,那么客户端和服务端会一直处于对应状态中,避免永久等待一些系统实现的版本中会有定时器去定时关闭,这是违反协议的。
复位报文段
TCP首部中的RST比特用于复位
的。
无论何时一个报文段发往基准的连接(referenced connection)出现错误,TCP都会发出一个复位报文段。
(这里提到的基准连接是指由目的IP地址和端口号以及源IP地址和端口号指明的连接)
到不存在的端口的连接
产生复位的一种常见情况是当连接请求到达时,目的端口没有进程正在听。对于UDP,会产生一个ICMP端口不可达信息。而TCP则使用复位
在这个例子中复位报文段中确认序号被置为ISN与数据长度(0)、SYN比特所占的1的总和。
异常终止一个连接
正常终止一个连接时一方发送FIN,FIN会在所有排队数据发送之后发送FIN,没有任务数据丢失,有时也称为有序释放。
有时也通过发送一个复位报文段而不是FIN来中途释放一个连接,这称为异常释放(abortive release)
异常终止一个连接对应用程序来说两个优点:
- 丢弃任何待发数据并立即发送复位报文段
- RST的接收方会区分另一端执行的是异常关闭还是正常关闭
使用tcp发送RST来终止连接。RST报文段包含一个序号和确认序号。RST报文段不会导致另一端产生任何响应,另一端不进行确认。收到RST的一方将终止该连接,并通知应用层连接复位
当收到RST时,它产生一个差错,这个差错是连接被对方复位了。
检测半打开连接
如果一方已经关闭或异常终止连接而另一方却不知道,这样的TCP连接称为半打开(half-open)连接。不在半打开连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常
1-3行是正常的连接建立过程。4是发送字符“hi there”,5是ack。6是重连后发送“another line”。第78是arp请求和应答。9是服务端发回了RST标志的报文段。客户收到复位报文段后显示连接已被另一端的主机终止
同时打开
两个应用程序同时彼此执行主动打开的情况是可能的,尽管发生的可能性极小。这又称为同时打开
例如,主机A中的一个应用程序使用本地端口7777,并与主机B的端口8888执行主动打开。主机B中的应用程序则使用本地端口8888,并与主机A的端口7777执行主动打开
TCP是特意设计为了可以处理同时打开,这种情况下仅建立一条连接而不是两条(OSI运输层是两条)
两端几乎在同时发送SYN,并进入SYN_SENT状态。当每一端收到SYN时,状态变为SYN_RCVD(如图18-12),
同时它们都再发SYN并对收到的SYN进行确认。当双方都收到SYN及相应的ACK时,状态都变迁为ESTABLISHED。
图18-17显示了这些状态变迁过程。
同时关闭
双方都执行主动关闭也是可能,TCP协议也允许这样的同时关闭
- 双方各发送一个FIN进入到FIN_WAIT1状态,各自收到后对方的FIN报文段并回复ACK后进入到 CLOSING状态
- 最后收到FIN的ACK后进入到TIME_WAIT状态
TCP选项
TCP首部可以包含选项部分
这些选项一般都只才最新版的TCP版本中实现
- 每个选项的开始1字节的kind字段。说明选项的类型
- 其他选项在kind字节后还有len直接。说明的长度是总长度,包括kind自己和len字节
TCP服务器的设计
大多数的TCP服务器进程是并发的。一个连接请求到底服务器,服务器是通过一个新进程或者线程来处理(取决于操作系统实现)
当一个服务器进程接受来自客户端进程的请求时是如何处理端口的?如果多个连接请求几乎同时到达会发送什么情况?
TCP服务器端口号
- 8080端口处于listen状态
- 第三行的8080端口表示,本地的172ip地址上有建立连接,远端IP地址是217开头的,处于FIN_WAIT1状态
限定的本地IP地址
当使用具体的某个网卡接口作为监听的IP+端口时,客户端只能通过该IP和端口才能与之连接。其他网卡接口连接时发送SYN,都会回复一个RST报文段以拒绝
限定远端IP地址
表中的顺序就是TCP模块收到一个连接请求时确定本地地址的顺序
呼入连接请求队列
当操作系统处理其他进程时,到达的多个连接请求,TCP如何处理?
- 使用固定长度的连接队列,该队列中的连接已被TCP接受(三次握手),但还没有被应用层所接受
- 应用层指明该队列的最大长度。通常是5
- 当一个连接请求(SYN)到达时,TCP使用算法,根据当前连接数来确定是否接受这个连接。队列的长度是应用层所允许接受连接的最大数目。队列的长度是已被TCP接受而等待应用层接受的最大连接数。这个值对系统所允许的最大连接数,并发数并无影响。
- 如果对应新连接请求,该TCP监听的端点的连接队列还有空间,TCP模块将SYN进行确认并完成连接的建立。但应用层只有在三次握手中的第三个报文段收到后才知道这个新连接。当客户端发送完成ACK后,就任务服务器准备好接收数据。(当服务器的TCP连接还在队列中,那么TCP仅将接收的数据放入缓冲队列中)
- 对于新的连接,没有空间时,TCP不会理会收到的SYN,也不发送任何报文段。那么客户端主动打开中间超时
小结
TCP协议交换数据前需要建立连接,UDP无需。
TCP完成数据交换后要关闭这个连接,UDP无需
TCP的三次握手和四次挥手的相应状态转换
一个TCP连接由一个4元祖唯一确定:本地IP地址,本地端口号,远端IP地址和远端端口号。
关闭时要保持TIME_WAIT状态,2MSL的超时时间
习题
18.1在18.2节我们说初始序号(ISN)正常情况下由1开始,并且每0.5秒增加64000,每次执
行一个主动打开。这意味着ISN的最低三位通常总是001。但在图18-3中,两个方向上
ISN中的最低三位都是521。究竟是怎么回事?
答:是每8ms增加1,最低三位不应该通常是0,在18.2节的ISN是系统运行一段时后发起的建立连接请求,所以值较大。那么为啥都是521,难道是巧合么
18.2在图18-15中,我们键入12个字符,看到TCP发送了13个字节。在图18-16中我们键入8
个字符,但TCP发送了10个字符。为什么在第1种情况下增加1个字节,而在第2种情况
下增加2个字节?
答:
18.3半打开连接和半关闭连接的区别是什么?
答:半关闭连接是:一端发送完数据后,发送FIN报文段,等待服务端处理的连接
半打开是:一端已经关闭或异常关闭,另一端并不知道的连接情况
18.4如果启动sock程序作为一个服务器程序,然后终止它(还没有客户进程与它相连接),
我们能立即重新启动这个服务器程序。这意味着它没有经历2MSL等待状态。用状态变
迁来解释这一切。
答:CLOSE->LISTEN->CLOSED->LISTEN
18.5在18.6节我们知道一个客户进程不能重新使用同一个本地端口,如果该端口是仍处于
2MSL等待连接的一部分。但如果sock程序作为客户程序连续运行两次,并且连接到
daytime服务器上,我们就能重新使用同一本地端口。另外,对一个仍处于2MSL等待的
连接,也能为它创建一个替身。这将如何做?
答:重启程序或者重启主机,使用原来的端口。
18.6在18.6节的最后,我们介绍了FIN_WAIT_2状态,提到如果应用程序仅过11分钟后实行
完全关闭(不是半关闭),许多具体的实现都将一个连接由这个状态转移到CLOSED状
态。如果另一端(处于CLOSE_WAIT状态)在宣布关闭(即发送FIN)之前等待了12分
钟,这一端的TCP将如何响应这个FIN?
答:发送RST标志的报文段,重置这个连接。
18.7对于一个电话交谈,哪一方是主动打开,哪一方是被动打开?是否允许同时打开?是否
允许同时关闭?
答:拨号是主动打开,接电话的是被动打开。不能同时打开。允许同时关闭
18.8在图18-6中,我们没有见到一个ARP请求或一个ARP应答。显然主机svr4的硬件地址一
定在bsdi的ARP高速缓存中。如果这个ARP高速缓存不存在,这个图会有什么变化?
答:会先发送ARP请求,获得ARP响应
18.9解释如下的tcpdump输出,并和图18-13进行比较。
答:客户端少了对服务端的FIN的ACK响应。
18.10为什么图18-4中的服务器不将对客户FIN的ACK与自己的FIN合并,从而将报文段数减
少为3个?
答:因为此时服务端可能正在传送数据给客户端,不能终止连接。FIN要在所有数据传送完后发送
18.11在图18-16中,RST的序号为什么是26368002?
答:因为SYN会占用一个序号。因此发送完SYN后,下一个序号就是2638002
18.12TCP向链路层查询MTU是否违反分层的规则?
答:不违反。每一层向下知道。
18.13假定在图14.16中,每个DNS使用TCP而不是UDP进行查询,试问需要交换多少个报文
段?
答:多了3个连接和4个关闭。并且每次查询报文都有确认报文。超时报文会重发。
18.14假定MSL为120秒,试问系统能够初始化一个新连接然后进行主动关闭的最大速率是多
少?
答:1/240个每秒
18.15阅读RFC793,分析处于TIME_WAIT状态的主机收到使其进入此状态的重复的FIN时
所发生的情况。
答:对FIN进行ACK响应,并重新进入TIME_WAIT状态,再次超时2MSL
18.16阅读RFC793,分析处于TIME_WAIT状态的主机收到一个RST时所发生的情况。
答:
18.17阅读HostRequirementsRFC并找出半双工TCP关闭的定义。
答:
18.18在图1-8中,我们曾提到到来的TCP报文段可根据其目的端口号进行分用,请问这种说
法是否正确?
答: