TCP是一个面向连接的协议。无论哪一方向另一方发送数据之前,都必须在双方之间建立一条连接。这种两端间连接的建立与无连接协议UDP不同,UDP向另一端发送数据报时,无需任何预告的握手。
1.建立连接的协议(3次握手)
1)请求端发送一个SYN段指明客户端打算连接的服务器端口,以及初始序列号。
2)服务器发回包含服务器的初始序号的SYN报文段作为应答。同时将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认。一个SYN将占用一个序号。
3)客户端将确认序号设置为服务器的ISN加1以对服务器的SYN报文段进行确认
ISN随时间而变化,因此每个连接都将具有不同的ISN。RFC793指出ISN可以看作是一个32比特的计数器,每4ms加1。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它作出错误的解释。
2.连接终止协议
建议一个连接需要3次握手,为什么终止一个连接需要4次握手?这由TCP的半关闭造成的。既然一个TCP连接是全双工(数据在两个方向能同时传递),因此每个方向必须单独地进行关闭。这原则就是当一方完成它的数据发送后就能发送一个FIN来终止这个方向数据。当一端收到一个FIN,它必须通知应用层另一端已经终止了那个方向的数据传送。
收到FIN包意味着在这一方向上没有数据流动。一个TCP连接在收到一个FIN后仍能发送数据。
3.最大报文段长度
最大报文段长度(MSS)表示TCP传往另一端的最大数据长度(不包括20字节的IP首部和TCP首部)。当一个连接建立时,连接的双方都要通告各自的MSS,MSS只能出现在SYN报文段中,这是一个“协商”选项,并不是强制性的。如果一方不接收来自另一方的MSS值,则MSS默认值为536字节。MSS值一般为链路层的MTU减去20字节的IP首部和20字节的TCP首部. MSS = MTU - 40Byte
一般来说,如果没有分段发生,MSS还是越大越好。报文段越大允许每个报文段传送的数据就越多,相对IP和TCP首部有更高的网络利用率。
MSS让主机限制另一端发送数据报的长度。加上主机也能控制它发送数据报的长度,这将使以较小MTU连接到网络上的主机避免分段。
4.TCP半关闭
TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这就是所谓的半关闭。
当客户端发送FIN到服务端,服务端返回这个FIN的ACK后,并没有向客户端发送FIN报文段,这段时间内就叫半关闭状态。此时客户端不能向服务端发送数据,但可以接收服务端发送给她的数据。
没有半关闭,需要其它的一些技术让客户端通知服务器,客户端已经完成了它的数据传送,但仍要接收来自服务器的数据。使用两个TCP连接也可以作为一个选择,但使用半关闭的单连接更好。
5.TCP状态变迁图
5.1 这是一个看起来比较复杂的状态迁移图,因为它包含了两个部分---服务器的状态迁移和客户端的状态变迁。
TCP正常连接建立和终止所对应的状态,对这11个状态中较重要的部分,在以后的博文再进行讲解。
TIME_WAIT状态也称为2MSL状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL,它是任何报文段被丢弃前在网络内的最长时间,而这个时间是有限的。
当TCP执行主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留2倍的MSL时间。这样可以让TCP再次发送最后的ACK以防止这个ACK丢失(另一端超时并重发最后的FIN)。插口对(socket pair):(包括服务端ip,port和客户端ip,port的四元组)唯一确定每个TCP连接。
这种2MSL等待的另一个结果是这个TCP连接等待期间,定义这个连接的插口不能再被使用,这个连接只能在2MSL结束才能再被使用。
在连接处于2MSL等待时,任何迟到的报文段将被丢弃,因为处于连接的插口不能被再用,因此当要建立一个有效的连接时,来自该连接的一个较早替身的迟到报文段作为新连接的一部分很有可能被曲解。
现在问题来了,客户执行主动关闭并进入TIME_WAIT是正常的。服务器通常执行被动关闭不会进入TIME_WAIT状态。这表明如果我们终止一个客户程序,并立即重启这个客户程序,则这个新客户程序不能使用相同的本地端口,这不会带来什么问题,因为客户使用本地端口,而并不关心这个端口号是什么。
然而,对于服务器就存在问题,因为服务器使用熟知端口。如果我们终止一个已经建立连接的服务器程序,并试图立即重启这个服务器程序,这个熟知端口不能赋值给服务器程序,因为该端口是处于2MSL连接的一部分。在重启服务器程序前,它需要1~4分钟。(如果我们一直试图重新启动服务器程序,并测试它直到成功所需的时间,我们就能估算出2MSL值)
TCP实现加强了该限制,默认在2MSL等待状态下,插口中使用的本地端口也不能使用,但通过SO_REUSEADDR参数可以重用该端口。注意:2MSL状态时该插口对不能使用是针对于主动关闭一方。 假如服务端主动关闭,然后用SO_REUSEADDR重用该端口,如果直接用2MSL的插口对连接客户端将不会成功,但如果客户端使用插口对中的端口来连接服务端,那么将是成功的,虽然该插口对还处于2MSL状态。即允许一个连接到达仍处于2MSL状态的连接。
5.3 平静等待时间
对于来自某个连接的较早替身的迟到报文段,2MSL等待可防止将它解释成使用相同插口对的新连接的一部分。但这只有在2MSL等待连接中的主机处于正常工作状态时才有效。
如果使用2MSL等待的主机出现故障并在MSL内重启,并立即使用处于2MSL的插口对来建立一个新的连接,从而故障前发出而迟到的报文段会被错误地当作属于重启后新连接的报文段。
为了防止这种情况,RFC793提出TCP在重启后的MSL秒内不能建立任何连接,这就是平静等待时间(quiet time)。
只有极少的实现版准遵守这一原则,因为大多数主机重启时间的时间都比MSL秒要长。
5.3 FIN_WAIT_2状态
主动关闭方已经发出了FIN,并且被动关闭方也已对它进行ACK确认,那么主动方进入FIN_WAIT_2状态。只有当被动方的进程完成这个关闭(发出FIN包),主动方才从FIN_WAIT_2进入TIME_WATI状态。
这意味着主动和被动方分别永远保持FIN_WAIT_2和CLOSE_WAIT状态,并一直保持这个状态直到应用层决定进行关闭。(许多伯克利实现采用以下方法来防止这种FIN_WAIT_2状态的无限等待,如果执行主动关闭的应用层将进行全关闭,而不是半关闭,表示它不想接收数据,就设置一个定时器,如果这个连接空闲超过10分钟75秒,那么TCP将进入CLOSED状态)
6.复位报文段
连接不存在的端口:
一般来说,无论何时一个报文段发往基准的连接出现错误,TCP都会发出一个复位报文段。产生复位的一种常见情况是当连接请求到达时,目的端口没有进程正在监听,例如当一个数据报达到目的端口时,该端口没在使用,它将产生一个端口不可达的信息,而TCP则使用复位。
异常终止一个连接:
终止连接有2个方式,其中一个是正常方式:一方发送FIN,也称为有序释放,正常情况下没有任何数据丢失。但也有可能发送一个复位报文段而不是FIN来中途释放一个连接,称为异常释放。
异常终止一个连接对应用程序来说有2个优点:
1)丢弃任何待发数据并立即发送复位报文段
2)RST的接收方会区分另一端执行的是异常关闭还是正常关闭。
需要注意的是RST报文段不会导致另一端产生任何响应,另一端根本不进行确认。收到RST的一方将终止连接,并通知应用层连接复位。
检测半打开连接:
如果一方已经关闭或异常终止连接而另一方却还不知道,我们将这样的TCP连接称为半打开的(Half-Open)。任何一端的主机异常都可能导致发生这种情况。只要不打算在半打开连接上传输数据,仍处于连接状态的一方就不会检测另一方已经出现异常。
半打开连接的另一个常见原因是当客户主机突然掉电而不是正常的结束客户应用程序后再关机。此时如果服务器又重新启动,它将丢失复位前连接的所有信息,因此它不知道数据报文段中提到的连接。此时TCP处理的原则是接收方以复位作为应答。
7.同时打开
两个应用程序同时彼此执行主动打开的情况是可能的,TCP特意设计可以处理同时打开,对于同时打开它仅建立一条连接而不是两条连接。
同时打开时,两端几乎同时发送SYN,并进入SYN_SENT状态。当每一端收到SYN时,状态变为SYN_RCVD,同时它们都再发SYN并对收到的SYN进行确认。当双方都收到SYN相应的ACK时,状态都变迁为ESTABLISH。
一个同时打开的连接需要交换4个报文段,比正常的三次握手多1个,如下图所示:
双方同时执行主动关闭是可能的,TCP协议也允许这样的同时关闭。
当应用层发出关闭命令时,两端均从ESTABLISH变为FIN_WAIT1,这将导致双方各发送一个FIN,两个FIN经过网络传送后分别到达另一端。收到FIN后,状态由FIN_WAIT1变迁到CLOSING,并发达最后的ACK,状态变化为TIME_WAIT。
同时关闭与正常关闭使用的段交换数据相同。
8.TCP服务器的设计
8.1 TCP服务器端口号:
TCP服务器对于每一个连接都需要建立一个独立的进程(或者是轻量级的,线程),来保证对话的独立性。所以TCP服务器是并发的。
当一个服务器进程接受一个来自客户端的请求时是如何处理端口的?如果多个连接请求几乎同时到达会发生什么情况?
1)首先使用neststat -a -n可以查看网络中的所有主机端,假定端口号23为监听端口,在没有任何请求连接时输入命令可以看到23端口处于LISTEN监听状态。(Foreign Address为*.*表示还不知道远端IP地址和端口号,因为该端还处于LISTEN状态,正等待连接请求的到达)
2)从客户端发起一个服务端23端口的连接,使用命令可以看到处于LISTEN状态的服务器进程仍然存在(23端口),用于接收其它的连接请求。当传入的连接请求到达并被接收时,系统内核中的TCP模块就创建一个处于ESTABLISHED状态的端点(23端口)。另外,处于ESTABLISHED状态的连接端口不会变化,也是23,与处于LISTEN状态的进程相同。
3)在使用端口23的进程中,只有处于LISTEN的进程能够接收新的连接请求。处于ESTABLISHED的进程将不能接收SYN报文段,而处于LISTEN的进程将不能接收数据报文段。
8.2 TCP处理呼入连接请求规则:
(1)正等待连接的一端有一个固定长度的连接队列,该队列中的连接已经完成3次握手,但还没有被应用层接收。TCP服务器无法使客户端的主动打开失效.因为服务器接收到请求时,TCP的三次握手已经完成。所以对于限定远程IP地址的服务器,必须在客户端三次握手建立连接后才能判断是否合法。
作者原创,转载请标明原处:http://blog.csdn.net/xifeijian/article/details/44250879
《TCP/IP详解》读书笔记(18章)-TCP连接的建立与中止
原文地址:http://blog.csdn.net/xifeijian/article/details/44250879