zoukankan      html  css  js  c++  java
  • 《TCP/IP具体解释》读书笔记(18章)-TCP连接的建立与中止

    TCP是一个面向连接的协议。不管哪一方向还有一方发送数据之前。都必须在两方之间建立一条连接。这样的两端间连接的建立与无连接协议UDP不同。UDP向还有一端发送数据报时,无需不论什么预告的握手。


    1.建立连接的协议(3次握手)

    1)请求端发送一个SYN段指明client打算连接的serverport,以及初始序列号。

    2)server发回包含server的初始序号的SYN报文段作为应答。同一时候将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认。一个SYN将占用一个序号。

    3)client将确认序号设置为server的ISN加1以对server的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提供了连接的一端在结束它的发送后还能接收来自还有一端数据的能力。这就是所谓的半关闭。

    当client发送FIN到服务端,服务端返回这个FIN的ACK后,并没有向client发送FIN报文段,这段时间内就叫半关闭状态。

    此时client不能向服务端发送数据,但能够接收服务端发送给它的数据。(书中有一个半关闭使用的样例)

    没有半关闭。须要其他的一些技术让client通知server,client已经完毕了它的数据传送,但仍要接收来自server的数据。使用两个TCP连接也能够作为一个选择。但使用半关闭的单连接更好。


    5.TCP状态变迁图

    5.1 这是一个看起来比較复杂的状态迁移图。由于它包含了两个部分---server的状态迁移和client的状态变迁。



    TCP正常连接建立和终止所相应的状态。这11个状态中较重要的部分,在以后的博文再进行解说。


    5.2 2MSL等待状态

    TIME_WAIT状态也称为2MSL状态。

    每一个详细TCP实现必须选择一个报文段最大生存时间MSL,它是不论什么报文段被丢弃前在网络内的最长时间,而这个时间是有限的。

    当TCP运行主动关闭,并发回最后一个ACK。该连接必须在TIME_WAIT状态停留2倍的MSL时间。

    这样能够让TCP再次发送最后的ACK以防止这个ACK丢失(还有一端超时并重发最后的FIN)。


    插口对(socket pair):(包含服务端ip。port和clientip,port的四元组)唯一确定每一个TCP连接。

    这样的2MSL等待的还有一个结果是这个TCP连接等待期间,定义这个连接的插口不能再被使用,这个连接仅仅能在2MSL结束才干再被使用。

    在连接处于2MSL等待时,不论什么迟到的报文段将被丢弃,由于处于连接的插口不能被再用,因此当要建立一个有效的连接时,来自该连接的一个较早替身的迟到报文段作为新连接的一部分非常有可能被曲解。

    如今问题来了,客户运行主动关闭并进入TIME_WAIT是正常的。

    server通常运行被动关闭不会进入TIME_WAIT状态。这表明假设我们终止一个客户程序,并马上重新启动这个客户程序。则这个新客户程序不能使用同样的本地port,这不会带来什么问题。由于客户使用本地port,而并不关心这个port号是什么。

    然而。对于server就存在问题,由于server使用熟知port。

    假设我们终止一个已经建立连接的server程序,并试图马上重新启动这个server程序。这个熟知port不能赋值给server程序。由于该port是处于2MSL连接的一部分。在重新启动server程序前,它须要1~4分钟。(假设我们一直试图又一次启动server程序,并測试它直到成功所需的时间,我们就能估算出2MSL值)

    TCP实现加强了该限制,默认在2MSL等待状态下,插口中使用的本地port也不能使用。但通过SO_REUSEADDR參数能够重用该port。

    注意:2MSL状态时该插口对不能使用是针对于主动关闭一方。 假如服务端主动关闭,然后用SO_REUSEADDR重用该port。假设直接用2MSL的插口对连接client将不会成功,但假设client使用插口对中的port来连接服务端。那么将是成功的,尽管该插口对还处于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包),主动方发出ack,主动方才从FIN_WAIT_2进入TIME_WATI状态。

    假设被动方一直不发出FIN包。这意味着主动和被动方分别永远保持FIN_WAIT_2和CLOSE_WAIT状态。并一直保持这个状态直到应用层决定进行关闭。(很多伯克利实现採用下面方法来防止这样的FIN_WAIT_2状态的无限等待,假设运行主动关闭的应用层将进行全关闭,而不是半关闭,表示它不想接收数据,就设置一个定时器。假设这个连接空暇超过10分钟75秒,那么TCP将进入CLOSED状态


    6.复位报文段

    连接不存在的port:

    一般来说,不管何时一个报文段发往基准的连接出现错误,TCP都会发出一个复位报文段。

    产生复位的一种常见情况是当连接请求到达时,目的port没有进程正在监听。比如当一个数据报达到目的port时,该port没在使用。它将产生一个port不可达的信息,而TCP则使用复位。

    异常终止一个连接:

    终止连接有2个方式。当中一个是正常方式:一方发送FIN,也称为有序释放。正常情况下没有不论什么数据丢失。但也有可能发送一个复位报文段而不是FIN来中途释放一个连接,称为异常释放。

    异常终止一个连接相应用程序来说有2个长处:

    1)丢弃不论什么待发数据并马上发送复位报文段

    2)RST的接收方会区分还有一端运行的是异常关闭还是正常关闭。

    须要注意的是RST报文段不会导致还有一端产生不论什么响应。还有一端根本不进行确认。收到RST的一方将终止连接,并通知应用层连接复位。

    检測半打开连接:

    假设一方已经关闭或异常终止连接而还有一方却还不知道。我们将这样的TCP连接称为半打开的(Half-Open)

    不论什么一端的主机异常都可能导致发生这样的情况。仅仅要不打算在半打开连接上数据传输。仍处于连接状态的一方就不会检測还有一方已经出现异常。

    半打开连接的还有一个常见原因是当客户主机突然掉电而不是正常的结束客户应用程序后再关机。此时假设server又又一次启动,它将丢失复位前连接的全部信息。因此它不知道数据报文段中提到的连接。此时TCP处理的原则是接收方以复位作为应答。


    7.同一时候打开

    两个应用程序同一时候彼此运行主动打开的情况是可能的。TCP特意设计能够处理同一时候打开,对于同一时候打开它仅建立一条连接而不是两条连接。

    同一时候打开时,两端差点儿同一时候发送SYN。并进入SYN_SENT状态。

    当每一端收到SYN时,状态变为SYN_RCVD,同一时候它们都再发SYN并对收到的SYN进行确认。当两方都收到SYN相应的ACK时。状态都变迁为ESTABLISH。

    一个同一时候打开的连接须要交换4个报文段。比正常的三次握手多1个,例如以下图所看到的:



    7.同一时候关闭

    两方同一时候运行主动关闭是可能的,TCP协议也同意这样的同一时候关闭。

    当应用层发出关闭命令时,两端均从ESTABLISH变为FIN_WAIT1,这将导致两方各发送一个FIN,两个FIN经过网络传送后分别到达还有一端。收到FIN后。状态由FIN_WAIT1变迁到CLOSING,并发达最后的ACK。状态变化为TIME_WAIT。

    同一时候关闭与正常关闭使用的段交换数据同样。



    8.TCPserver的设计


    8.1 TCPserverport号:

    TCPserver对于每一个连接都须要建立一个独立的进程(或者是轻量级的。线程),来保证对话的独立性。所以TCPserver是并发的。

    当一个server进程接受一个来自client的请求时是怎样处理port的?假设多个连接请求差点儿同一时候到达会发生什么情况?

    1)首先使用neststat -a -n能够查看网络中的全部主机端。假定port号23为监听port。在没有不论什么请求连接时输入命令能够看到23port处于LISTEN监听状态。

    (Foreign Address为*.*表示还不知道远端IP地址和port号。由于该端还处于LISTEN状态。正等待连接请求的到达)

    2)从client发起一个服务端23port的连接,使用命令能够看到处于LISTEN状态的server进程仍然存在(23port)。用于接收其他的连接请求。当传入的连接请求到达并被接收时,系统内核中的TCP模块就创建一个处于ESTABLISHED状态的端点(23port)。另外,处于ESTABLISHED状态的连接port不会变化,也是23,与处于LISTEN状态的进程同样。

    3)在使用port23的进程中,仅仅有处于LISTEN的进程能够接收新的连接请求。

    处于ESTABLISHED的进程将不能接收SYN报文段,而处于LISTEN的进程将不能接收数据报文段。


    8.2 TCP处理呼入连接请求规则:

    (1)正等待连接的一端有一个固定长度的连接队列,该队列中的连接已经完毕3次握手,但还没有被应用层接收。


    (2)应用层指定这个连接队列的最大长度。这个值通常叫做积压值(backlog)。取值范围为0至5的整数。


    (3)当一个请求连接到达(SYN),TCP依据连接队列中的连接数确认是否接收这个连接,但这时的最大排队连接数并不等于积压值。
    (4)假设连接队列中的连接数少于最大排队的连接数,TCP将确认建立连接。在client主动连接成功而服务端应用层还没接收这个连接时。client发送的数据将保存在服务端的TCP缓存队列。
    (5)假设连接队列没有空间,TCP将丢弃收到的SYN请求。不发回不论什么报文(包含RST),client将超时重传SYN请求,等待连接队列有空间。


    TCPserver无法使client的主动打开失效。由于server接收到请求时。TCP的三次握手已经完毕。所以对于限定远程IP地址的server,必须在client三次握手建立连接后才干推断是否合法。

    作者原创,转载请标明原处:http://blog.csdn.net/xifeijian/article/details/44250879

  • 相关阅读:
    javascript实现优先队列
    javascript中的队列结构
    mysql及php命名规范
    javascript使用栈结构将中缀表达式转换为后缀表达式并计算值
    【转】感知哈希算法——找出相似的图片
    重新注册iis的.NET Framework版本
    Extjs GridPanel用法详解
    Extjs Window用法详解
    Extjs Form用法详解(适用于Extjs5)
    Extjs MVC开发模式详解
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5056022.html
Copyright © 2011-2022 走看看