精华内容
下载资源
问答
  • rfc793
    更多相关内容
  • rfc793(tcp传输控制协议),tcp协议标准规范...
  • RFC793中文版

    千次阅读 2021-02-20 22:29:50
    传输控制协议(TRANSMISSION CONTROL PROTOCOL) DARPA INTERNET程序 协议规范 1.介绍 传输控制协议在包交换计算机通讯网络和这些网络的互联系统中作为一个高可靠性的主机到主机协议使用。 本文档描述了传输控制协议...

    转自:https://blog.csdn.net/dlmu2001/article/details/1184366

    传输控制协议(TRANSMISSION CONTROL PROTOCOL)
    DARPA 互联网程序
    协议规范

    1981年9月

    1.简介

    传输控制协议(TCP)在交换计算机通讯网络和这些网络的互联系统中作为一个高可靠性的主机到主机协议使用。

    本文档描述了传输控制协议执行的功能,实现的程序,和程序接口或者要求它的服务的用户。

    1.1 动机(Motivation)

    计算机通信系统在军事、政府和民用环境中起着越来越重要的作用。本文档主要将注意力集中在军用计算机通讯要求,尤其是在不可靠通信中的坚固和在拥塞情况下的可用,但是很多这样的问题在民用和政府用途环境中也会碰到。

    随着战略上和战术上的计算机通讯网络的发展和应用,提供一种方法来连接这些网络,以及提供可用的支持大量应用程序的标准进程间通信协议是必要的。预料到该标准的需要,国防研究和工程副部长宣告了这里描述的TCP协议,来作为DoD范围的互联协议标准的基础。

    TCP是一个基于连接的、端到端的可靠协议,该协议设计以用来适应一个支持多个网络应用程序的层间协议结构。TCP提供了在属于不同的但是是互联的计算机通信网络的宿主主机中的进程对间的可靠进程间通讯。在TCP层之下,很少考虑到通信协议的可靠性。TCP假定它可以从底层协议获得一个简单的、潜在的不可靠数据报。原理上,TCP必须能够在一个从有线连接到包交换或者回路交换网络的比较大范围的通讯系统上工作。TCP基于Cerf和Kahn在[1]中第一次描述的概念。TCP适用于一个层间协议架构,该架构在一个基本的Internet协议之上,Internet协议为TCP提供了一种发送和接收封装在internet数据报中的可变长度分片的方法。internet数据报提供了在不同网络中寻址源和目的TCPs的方法。Internet协议也处理为在多个网络和互联网关上取得传输和投递所进行的TCP分片的分片和重组。internet协议也携带优先级、安全分类和TCP分片的分隔,因此这些信息可用通过多个网络进行端到端传输。

    本文档大部分是在宿主计算机中位于高层协议下的TCP实现上下文中写的。有些计算机网络会通过承载有TCP和ip层以及网络特定软件前端计算机连接到网络。TCP规范描述了到高层协议的接口,这些接口即使对于前端情况也是
    可实现的,同时一个适合的主机到前台(host-to-front)端协议也被实现。

                              Protocol Layering
                            +---------------------+
                            |     higher-level    |
                            +---------------------+
                            |        TCP          |
                            +---------------------+
                            |  internet protocol  |
                            +---------------------+
                            |communication network|
                            +---------------------+
    
                                    Figure 1
    

    1.2 范围(Scope)

    TCP用来在多网络环境下提供一个可靠的进程到进程通讯服务。TCP用来作为在多网络条件下的通用的主机到主机协议。

    1.3 关于这个文档

    本文档提出了任何TCP实现所要求的行为的规范,包括同高层协议的交互以及同其它TCPs的交互。文档的其它部分提高了一个协议接口和操作的简要视图。第二部分概要描述了TCP设计的理论基础,第三部分提供了不同事件发生情况下(新分片到达,用户调用,错误等)的TCP要求的行为和TCP分片的详细描述。

    1.4 接口

    TCP接口一端是用户或者应用程序,另一端是底层协议如IP协议。

    应用程序和TCP间的接口将详细阐述。该接口包含一套调用,类似于操作系统提供给应用程序操作文件的接口。比如,有打开和关闭连接以及在已经建立的连接上发送数据报的接口。同时希望TCP可以异步地同几个应用程序通信。虽然TCP的实现者有一定的自由设计适合于特定操作系统环境的接口,对任何一个合法的实现来说,都需要实现一个TCP/用户接口的最小接口集合。

    除了认为存在一个机制可以互相异步地传递信息,TCP和底层协议的接口没有特别指定。通常希望底层协议来指定该接口。TCP设计来在一个非常通用的互联网络上工作。

    本文档假定的底层协议是Internet协议[2]。

    1.5 操作

    如上所述,TCP的一个主要目的是在进程间提供可靠的,安全的逻辑回路或者连接服务。

    为了在一个比较不可靠的相互通信上提供这个服务,系统要求提供如下功能:

    • 基本的数据传输(Basic Data Transfer)
    • 可靠性(Reliability)
    • 流控(Flow Control)
    • 多路(Multiplexing)
    • 连接(Connections)
    • 优先级和安全性(Precedence and Security)

    TCP在这些领域的基本操作在下面的段落中描述。

    基本数据传输:
    TCP通过将一定量的字节打包成在internet系统上传输的分片,能够在用户之间在两个方向上传输连续的字节流。通常情况下,TCPs决定什么时候阻塞以及前推数据。

    有时候用户需要确保他们所提交给TCP的所有数据都被传输。基于这个目的,定义了push功能。为了确认提交给TCP的数据报确实被传送了,发送用户指示数据必须被推给接收用户。Push导致了TCPs立即前推和投递数据给接收者。确切的push点对接收用户可能不可见,且push功能不提供一个记录边界标识。

    可靠性(Reliability):
    TCP必须从被internet通信系统破坏的,丢失的,复制的或者非正确顺序投递的数据中恢复。这是通过给每个传输的字节流分片一个系列号实现的,且要求从接收TCP有肯定的确认(ACK)。如果ACK在一个间隔内(timeout interval)没有收到,数据就重传。在接收端,系列号用来正确对分片排序(分片可能次序颠倒)以及排除重复分片。通过给每个传输分片增加一个校验和来处理损坏,在接收端进行检查,且丢弃损坏的分片。

    流量控制(Flow Control):
    TCP为接收者提供一个办法让其控制发送者发送的数据的数量。这是通过在每个ACK中返回一个窗口(“window”)来指示超过最后成功接收的一个分片的可接受的系列号码的范围。窗口指示了发送者在接收到进一步的允许前可以传输的字节的数量。

    多路技术(Multiplexing):
    为了允许在一个单独的主机里多个进程同时使用TCP通信机制,TCP提供了一套地址和端口。从internet通信层同网络和宿主地址连接,这形成了一个socket。一对socket标识了一个连接。也就是说,一个socket可能同时被使用在多个连接中。

    绑定端口到进程被每个主机单独处理。但是,将常用的进程(如“logger”或者时间服务)隶属于众所皆知的socket被证明是有用的。这些服务就可以通过已知的地址获取到。建立和学习其它进程的端口地址可能包括更加动态的机制。

    连接(Connections):
    上面描述的可靠性和流量控制机制要求所有的TCPs为每个数据流发起和维护某些状态信息。这些信息的结合体,包括sockets,系列号,和窗口大小,被称为一个连接。每个连接被一套指定两端的sockets唯一指定。

    当两个进程需要通信的时候,他们的TCPs必须首先建立一个连接(在每一端初始化状态信息)。当通信完成的时候,连接终止或者关闭以释放资源用于其它用途。

    由于连接必须在不可靠的主机和不可靠的internet通信系统上建立,一个带有基于时钟的系列号的握手机制被用来避免连接的错误初始化。

    优先级和安全(Precedence and Security):

    TCP用户可以指示通信的安全性和优先级。当这些特性不需要的时候,规定采用缺省值。

    2.体系(PHILOSOPHY)

    2.1 Internetwork系统的元素

    internetwork环境由连接到依次通过网关互联的网络的主机组成。这里的网络有可能是局域网络(比如 ETHERNET)或者大网络(比如ARPANET),但是任何一种情况都是基于包交换技术。产生和消费消息的活动代理是进程。在网络,网关和主机上不同层次的协议支持一个通信系统,该系统提供了进程端口之间在逻辑连接上的双向数据流。

    术语包(packet)用在这里表示一个主机和它的网络之间的一次事务的数据。我们通常不用关心一个网络内交换的数据块的格式。

    主机是附着在一个网络上的计算机,从通信网络的观点来看,是包的源和目的地。进程在主机里被视为活动的元素(根据相对普遍的定义,进程是在执行的程序)。终端和文件或者其它I/O设备都被视为通过进程的使用彼此通信的东东。因此,所有的通信被视为进程间通信。

    既然一个进程可能需要在它自己和其它进程间区分通信流,我们想像每个进程可以有几个端口,通过这些端口,进程同其它进程的端口进行通信。

    2.2 操作模型

    进程通过调用TCP传输数据,调用时传递数据的缓冲。TCP将这些缓冲的数据打包成分片,然后调用internet模块来传输每个分片到目的TCP。接收TCP将一个分片中的数据放置到接收用户缓冲,然后通知接收用户。TCPs在分片里面包含了控制信息来确保可靠有序的数据传输。

    Internet通信模型是由一个同每个TCP有联系的internet协议模块提供了到局域网络的接口,这个internet模块组装TCP分片到internet数据报,然后为这些数据报选路到一个目的internet模块或者中继网关。为了在局域网络上传输数据报,它被包含在一个局域网络桢中。

    包交换可以执行进一步的包装,分片或者其它操作以投递局域网包到目的internet模块。

    在一个网络间的网关上,从它的局域网包上来的internet数据报是未经包装的(unwrapped),这些数据报被检查以确定数据报下一个要经过的网络。然后Internet数据报被包装成适合于下一个网络的局域包,然后被选路到下一个网关或者最后的目的地。

    允许网关将一个internet数据报分解成更小的internet数据报分片,如果这在下一个网络中传输是必要的话。要这样做,网关要产生一套internet数据报,每个携带一个分片。分片在接下去的网关中可能会进一步分解成更小的分片。Internet分片设计成目的internet模块可以组装分片成一个internet数据报的格式。

    目的internet模块从数据报解开分片后(如果需要,经过重组数据报)传给目的TCP。

    这个简单的操作被多个细节注释。一个重要的特性是服务类型。这给网关(或internet模块)提供了指导它选择在下一个网络上传输的服务参数的信息。包含在服务类型信息里面的是数据报的优先级信息。数据报也可以携带安全信息来允许主机和操作在不同安全级别环境上的网关来基于安全考虑正确隔离数据报。

    2.3 主机环境

    TCP被当作操作系统中的一个模块。用户就像他们访问文件系统那样访问TCP。TCP可以调用其它操作系统接口,比如,管理数据结构的接口。假设真正的对网络的接口由设备驱动模块控制。TCP不直接调用网络设备驱动,而是调用internet数据报协议模块,在internet数据报协议模块中调用设备驱动。

    TCP机制并不排除在一个前端(台)处理器(front-end processor)上的TCP实现。但是,在这样一个实现中,一个主机到前台(host-to-front-end)的协议必须提供功能来支持在本文档中描述的TCP用户接口的类型。

    2.4 接口

    TCP/用户接口提供了用户可以调用的TCP的接口,这些接口用来打开或者关闭一个连接,发送或者接收数据,或者获取一个连接的状态信息。这些接口同操作系统中其它用户的接口相似,比如,打开,读取或者关闭一个文件的接口。

    TCP/internet接口提供了在internet系统中发送和接收寻址到主机上的TCP模块的数据报。这些接口中可以传递地址(address),服务类型(type of service),优先级(precedence),安全(security)和其它控制信息。

    2.5 同其它协议的关系

    下图展示了TCP在协议层次结构中的地位:

           +------+ +-----+ +-----+       +-----+                    
           |Telnet| | FTP | |Voice|  ...  |     |  Application Level 
           +------+ +-----+ +-----+       +-----+                    
                 |   |         |             |                       
                +-----+     +-----+       +-----+                    
                | TCP |     | RTP |  ...  |     |  Host Level        
                +-----+     +-----+       +-----+                    
                   |           |             |                       
                +-------------------------------+                    
                |    Internet Protocol & ICMP   |  Gateway Level     
                +-------------------------------+                    
                               |                                     
                  +---------------------------+                      
                  |   Local Network Protocol  |    Network Level     
                  +---------------------------+                      
      
                             Protocol Relationships
                                   Figure 2.
    

    TCP被期望能够有效支持高层协议。必须容易确定高层协议如ARPANET Telnet或者AUTODIN II THP和TCP的接口

    2.6 可靠通信(Reliable Communication)

    在一个TCP连接上的数据流可以可靠的顺序地投递到目的地。

    传输通过系列号(sequence numbers)和确认(acknowledements)来确保可靠。概念上,每八位字节数据分配一个系列号码。一个分片里面的第一个八位字节数据同分片一块传输,被

    称为分片系列号码。分片也携带一个确认号码,该确认号码是在相反方向的下一个期望的传输八位字节数据的系列号码。当TCP传输一个包含数据的分片的时候,他将该数据分片的拷贝放在重传队列中,然后开始一个定时器,当数据的确认收到的时候,该分片拷贝从队列中删除,如果在定时到达之前没有收到确认,分片被重传。

    TCP的确认并不保证数据被投递到最后的用户,只是保证接收TCP将负起该责任。

    为了控制TCPs之间的数据流量,采用了一个流量控制机制。接收TCP报告一个“窗口”(window)来发送TCP。这个窗口指定了接收TCP当前准备接收的数据的字节数目,该数据字节从确认号码开始。

    2.7 连接建立和清除(Connection Establishment and Clearing)

    为了鉴别TCP可能要处理的分散的数据流,TCP提供了一个端口标识。因为端口标识是每个TCP单独选择了,他们可能不是唯一的。为了在每个TCP内提供一个唯一地址,我们将标识TCP的一个internet地址和一个端口标识结合起来创建一个socket,这在所有互联的网络上是唯一的。

    一个连接由两端的socket对完全指定。一个本地的socket可以参与到多个外部socket的多个连接。一个连接可以用来携带两个方向的数据,这就是“全双工”(full duplex)。

    TCP可以自由地将端口和他们选择的进程联系起来。但是,在任何实现中,有些基本概念是必须的。必须有些众所皆知的sockets,在这些socket上TCP仅通过一些方法同合适的进程联系在一起。我们想像进程可以“拥有”端口,且这些进程仅可以在他们拥有的端口上发起连接。(实现拥有权的方式是一个本地问题,但我们想像一个请求端口用户命令,或者唯一分配一组端口给一个指定进程的方法,比如,将端口名字的高位同一个指定的进程联系在一起)

    连接由带有本地端口和外部socket参数的OPEN调用被指定。反过来,TCP提供了一个(短的)本地连接名字,通过这个名字,用户可以在接下来的调用中索引到这个连接。这里有些关于一个连接的信息必须被记住。为了存储这些信息,我们假定有个称为传输控制块(Transmission Control Block :TCB)的数据结构。一个实现策略是将一个指向该连接的TCB的指针作为本地连接名字。OPEN调用也指定了连接的建立是主动要求的,还是被动接受的。

    被动OPEN请求意味着进程要接受到来的连接请求而不是尝试初始化一个连接。通常请求一个被动OPEN的进程将接受任何调用者的连接请求。在这种情况下,全0的外部socket用来指示一个未指定的socket。未指定的外部socket仅在被动OPENs被允许。

    一个服务进程如果希望为其它未知进程提供服务,要以未指定的外部socket发起一个被动的OPEN请求。然后连接可以通过请求连接到该本地socket的任何进程建立。如果这个本地socket已知用来同这个服务联系,这将是有帮助的。

    将一个socket地址同标准服务联系起来的这种众所皆知的socket是一个便利的机制。比如,“Telenet-Server”进程永久地分配给一个特定的socket,其它的soket保留给文件传输(File Transfer),远程工作入口(Remote Job Entry),文本产生器(Text Generator),回声(Echoer)和Sink进程(后面三个进程测试用)。一个socket地址可能被保留来访问一个“Look-Up”服务,该服务将返回指定的socket,在该socket上一个新创建的服务将被提供。众所皆知的socket是TCP规范的一部分,但是分配socket给服务超出了这个规范。(参见[4])

    进程可以创建(issue)被动OPENs,然后等待其它进程的匹配的主动OPENs,当连接建立时,TCP就会通知它。在同一时间彼此向对方发起主动OPENs的两个进程将被正确连接。这个弹性对支持分部式计算是至关重要的,在分部式计算中,各个部分异步行动,不考虑对方。

    在本地被动OPENs和一个外部主动OPENs时有两种匹配socket的基本情况。第一种情况,本地被动OPENs完全指定了外部socket,在这种情况中,匹配是精确的。第二种情况,本地被动OPENs未指定外部socket,在这种情况下,任何外部的socket只要本地socket匹配就被接受。其它可能的情况包括部分严格匹配。

    如果有几个具有相同本地socket的悬而未决的被动OPENs(记录在TCBs),如果存在一个指定了外部socket的TCB,则在选择一个未指定外部socket的TCB之前,外部主动OPEN将被匹配到该TCB。

    建立连接的工程利用了同步控制标志(SYN),包含了三条消息的交换。这个交换被称为三步握手[3]。

    通过包含一个SYN的到达分片和由用户OPEN命令创建的一个等待TCB条目的集合点,连接被发起。当一个连接被发起时,判定本地和外部socket的匹配。当系列号(sequence numbers)在两个方向被同步的时候,连接创建。

    连接的清除也包括分片的交换,此时,携带的是FIN控制标志。

    2.8 数据通信(Data Communication)

    在一个连接上的数据流可以视为字节流。发送用户在每个SEND调用中指示数据是否要通过PUSH标志的设置被立即推给接收用户。

    发送TCP允许集结从发送用户过来的数据,以他们便利的方式以分片发送那些数据,直到push函数被标记,然后它发送所有的未发送的数据。当接收TCP看到PUSH标志,它必须立即将数据传递给接收进程而不是等待发送TCP的其它数据。

    Push功能和分片边界之间没有必然联系。在任何特定分片的数据可以是一个单独的SEND调用的结果,全部或者部分,或者是多个SEND调用。

    PUSH功能和PUSH标志的目的是把数据从发送用户推给接收用户。它不提供一个记录服务(record service)。

    Push功能和TCP/用户接口的数据的缓冲的使用有个结合点。每次,一个PUSH标志同放到接收用户缓冲的数据联系在一起,即使缓冲没有满也会返回给用户处理。如果数据到达,且在发现PUSH标志前充满了整个缓冲,数据以缓冲大小为单元传给用户。

    TCP还定义了一种同数据接收者通信的方法,就是在某些点上,有紧急数据,接收者正在读取(TCP also provides a means to communicate to the receiver of data that at some point further along in the data stream than the receiver is currently reading there is urgent data.)。TCP并不尝试定义用户在收到悬而未决的紧急数据的通知时确定的行为,但是通用的观念是接收进程将很快采取行动处理紧急数据。

    2.9 优先级和安全性

    TCP使用internet协议的服务类型头部和安全选项来在每个连接上提供优先级和安全性。不是所有的TCP模块必须在多级别安全环境下起作用。有些可能仅限于未分类使用,其它可能仅在一个安全级别和隔离上操作。从而,有些TCP实现和对于用户的服务可以限制在多级别安全场合的一个子集上。

    运行在多级别安全环境的TCP模块必须正确标记外出的分片的安全性,分隔和优先级。这样的TCP模块必须同时提供他们的用户或者高层协议入Telnet或者THP一个接口以允许他们指定要求的安全级别,分隔和连接优先级。

    2.10 坚固性原则(Robustness Principle)

    TCP实现遵循了一个通用的坚固性原则:对自己所做的保守,对从其它地方接受的自由。

    3. 功能性规范(FUNCTIONAL SPECIFICATION)

    3.1 头部格式

    TCP分片作为internet数据报发送。Internet协议头部携带了一些信息头部,包括源主机地址和目的主机地址[2]。TCP头部也仿效Internet头部,提供TCP协议的一些特定信息。这种区分考虑了除了TCP外的主机级别协议的存在。

    TCP头部格式

        0               1               2               3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |        Source Port             |        Destination Port      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                        Sequence Number                        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                    Acknowledgment Number                      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |  Data |           |U|A|P|R|S|F|                               |
       | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
       |       |           |G|K|H|T|N|N|                               |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |           Checksum            |         Urgent Pointer        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                    Options                    |    Padding    |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                             data                              |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
                                   Figure 3.
    
    • 源端口(Source Port):16 bits源端口号码

    • 目的端口(Destination Port):16 bits目的端口号码

    • 系列号(Sequence Number):32 bits
      本分片的第一个数据八位字节的系列号码(除当SYN出现时外)。如果出现SYN标志,系列号是初使系列号(initial sequece:ISN),且第一个数据八位字节是ISN+1.

    • 确认号码(Acknowledgment Number):32 bits
      如果设定了ACK控制位,则这个头部包含了分片接收者期待接收的下一个系列号码。一旦连接建立,这个头部就总是发送。

    • 数据偏移(Data Offset): 4 bits,TCP首部长度TCP头部中32bit双字(words)的数目。指示了数据从哪里开始。TCP头部(即使是包含选项的头部)是32bits长度的整数号码。

    • 保留(Reserved):6 bits
      保留以后使用。必须是0值。

    • 控制位(Control Bits):6 bits(从左到右):

      • URG:紧急指针字段有效(Urgent Pointer field significant)

      • ACK:确认头部字段有效(Acknowledgment field significant)

      • PSH:强制函数(Push Function)

      • RST:重置连接(Reset the connection)

      • SYN:同步系列号码(Synchronize sequence numbers)

      • FIN:再没有来自发送者的数据(No more data from sender)

    • 窗口(Window):16 bits
      接收端正准备接收的数据八位字节的数目,开始于确认字段(acknowledgment)指示的位置。

    • 校验和(Checksum):16 bits
      在头部和文本中的所有16 bit字的补偿总和。如果分片包含一个0值的头部和文本八位字节要检验,为校验用最后一个八位字节右边填充0值以构成一个16 bit的字。填充不作为分片的一部分传送。当计算校验和的时候,校验和字段自己被0代替。
      校验和也包括TCP头部之前的伪头部。这些伪头部包括源地址,目的地址,协议和TCP长度。这可以避免报文被错误地路由。这些信息在Internet协议中携带,在TCP/Network接口的参数中传递,或者是TCP调用IP的返回值。

                         +--------+--------+--------+--------+
                         |           Source Address          |
                         +--------+--------+--------+--------+
                         |         Destination Address       |
                         +--------+--------+--------+--------+
                         |  zero  |  PTCL  |    TCP Length   |
                         +--------+--------+--------+--------+
    

    TCP长度是TCP头部加上数据长度的八位字节数(这不是一个明确传输的数量,但是被计算),且它不计入12个八位字节的伪头部。

    • 紧急指针(Urgent Pointer):16 bits

    该字段传达了紧急指针的当前值,是该分片里面从系列号码开始的正偏移。紧急指针指向根据紧急数据后面的八位字节的系列号码。该头部只有当URG控制位设置时才有效。

    • 选项(Options):可变

    选项位于TCP头部后面,长度上是8 bits的整数倍。所有选项包含在校验和中。选项可以开始于任何八位字节巴结。选项的格式有两种情况:

    1:单独的选项类型(option-kind)的八位字节

    2:选项类型的八位字节,选项长度的八位字节,以及真正的选项数据字节。

    选项长度(option-length)计入了选项类型(option-kind),选项长度(option-length)以及选项数据(option-data)。

    注意选项列表可能比数据偏移字段暗示的短。超过End-of-Option选项的头部的内容必须进行填充。

    TCP必须实现所有的选项

    当前定义的选项包括:

    类型 长度 意义

    0 ----- 选项列表的结束

    1 ----- 无操作

    2 4 最大分片大小

    特定选项定义:

    选项列表的结束(End of option list)

            +--------+
            |00000000|
            +--------+
             Kind=0
    

    选项代码指示了选项列表的结束。这可能同根据数据偏移字段得到的TCP头部不一致。这个选项用在所有选项的结束,而不是每个选项的结束。且仅在选项的结束同TCP头部的结束不一致的情况下需要使用

    无操作(No Operation):

            +--------+
            |00000001|
            +--------+
             Kind=1
    

    该选项代码可以在选项之间使用,比如,为了在一个字边界上对齐接下来的选项的开始。不保证发送者使用这个选项,所以接收者必须准备处理没有在一个字边界对齐的选项。

    最大分片大小(Maximum Segment Size)

            +--------+--------+---------+--------+
            |00000010|00000100|   max seg size   |
            +--------+--------+---------+--------+
             Kind=2   Length=4
    

    最大分片大小选项数据(Maximum Segment Size Option Data):16 bits

    如果这个选项存在,则其指示了发送这个分片的TCP的最大接收分片。这个字段必须仅在初始化连接请求阶段发送(比如,在SYN 控制位设置的分片)。如果不使用这个选项,则允许任意大小的分片。

    填充(Padding):可变

    3.2 术语(Terminology)

    在深入探讨TCP的操作之前,我们需要详细介绍一些术语。TCP连接的维持要求记住一些变量。我们设想这些变量存储在一个连接记录中,称这个连接记录为传输控制块(Transmission Control Block :TCB)。在存储在TCB的变量中,有本地和远端socket编号(local and remote socket numbers),安全(security)和连接优先级(precedence of connection),用户发送和接收缓冲的指针,重传队列和当前分片的指针。另外还有一些同发送和接收系列号相关的变量存储在TCB中。

    发送系列号变量

    • SND.UNA - 发送 未经确认(send unacknowledged)

    • SND.NXT - 发送下一个(send next)

    • SND.WND - 发送窗口(send window)

    • SND.UP - 发送紧急指针(send urgent pointer)

    • SND.WL1 - 供最后窗口更新的分片系列号(segment sequence number used for last window update)

    • SND.WL2 - 供最后窗口更新的分片确认号码(segment acknowledgment number used for last Window update)

    • ISS - 初始发送系列号(initial send sequence number)

    接收系列号变量

    • RCV.NXT - 接收下一个(receive next)

    • RCV.WND - 接收窗口(receive window)

    • RCV.UP - 接收紧急指针(receive urgent pointer)

    • IRS - 初始接收系列号(initial receive sequence number)

    下面的图可以帮助你将这些发送系列号空间的变量联系起来。

    发送系列号空间

                       1         2          3          4     
    
                  ----------|----------|----------|----------
    
                         SND.UNA    SND.NXT    SND.UNA       
                                             + SND.WND   
    

    1 - 已经确认过的老的系列号

    2 - 未确认过的数据的系列号

    3 - 新数据传送的系列号(sequence numbers allowed for new data transmission)

    4 - 当前不允许传送的将来的系列号

    发送窗口是图中3所标注的空间

    接收系列号空间:

                           1          2          3     
    
                       ----------|----------|----------
    
                              RCV.NXT    RCV.NXT       
                                       + RCV.WND       
    

    1 - 已经确认的老系列号

    2 - 新接收的系列号(sequence numbers allowed for new reception)

    3 - 现在不允许的将来的系列号(future sequence numbers which are not yet allowed)

    接收窗口是图中2所标注的空间

    还有一些变量经常使用,它们是从当前分片的字段中取值的。

    当前分片变量

    • SEG.SEQ - 分片系列号(segment sequence number)

    • SEG.ACK - 分片确认号码(segment acknowledge number)

    • SEG.LEN - 分片长度(segment length)

    • SEG.WND - 分片窗口(segment window)

    • SEG.UP - 分片紧急指针(segment urgent pointer)

    • SEG.PRC -分片优先级值(segment precedence value)

    连接过程在生存时间内经过一系列的状态。LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT,和最后的虚构状态CLOSED。CLOSED是虚构的因为处于CLOSED状态的时候已经没有TCB,也就是说没有连接。下面是这些状态的简单意义:

    • LISTEN - 等待从任何远端TCP和端口的连接请求

    • SYN-SENT -发送完一个连接请求后等待一个匹配的连接请求

    • SYN-RECEIVED - 发送连接请求并且接收到匹配的连接请求以后等待连接请求确认

    • ESTABLISHED - 表示一个打开的连接,接收到的数据可以被投递给用户。连接的数据传
      输阶段的正常状态。

    • FIN-WAIT-1 - 等待远端TCP的连接终止请求,或者等待之前发送的连接终止请求的确认

    • FIN-WAIT-2 - 等待远端TCP的连接终止请求

    • CLOSE-WAIT - 等待本地用户的连接终止请求

    • CLOSING - 等待远端TCP的连接终止请求确认

    • LAST-ACK - 等待先前发送给远端TCP的连接终止请求的确认(包括它字节的连接终止请求的确认)

    • TIME-WAIT - 等待足够的时间过去以确保远端TCP接收到它的连接终止请求的确认

    • CLOSED - 不在连接状态

    TCP连接响应不同的事件从一个状态过渡到另一个状态。事件是用户调用,OPEN,SEND,RECEIVE,CLOSE,ABORT,和STATUS;到来的分片,特别是那些包含SYN,ACK,RST和FIN标志的分片;超时。

    下图仅展示了状态变化,及导致变化的事件和引起的行为,但是未连接的错误情况和行为不包含在内。在后面的章节,将提供更详细的描述。

    注:该图仅是摘要,不能当作所有的规范。

                                 +---------+ ---------/      active OPEN  
                                  |  CLOSED |            /    -----------  
                                  +---------+<---------/   /   create TCB  
                                    |     ^              /   /  snd SYN    
                       passive OPEN |     |   CLOSE        /   /           
                       ------------ |     | ----------       /   /         
                        create TCB  |     | delete TCB         /   /       
                                    V     |                      /   /     
                                  +---------+            CLOSE    |    /   
                                  |  LISTEN |          ---------- |     |  
                                  +---------+          delete TCB |     |  
                       rcv SYN      |     |     SEND              |     |  
                      -----------   |     |    -------            |     V  
     +---------+      snd SYN,ACK  /       /   snd SYN          +---------+
     |         |<-----------------           ------------------>|         |
     |   SYN   |                    rcv SYN                     |   SYN   |
     |   RCVD  |<-----------------------------------------------|   SENT  |
     |         |                    snd ACK                     |         |
     |         |------------------           -------------------|         |
     +---------+   rcv ACK of SYN  /       /  rcv SYN,ACK       +---------+
       |           --------------   |     |   -----------                  
       |                  x         |     |     snd ACK                    
       |                            V     V                                
       |  CLOSE                   +---------+                              
       | -------                  |  ESTAB  |                              
       | snd FIN                  +---------+                              
       |                   CLOSE    |     |    rcv FIN                     
       V                  -------   |     |    -------                     
     +---------+          snd FIN  /       /   snd ACK          +---------+
     |  FIN    |<-----------------           ------------------>|  CLOSE  |
     | WAIT-1  |------------------                              |   WAIT  |
     +---------+          rcv FIN  /                            +---------+
       | rcv ACK of FIN   -------   |                            CLOSE  |  
       | --------------   snd ACK   |                           ------- |  
       V        x                   V                           snd FIN V  
     +---------+                  +---------+                   +---------+
     |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
     +---------+                  +---------+                   +---------+
       |                rcv ACK of FIN |                 rcv ACK of FIN |  
       |  rcv FIN       -------------- |    Timeout=2MSL -------------- |  
       |  -------              x       V    ------------        x       V  
        / snd ACK                 +---------+delete TCB         +---------+
         ------------------------>|TIME WAIT|------------------>| CLOSED  |
                                  +---------+                   +---------+
    
                        TCP Connection State Diagram
                                   Figure 6.
    
    

    3.3 系列号(Sequence Numbers)

    设计上的一个基本观点是在一个TCP连接上发送的每个八位字节的数据都有一个系列号码。因为每个八位字节是编过序号的,所以每个都可以被确认。所使用的确认机制是累积的,所以序号X的确认指示的是所有X之前但不包括X的数据已经收到了。这种机制在重传中允许用来作为直接的复制检测。在一个分片内的八位字节的编号机制是紧跟头部之后的第一个数据八位字节是最小编号的,接下来进行顺序编号。

    需要记住的是实际的系列号码空间是有限的,但是是很大的。该空间从0到2**32-1。因为空间是有限的,所有的处理系列号的算法必须执行模2**32。当系列号从2**32-1循环到0的时候,算法保留系列号的关系。计算模算法有些微妙,必须在比较这些值的编程的时候谨慎考虑。符号“=<“表示”小于或者等于“(模2**32)。

    TCP必须执行的典型的一种系列号比较包括:

    (a)确定一个确认指向的是有些系列号码已经发送但是未经确认

    (b)确定一个分片的所有系列号码被确认(比如,用以将分片从重传队列中移除)

    (c) 确定到来的分片包含了期望的分片(比如,分片同接收窗口交迭)

    作为发送数据的响应,TCP会收到确认(acknowledgements)。在处理确认的时候需要做如下比较:

    • SND.UNA = 最老的未确认的系列号

    • SND.NXT = 下一个要发送的系列号

    • SEG.ACK = 接收TCP发送过来的确认(接收TCP期望的下一个系列号)

    • SEG.SEQ = 分片的第一个系列号

    • SEG.LEN = 分片中数据占有的八位字节号码(包括SYN和FIN)

    • SEG.SEQ+SEG.LEN-1 = 分片的最后一个系列号

    一个新的确认(称为“可接受的ack”),遵循下面的不等式:

    • SND.UNA<SEG.ACK=<SND.NXT

    如果重传队列中的一个分片的系列号和长度的和小于或者等于到来分片中的确认的值,则该分片被完全确认。

    收到数据的时候,需要进行下面的比较:

    • RCV.NXT = 在到来分片中期待的下一个系列号,且是接收窗口的左或下边缘

    • RCV.NXT+RCV.WND-1 = 到来分片中期待的最后一个分片,且是接收窗口的右或上边缘

    • SEG.SEQ = 到来分片占据的第一个系列号

    • SEG.SEQ+SEG.LEN-1 = 到来分片占据的最后一个系列号

    一个分片被认为用来占据合法系列空间的一部分,如果:

    • RCV.NXT =<SEG.SEQ<RCV.NXT+RCV.WND

    或者

    • RCV.NXT=<SEG.SEQ+SEG.LEN-1<RCV.NXT+RCV.WND

    第一部测试是检查分片的开始部分是否在窗口内,第二部测试是检查分片的结尾是否在窗口内,如果分片通过了这两步测试,则它包含了窗口内的数据。

    实际情况要比这复杂。由于零窗口和0长度分片的存在,到来分片的接收有4种情况:

        Segment Receive  Test
    
        Length  Window
    
        ------- -------  -------------------------------------------
           0       0     SEG.SEQ = RCV.NXT
    
           0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
    
          >0       0     not acceptable
    
          >0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
                         or 
                         RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND
    

    注意当接收窗口为0的时候,除了ACK分片外的其它分片都不被接受。因此,TCP在传输数据和接收ACKs时维护一个0接收窗口是可能的。但是,即使当接收窗口是0,TCP必须处理所有到来分片的RST和URG字段。

    初始化系列号选择

    我们也利用编号机制来保护某些控制信息。这是通过在系列号空间中明显地包含一些控制标志让这些分片可以无冲突地被传输和确认来实现的(比如,一个且只有一个控制的拷贝被遵循)。控制信息在物理上不在分片数据空间携带。因此,我们必须采用暗中分配系列号给控制信息的规则。SYN和FIN是要求这个保护的控制信息,且这些控制信息仅在连接打开和关闭的时候使用。为了系列号的用途,SYN被认为出现在它出现的分片的第一个真实数据字节之前,而FIN被认为出现在它出现的分片的最后一个字节之后。分片长度包含数据和控制信息占据的系列号空间。当SYN出现的时候,SEG.SEQ是SYN的系列号。

    协议不限制一个特定的连接被一次又一次的使用。连接被定义成一对socket。一个连接的新实例将当成连接的化身。这就引起了问题“TCP如何从之前连接化身的分片区分出复制分片”。如果连接关闭后被快速地打开,或者当连接由于内存丢失而断开然后重建,这个问题就变得很明显。

    为了避免混淆,必须防止连接的一个化身的分片当同样的系列号存在于更早的化身的网络上的情况下被使用。我们要确保这点,即使TCP崩溃,丢失了他使用过的系列号码的所有信息。当新的连接建立的时候,初始化系列码(intitial sequence number :ISN)产生器被调用,该产生器选择了新的32位的ISN。该系列码产生器一定要有(可能是虚构的)个32位时钟,时钟的低位是每4微妙增长。也就是说,ISN大概每4.55个小时循环一次。因此我们假定分片不会在网络上停留超过最大分片生命周期(Maximum Segment Lifetime:MSL),且MSL小于4.55个小时,这样,我们就有理由假定ISN是独一无二的。

    每个连接有一个发送的系列号和一个接收系列号。初始的发送系列号(ISS:initial send sequence number)由发送数据的TCP选择,初始的接收系列号(IRS:initial receive sequence number)在连接建立阶段得到。

    一个连接要建立或者初始化,两个TCPs必须在双方的初始化系列号上同步。这是通过携带称为SYN(同步用)的控制位和初始化系列号的连接建立分片的交换完成了。作为一个速记标识,携带SYN位的分片也称为”SYNs”。然后,解决方案需要一个适宜的机制选择初始系列号以及细微地参与交换ISN的握手过程。

    同步需要每一端发送它自己的初始化系列号,且从另一端通过ACK接收确认。每一端也必须接收另一端的初始化系列号并且发送一个确认ACK

    1) A-->B   SYN 我的系列号是X
    
    2) A<--B   ACK 你的系列号是X
    
    3) A<--B   SYN 我的系列号是Y
    
    4) A-->B   ACK 你的系列号是Y
    

    因为步骤2和3可以结合在一条消息里面,所以这个过程称为3次握手。

    因为系列号不依赖于一个网络中的全局时钟,所以三次握手是需要的,TCPs可以有不同的机制来挑选ISN。第一个SYN的接收者无法知道分片是否是个旧的延迟的分片,除非它记得上次用于连接的系列号(这往往是不可能的),因此它必须要发送者验证这个SYN。三次握手以及时钟驱动机制的优点在[3]中会进行讨论。

    知道什么时候保持静止

    为保证TCP不会产生一个携带了保留在网络中的老的分片的系列号的分片,TCP如果是从一个崩溃中启动或者恢复必须在一个最大分片生命周期内保持静止,在这个周期内,在使用的系列号内存被丢弃,这个周期之后才可以分配任何系列号。本规范设定MSL为2分钟。这是个工程上的选择,如果经验指示它需要做出修改他就可以修改。注意如果TCP被在某种意义上重新初始化,却仍然保留在使用的系列号内存,那么它不需要等待,它仅需保证使用大于那些刚被使用过的系列号码。

    TCP静止时间概念(The TCP Quiet Time Concept)

    规范规定那些崩溃(crash)且未能保持任何在每个活动(而不是关闭的)连接传输的系列号的信息的主机必须延迟发送任何TCP分片,这个延迟至少是在主机作为一部分的internet系统上经过协议的最大分片生命周期(MSL)。在下面的段落中,将给出规范的解释。TCP实现者可以违反“静止时间”限制,但是有可能导致某些老数据被internet系统上的某些接收者当作新数据接收或者新数据被接收者当作老的复制分片拒绝的风险。

    每次形成一个分片然后进入在源主机的网络输出队列,TCPs消耗了系列号空间。TCP协议的复制检测和系列号算法依赖于分片数据同系列号空间的唯一绑定关系,假定系列号在绑定于该系列号的分片被投递和确认且所有复制拷贝已经在internet上消失之前不会循环了所有了2**32的值。如果没有这个假设,两个不同的分片可能被分配了一个同样的或者重叠的系列号,导致接收者对哪个数据是新的哪个数据是老的产生混淆。每个分片会被分配一串连贯的系列号,跟他们分片中的八位字节数目一样。

    正常情况下,TCP会记录下一个要发送的系列号和最老的等待确认的分片的系列号,这样子可以避免错误地使用一个它的第一次使用还未被确认的系列号。仅仅这样并不能保证老的重复数据已经在网络上消失,因此系列号空间需要足够大以减少闲置重复分片到来导致的麻烦的可能性。在2兆/秒情况下,需要4.5个小时才能用光2**32个八位字节的系列号空间。由于网络上的最大分片生命周期不太可能超过几十秒,因此这被认为对可以预见的网络的足够保护,即使数据传输率上升到10兆/秒。在100兆/秒的条件下,循环时间是5.4分钟,这看起来有点短,但仍然在合理范围内。

    但是,如果一个源TCP不记得它前次在一个给定连接上使用的系列号的话,TCP中的基本的重复分片检测和系列号算法可能被击败。比如,如果TCP以系列号0开始所有的连接,那么在崩溃或者重启的时候,一个TCP可能重新形成了一个较早的连接(可能是在半打开连接解决方案(half-open connection resolution)之后),且发送了系列号等于或者覆盖在同一个连接的较早化身上的系列号码的包。在不知道使用于一个特定连接的系列号信息的情况下,TCP规范建议源主机在发送分片之前延迟MSL的时间,以让那么较早连接化身发出的分片有时间从系统上消失。

    即使主机能够记住时间并且用它来确定初始系列号值,也不能确保没有这个问题(比如,即使时间用来确定每个新的连接化身的初始系列号值)。

    比如,假设一个连接以系列号S打开。假定这个连接没有怎么使用,最后初始化系列号函数(ISN(t))采用了一个值S1,这个S1是TCP在一个特定的连接上发送的最后一个分片。现在假定在这个时候,主机崩溃,恢复,然后建立了该连接的一个新化身。初始系列号是S1=ISN(t),刚好是连接的老化身上使用的最后一个系列号!如果恢复足够快速,在产生系列号的网络上任何S1相邻的重复分片都可能到来且被连接的新化身接收者视为新的数据包。

    处理这个问题的一种方法是在从崩溃中恢复后特意地延迟发送分片一个MSL的时间,这就是“静止时间”规范。那些想避免等待且乐于接受老数据包和新数据包可能在目的主机产生混淆的风险的主机可以选择不等待“静止时间”。实现者可以提供TCP用户选择在以连接为基础的连接是否在崩溃或等待一段时间的能力,或者可以非正式的对所有连接实现“静止时间”。显然,即使一个用户选择“等待”,主机也并不一定要等待MSL这么长的时间。

    总结:每个发送的分片在系列号空间中占据了一个或者多个系列号,被一个分片占用的号码在过去MSL时间之前都是“忙”或者“在用”,崩溃的时候,一块系列号空间被最后发送的分片的八位字节占用,如果新的化身太快启动且使用了同一个连接的前一个化身的最后分片的系列号空间范围内的系列号,这些潜在的系列号空间覆盖可能导致接收者产生混淆。

    3.4 建立一个连接

    “三次握手”“是用来建立一个连接的程序。这个程序一般是由一个TCP发起,另一个TCP响应。如果两个TCP同时发起该程序,也可以工作。当同时尝试发起产生的时候,一个TCP在发送了一个“SYN”分片以后接收到一个没有携带确认的“SYN”分片。当然,一个老的重复的“SYN”分片的到达也可能使这种情况发生。正确的“reset”分片的使用可以消除这种情况下的歧义。

    下面是一些连接发起的例子。虽然这些例子没有展示使用携带数据的分片的连接同步,这是完全合法的,只要接收TCP没有在清楚的知道数据是合法之前将数据投递给用户(比如,数据必须在接收者的缓冲上进行缓存直到连接到达ESTABLISHED状态)。三次握手降低了错误连接的可能性。这是实现者在内存和提供检查信息的消息之间的一个平衡。

    最简单的三次握手入下图所示。图中,每条线为引用方便被编号。右箭头(–>)表示TCP分片从TCP A到TCP B的离开,或者从A到B的到达。左箭头(<–)相反。省略号(…)表示仍然停留在网络上(延迟)的分片。“XXX”表示丢失或者被拒绝的分片。注释出现在圆括号中。TCP状态代表了分片(内容在每条线中间显示)离开或者到达后的状态。分片内容以缩略形式表示,包括系列号,控制位和ACK字段。其它字段如窗口,地址,长度和文本被忽略。

          TCP A                                                TCP B
        
      1.  CLOSED                                               LISTEN
       
      2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               --> SYN-RECEIVED
     
      3.  ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED
      
      4.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK>       --> ESTABLISHED
      
      5.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED
    
    
              Basic 3-Way Handshake for Connection Synchronization
    
                                    Figure 7.
    

    在上图的线2中,TCP A通过发送一个指示它将使用100开始的系列号码的SYN分片开始。在线3中,TCP B发送了一个SYN和它从TCP收到的分片的确认。注意,确认字段指示了TCP B现在期望侦听系列号101,确认占用系列号100的SYN。

    在线4中,TCP A以包含TCP‘s的SYN包的ACK的空分片响应,而在线5,TCP A发送了一些数据。注意在线5中的系列号同线4中的系列号一致,因为ACK并不占用系列号空间(如果占用,我们将结束正在确认的ACK)。

    同时发起相对复杂一些,在现图中展示。每个TCP循环从CLOSED到SYN-SENT到SYN-RECEIVED到ESTABLISHED。

         TCP A                                            TCP B
         
      1.  CLOSED                                           CLOSED
      
      2.  SYN-SENT     --> <SEQ=100><CTL=SYN>              ...
      
      3.  SYN-RECEIVED <-- <SEQ=300><CTL=SYN>              <-- SYN-SENT
      
      4.               ... <SEQ=100><CTL=SYN>              --> SYN-RECEIVED
      
      5.  SYN-RECEIVED --> <SEQ=100><ACK=301><CTL=SYN,ACK> ...
      
      6.  ESTABLISHED  <-- <SEQ=300><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED
      
      7.               ... <SEQ=101><ACK=301><CTL=ACK>     --> ESTABLISHED
    
    
                    Simultaneous Connection Synchronization
    
                                   Figure 8.
    

    三次握手的基本原则是防止老的重复连接发起导致的混乱。为了处理这个,一条特殊的控制信息,reset,被提了出来。如果正在接收的TCP正处于一个非同步状态(比如,SYN-SENT,SYN-RECEIVED),它在接收到一个可以接受的reset后返回继续监听。如果TCP处于同步状态(ESTABLISHED,FIN-WAIT-1,FIN-WAIT2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT),它放弃了连接且通知用户。我们后面会讨论这种处于“半打开(half-open)”连接下的情况。

         TCP A                                                TCP B
    
      1.  CLOSED                                               LISTEN
    
      2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               ...
      
      3.  (duplicate) ... <SEQ=90><CTL=SYN>               --> SYN-RECEIVED
     
      4.  SYN-SENT    <-- <SEQ=300><ACK=91><CTL=SYN,ACK>  <-- SYN-RECEIVED
      
      5.  SYN-SENT    --> <SEQ=91><CTL=RST>               --> LISTEN
      
      6.              ... <SEQ=100><CTL=SYN>               --> SYN-RECEIVED
    
      7.  SYN-SENT    <-- <SEQ=400><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED
    
      8.  ESTABLISHED --> <SEQ=101><ACK=401><CTL=ACK>      --> ESTABLISHED
       
                        Recovery from Old Duplicate SYN
                        
                                   Figure 9.
    

    作为一个从老的重复分片中恢复的简单例子,看下图9。在线3,一个老的重复SYN到达TCP B。TCP B不能断定这是一个老的重复分片,所以它正常响应(线4)。TCP A检测到ACK字段不正确就返回了一个RST(reset),该RST的SEQ字段使分片可信。TCP B在收到RST后,返回到LISTEN状态。当原来的SYN在线6最后到达,同步过程正常开始。如果线6的SYN在RST前到达,则将发生更复杂的交换(两个方向都发送RST)。

    半打开(Half-Open)连接和其它不规则情况

    一条已经建立的连接当TCP的一方已经在自己一端关闭或者中断连接且不知道另一端的情况,或者当连接双方由于崩溃导致失忆变成不同步,就称为“半打开”的连接。如果尝试在该连接的任何一个方向上发送数据,这个连接就会自动变成reset。但是,半打开连接被期望是不同寻常的,恢复过程仅轻微涉及。

    如果在A端连接不再存在,则用户在B端在该连接发送任何数据会导致B端的TCP接收到一个reset控制信息。该信息指示B端TCP连接发生了错误,且它被期望终止连接。

    假设两个用户进程A和B相互通信,这时一个崩溃发生导致了A端TCP失忆。依赖于操作系统对A端TCP的支持,有可能存在一些错误恢复机制。当TCP重新启动,A可能从最开始点启动或者从一个恢复点启动。因此,A可能会尝试重新打开连接或者在他认为打开的连接上发送数据。在后一种情况中,它会从本地(A的)TCP收到“连接未打开”的错误信息。在尝试建立连接时,A端TCP会发送一个包含SYN的分片。这个例子在下图中展示。TCP A崩溃后,用户尝试重新打开连接。同时TCP B认为连接是打开的。

          TCP A                                           TCP B
      
      1.  (CRASH)                               (send 300,receive 100)
     
      2.  CLOSED                                           ESTABLISHED
      
      3.  SYN-SENT --> <SEQ=400><CTL=SYN>              --> (??)
      
      4.  (!!)     <-- <SEQ=300><ACK=100><CTL=ACK>     <-- ESTABLISHED
      
      5.  SYN-SENT --> <SEQ=100><CTL=RST>              --> (Abort!!)
      
      6.  SYN-SENT                                         CLOSED
      
      7.  SYN-SENT --> <SEQ=400><CTL=SYN>              -->
      
                         Half-Open Connection Discovery
      
                                   Figure 10.
    

    当SYN在线3到达的时候,TCP B,正处于同步状态,且到来的分片在窗口之外,以一个确认响应,该确认指示了他期望听倒的下一个系列号(ACK 100)。TCP A发现这个分片没有对他发送的分片作任何确认,处于未同步状态,由于它检测到了一个半打开的连接就发送了一个reset(RST)。TCP B在线5中断了连接。TCP A继续尝试建立连接,问题就变成基本的三次握手。

    另外一种有趣的情况是当TCP A崩溃时,TCP尝试在它认为是一个同步的连接上发送数据。这种情况在下图中展示。在这种情况中,从B到达A(线2)的数据由于没有连接存在不被接收,所以TCP A发送了一个RST。RST是可接受的,所以TCP B处理了该RST,然后终止了连接。

           TCP A                                              TCP B
      
      1.  (CRASH)                                   (send 300,receive 100)
      
      2.  (??)    <-- <SEQ=300><ACK=100><DATA=10><CTL=ACK> <-- ESTABLISHED
      
      3.          --> <SEQ=100><CTL=RST>                   --> (ABORT!!)
       
      
               Active Side Causes Half-Open Connection Discovery
      
                                   Figure 11.
    

    在下图中,我们发现TCP A和B都处于被动等待SYN连接。一个老的重复分片到达TCP B(线2),触发了B的动作。一个SYN-ACK被返回(线3),并且导致了TCP A产生了一个RST(线3的ACK是不能被接受的),TCP B接受了reset,且返回到被动监听状态。

          TCP A                                         TCP B
      
      1.  LISTEN                                        LISTEN
      
      2.       ... <SEQ=Z><CTL=SYN>                -->  SYN-RECEIVED
      
      3.  (??) <-- <SEQ=X><ACK=Z+1><CTL=SYN,ACK>   <--  SYN-RECEIVED
      
      4.       --> <SEQ=Z+1><CTL=RST>              -->  (return to LISTEN!)
     
      5.  LISTEN                                        LISTEN
      
           Old Duplicate SYN Initiates a Reset on two Passive Sockets
     
                                   Figure 12.
    

    各种各样的情况都有可能,所有的这些情况遵循RST产生和处理的规则。

    Reset产生

    作为一条基本规则,reset(RST)在一个分片到达且明显不是当前连接的分片的任何时候必须被发送。如果还不清楚,reset不能被发送。

    有三组状态:

    1. 如果连接不存在(关闭),那么reset被发送来响应任何到来分片(另外一个reset除外)。特别的,属于一个不存在的连接的SYN被这个方式拒绝。

      如果到来分片有ACK字段,reset从该分片的ACK字段提取系列号,否则reset系列号取0,ACK字段设置成系列号和到来分片的分片长度的和。连接保留在关闭(CLOSED)状态。

    2. 如果连接处于非同步状态(LISTEN,SYN-SENT,SYN_RECEIVED),到来分片对没有发送的东西进行确认(分片携带了不被接受的ACK),或者一个到来分片同连接要求的安全级别或者分隔没有严格匹配,reset被发送。

      如果我们的SYN还没有被确认且到来分片的优先级别高于要求的优先级别,则双方都提高本地优先级别(如果用户和系统允许的话)或者发送一个reset;或者如果到来分片的优先级别低于要求的优先级别,则继续,就像优先级别严格匹配一样。(如果远端TCP不能提高优先级别来匹配我们的优先级别,在下一个它发送的分片中会被检测到,且连接将会被终止)。如果我们的SYN已经被确认(可能是在这个到来的分片中),到来分片的优先级别必须同本地优先级别严格匹配,如果没有匹配,一个reset必须被发送。
      如果到来分片有一个ACK字段,reset从分片的ACK字段中提取系列号。否则,reset的系列号为0,ACK字段设置成到来分片系列号和分片长度的和。连接维持同一状态。

    3. 如果连接处于同步状态(ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT),任何不被接受的分片(在系列号窗口之外或者不被接受的确认号码)必须仅发出一个包含当前发送队列号码和指示下一个期望被收到的系列号的确认的确认分片,且连接维持在相同的状态。

      如果到来分片同连接要求的安全级别、分隔不一样,reset被发送,连接进入到CLOSED状态。Reset从到来分片的ACK字段提取系列号。

    Reset处理

    在除了SYN-SENT之外的所有状态中,所有的reset(RST)分片通过检查他们的SEQ字段被验证有效。如果一个reset的系列号在窗口内,则它是有效的。在SYN-SENT状态中(一个reset作为一个初始SYN的响应被收到),如果ACK字段确认了SYN,RST是可以接受的。

    RST的接收者首先验证它,然后改变状态,如果接收者处于LISTEN状态,它可以忽略RST。如果接收者处于SYN-RECEIVED状态,且之前处于LISTEN状态,则接收者返回到LISTEN状态,否则接收者终止连接然后进入CLOSED状态。如果接收者处于其它的状态,它终止连接,告知用户,进入CLOSED状态。

    3.5 关闭一个连接

    CLOSE是表示“我已经没有数据要发送”的操作。关闭一个全双工的连接的概念属于含糊的解释,当然,既然它不清楚如何对待连接的接收端。我们选择以一个单工方式处理CLOSE。关闭连接的用户可以继续接收直到他被告知另一端也关闭了。这样,一个程序可以在几个SENDs之后跟1个CLOSE,然后继续接收直到由于另一端关闭RECEIVE失败。我们假定TCP会通知用户另一端已经关闭,即使没有明显的RECEIVE,这样用户可以优雅地关闭他自己这端的连接。TCP将可靠地投递所有的在连接关闭前发送的缓冲,因此一个希望没有返回数据的用户只要等待监听连接被成功关闭一直到他的所有数据被目标TCP接收。用户必须保持读取他们关闭发送的连接直到TCP说明再没有数据。
    最基本的三种情况:

    1. 用户通过告知TCP关闭连接发起
    2. 远端TCP通过发送FIN控制信号发起
    3. 双方用户同时关闭
    • 第一种情况:本地用户发起关闭
      在这种情况中,FIN分片可以被构建并放在外发分片队列中。从用户发起的SENDs不再被TCP接受,TCP进入了FIN-WAIT-1状态。RECEIVE在这个状态是允许了。所有之前发送的分片包括FIN分片都会被重传直到被确认。当其它TCP确认了FIN分片,且发送了它自己的FIN分片,第一个TCP可以确认这个FIN。注意一个接受到一个FIN的TCP将确认但不发送它自己的FIN,直到用户也关闭了连接。

    • 第二种情况:TCP从网络上接受到一个FIN
      如果一个未被恳求的FIN从网络上到达,接收TCP可以确认它,然后告知用户连接正在关闭。用户以CLOSE响应,在这个过程中TCP在发送完任何剩下的数据后可以发送一个FIN给另一个TCP。然后TCP等待它自己的FIN被确认后关闭连接。如果ACK没有到来,则在用户超时后连接被终止,用户也会被告知。

    • 第三种情况:双方用户同时关闭
      连接两端FIN同时被用户关闭导致FIN分片被交换。当FIN之前的所有分片被处理和确认,每个TCP可以确认它收到的FIN。双方在接收到这些ACK后将删除连接。

          TCP A                                                TCP B
     
     1. ESTABLISHED                                         ESTABLISHED
     
     2. (Close)
          FIN-WAIT-1 --> <SEQ=100><ACK=300><CTL=FIN,ACK> --> CLOSE-WAIT
     
     3.  FIN-WAIT-2 <-- <SEQ=300><ACK=101><CTL=ACK>      <-- CLOSE-WAIT
     
     4.                                                       (Close)
        TIME-WAIT   <-- <SEQ=300><ACK=101><CTL=FIN,ACK> <-- LAST-ACK
     
     5. TIME-WAIT   --> <SEQ=101><ACK=301><CTL=ACK>      --> CLOSED
     
     6. (2 MSL)
          CLOSED                                                      
     
                             Normal Close Sequence
     
                                   Figure 13.
    
          TCP A                                                TCP B
     
     1. ESTABLISHED                                         ESTABLISHED
     
     2. (Close)                                              (Close)
           FIN-WAIT-1 --> <SEQ=100><ACK=300><CTL=FIN,ACK> ... FIN-WAIT-1
                      <-- <SEQ=300><ACK=100><CTL=FIN,ACK> <--
                      ... <SEQ=100><ACK=300><CTL=FIN,ACK> -->
     
     3.   CLOSING     --> <SEQ=101><ACK=301><CTL=ACK>      ... CLOSING
                      <-- <SEQ=301><ACK=101><CTL=ACK>      <--
                      ... <SEQ=101><ACK=301><CTL=ACK>      -->
     
     4. TIME-WAIT                                            TIME-WAIT
          (2 MSL)                                              (2 MSL)
          CLOSED                                               CLOSED
     
                          Simultaneous Close Sequence
     
                                   Figure 14.
    

    3.6 优先级和安全性

    意图是连接只允许在同两个端口要求的优先级和间隔值相等或者更高的情况下在端口间进行。
    在TCP中使用的优先级和安全参数就是那些在Internet协议(IP)中定义的优先级和安全参数。在这个TCP规范中,术语“安全性/间隔(compartment)”是用来指示用于IP的安全参数,包括安全性(security),间隔(compartment),用户组(user group)和处理限制(handling resetriction)
    一个有着不匹配的安全/间隔值或者较低优先级值的连接尝试必须通过发送一个reset被拒绝。由于优先级太低拒绝一个连接只发生在SYN的确认收到之后。
    注意,仅操作于优先级的缺省值之上的TCP模块仍会检查到来分片的优先级,且可能提高他们用于连接的优先级。
    安全参数可以被用于一个非安全环境(值指示了未分类的数据),然后在非安全环境中的主机必须准备接收安全参数,即使他们不需要发送这些参数。

    3.7 数据通信

    一旦连接建立,数据通过分片的交换进行通信。因为分片可能由于错误(校验和测试失败),或者网络拥挤而丢失,TCP使用了重传(经过一个超时)来确保每个分片的投递。由于网络和TCP的重传,可能会有重复分片到达。如系列号一节讨论的,TCP对分片的系列号和确认号码进行某些测试来查证可接受性。
    数据发送者在变量SND.NXT中记录了下一个要使用的系列号。接收者在变量RCV.NXT中使用了期待接收的下一个系列号。数据发送者在变量SND.UNA中记录了最老的未经确认的系列号。如果数据流即刻空闲,且所有发送的数据已经被确认,则三个变量相等。
    当发送者创建了一个分片并传输它的时候,发送者增加了SND.NXT。当接收者接受了一个分片,他增加了RCV.NXT并发送了一个确认。当数据发送者接收一个确认,它增加了SND.UNA。这三个变量的差值可以用来衡量通信的延迟。变量增加的数量是分片中数据的长度。注意,一旦处于ESTABLISHED状态,所有的分片必须携带当前确认信息。
    CLOSE用户调用执行了一个PUSH函数,通过这个函数,在一个到来分片中包含了FIN控制位。
    重传超时(Retransmission Timeout)
    由于组成internetwork系统的网络的多样性和TCP连接的大范围使用,重传超时必须被动态确定。确定重传超时的一个程序在下面给出。
    一个重传超时例子程序
    计算以一个特殊系列号发送一个数据八位字节和接收到覆盖那个系列号(发送分布不一定要匹配接收到的分片)的确认之间这段流逝时间。这样计算的流逝时间是往返旅行时间(RTT:Round Trip Time)。接着计算一个平滑往返旅行时间(SRTT:Smoothed Round Trip Time):
    SRIT = (ALPHA * SRTT)+((1-ALPHA) * RTT)
    基于这个,计算重传超时(RTO:retransmission timeout):
    RTO = min[UBOUND,max[LBOUND,(BETA * SRTT)]]
    这里UBOUND是超时上限(比如,1分钟),LBOUND是超时下限(比如,1秒),ALPHA是个平滑因子(比如,.8到.9),BETA是一个延迟因子(比如,1.3到2.0)
    紧急信息的通信(The Communication of Urgent Information)
    TCP紧急机制的目的是允许发送用户激发接收用户去接收一些紧急数据并允许接收TCP当所有的当前已知的紧急数据被用户接收的时候去指示接收用户。
    这个机制允许数据流中的某个点被指派为紧急信息的结束。不管什么时候当这个点在接收TCP的接收系列号(RCV.NXT)之前,TCP必须告知用户进入“紧急模式(urgent mode)”,当接收系列号追上紧急指针,TCP必须告诉用户进入“正常模式(normal mode)。如果紧急指针在用户处于紧急模式的时候被更新,这个更新对用户是不可见的。
    这个方法采用了一个在所有传输分片中传输的紧急字段。URG控制位指示了紧急字段有意义且必须被添加到分片系列号里面来产生紧急指针。如果没有这个标志,则表示没有显著的紧急数据。
    要发送一个紧急指示,用户必须发送至少一个数据字节。如果发送用户同时指示了一个push,紧急信息到目的进程的立即投递被加强。
    管理窗口(Managing the window)
    在每个分片中发送的窗口指示了窗口发送者(数据接收者)当前准备接收的系列号的范围。通常假定这是同这个连接的当前可用数据缓冲空间相关的。
    指定一个大的窗口可能会加大重传概率。如果多于可以被接受的数据到达,它将被丢弃。这将导致过多的重传,给网络和TCP增加了不必要的负载。指示一个小的窗口可以限制重传,但会在每个新的重传分片之间引入一个往返延迟。
    这个机制允许TCP广告一个大的窗口,随后没有接收那么多数据,广告一个小得多的窗口。这也称为“收缩窗口”,是非常令人气馁的.坚固原则规定TCP不会自己收缩窗口,但会对另一端的TCP的这种行为有所准备。
    发送TCP必须准备从用户接受并且发送至少一个八位字节的新数据即使发送窗口是0。发送TCP必须有规律地重传给接收TCP即使当窗口是0。当窗口是0时,建议重传间隔为2分钟。重传可以保证任何有一个0窗口的TCP的窗口的重新打开将会可靠地报告给另一端。
    当接收TCP有一个0窗口并且分片到达,它必须仍然发送一个确认说明它下一个期待的系列号和当前窗口(0)。
    发送TCP将要传输的数据包装成适合当前窗口的分片,且可以在重传队列中重新包装分片。这样的重新包装不要求,但是很有用。
    在一个有着单向数据流的连接中,窗口信息将会在确认分片中携带,这些分片具有相同的系列号,因此如果他们不按顺序到达,就没有办法记录他们。
    这不是一个严重的问题,但它将允许窗口信息有时临时地基于数据接收者的老的报告。一个避免这个问题的技巧是作用于携带最高的确认号码的窗口信息(也就是那些确认号码等于或者大于之前接收到的最高的确认号码)
    窗口管理程序对通信性能有重要影响。下面是一些对实现者的建议。
    窗口管理建议
    分配一个很小的窗口导致数据需要用很多小的分片传输,这时使用尽量少的大分片可以获取更优的性能
    接收者尽量避免更新窗口,直到额外分配至少达到了连接的最大可能分配的百分X(这里X可能从20到40)
    发送者尽量避免等到窗口足够大才发送小分片。如果用户用信号通知了一个push功能,则数据必须被发送,即使它是个小分片。
    注意确认不能被延迟,否则会导致不必要的重传。一个策略是当一个小分片到来的时候立即发送一个确认(没有更新窗口信息),然后发送另外一个带有新的窗口信息的确认,当窗口更大的时候。
    查到一个零窗口,发送可能会开始将传送数据分成越来越小的分片。如果一个包含单个数据八位字节的被发送且检查到0窗口的分片被接受,它就假定当前一个八位字节的窗口是可用的。如果发送TCP仅简单地发送任何窗口不为0的时候它能发送的数据,传输数据将会分成可以代替的大的和小的分片。随着时间的推移,在接收端让窗口分片可用的临时的停顿将导致大的分片被分成小的和不那么大的分片。过一会,数据传输会以大部分小分片进行。
    这里建议TCP实现需要主动尝试连接小窗口分片成为大窗口,因为管理窗口的机制在很多简单的实现中倾向于很多小的窗口。

    3.8 接口(Interfaces)

    当然,我们关心两组接口:用户/TCP接口和TCP/底层接口。我们有一个用户/TCP接口的相对详细的模型,但是底层协议模块的接口这里没有指定,因为它将由底层协议规范详细指定。在这个例子中,底层是IP,我们列出TCP可以使用了一些参数值。

    用户/TCP接口
    下面对TCP的用户命令描述尽可能地概括式的,因为每个操作系统有不同的接口。从而,我们必须警告读者,不同的TCP实现可能有不同的用户接口。但是,所有的TCP必须提供最少的一套服务接口以保证所有的TCP实现可以支持同样的层次结构。这个部分指定了所有TCP实现要求的功能性接口。

    TCP 用户命令
    下面的章节功能性地描述了一个用户/TCP接口。使用的符号同高级语言里面的大多数程序和接口函数相似,但这个用法不是用来排除圈套类型的服务调用(比如,SVCs,UUOs,EMTs)
    下面描述的用户命令指定了TCP必须实现以支持进程间通信的基本功能。各自的实现必须定义他们自己的形式,且可以在一个简单调用中提供基本功能的结合或者子集。特别地,一些实现可能希望自第一个SEND或者RECEIVE调用中自动OPEN一个连接。
    要提供进程间通信的接口,TCP不仅要接受命令,还要返回信息给他服务的进程。后一部分包括:
    (a) 一个连接的的概要信息(比如,中断,远端关闭,未指定外部端口的绑定)
    (b) 回复特定的用户命令指示成功或者各种类型的失败

    Open
    格式:OPEN(本地端口(local port),外部套接字(foreign socket),主动(active)/被动(passive)[,超时(timeout)][,优先级(precedence)][,安全性(security)/分隔(compartment)][,选项(options])–>本地连接名字(local connection name)
    我们假定本地TCP知道他服务的进程的id,并且将检查进程使用指定连接的权利。依赖于TCP的实现,本地网络和源地址的TCP标识将由TCP或者底层协议(比如IP)提供。这些考虑是是安全性考虑的结果,比如没有TCP可以假扮成其它TCP,等等。类似的,没有进程可以假扮成另一个进程,除非同TCP勾结。
    如果主动(active)/被动(passive)标志被置成被动,则是对LISTEN的调用,对到来的连接进行监听。一个被动的打开可以有一个完全指定的外部套接字来等待一个特定的连接或者一个未指定的外部套接字来等待任何调用,一个完全指定的被动调用可以通过后来的SEND的执行变成主动的。
    一个传输控制块(TCB)被创建且部分被从OPEN命令参数得到的数据填充。
    在一个主动调用命令里面,TCP将立即开始连接的同步过程(如,建立)。
    超时参数,如果出现的话,允许调用者对所有的传输给TCP的数据设定超时。如果数据不在指定的时间间隔被成功投递到目的地,TCP将终止连接。现在的缺省值是5分钟。
    TCP或者操作系统的某些部分将验证用户以指定的优先级或者安全性/分隔打开一个连接的权限。如果不在OPEN调用中指定优先级或者安全性/分隔,则采用缺省值。
    仅当安全性/分隔信息相同且优先级等于或者高于OPEN调用所要求的优先级,TCP将接受到来的请求。
    连接的优先级是OPEN调用所要的值和接收到的到来请求的值的高者,并且在连接的生命周期内固定在那个值。实现者可能希望给用户优先级协商的控制。比如,用户可能允许指定优先级必须严格匹配,或者任何提高优先级的尝试必须由用户确认。
    本地连接名字被TCP返回给用户。该名字可以用来作为由《本地套接字,外部套接字》对定义的连接的简称。

    Send
    格式:SEND(本地连接名字(local connection name),缓冲地址(buffer address),字节数量(bye count),PUSH标志,URGENT标志[,超时(timeout)])
    这个调用导致了包含在指定用户缓冲的数据在指定连接被发送。如果连接还没有打开,SEND被认为是一个错误。有些实现可能会允许用户先SEND;在这种情况下,将会自动调用OPEN。如果调用进程没有权利使用这个连接,返回一个错误。
    如果设置了PUSH标志,数据必须立刻被传送给接收者,且PUSH位将被设置于从缓冲中创建的最后一个TCP分片。如果PUSH标志没有设置,数据可以同之后的SEND调用的数据结合在一起,以提高传输效率。
    如果设置了URGENT标志,发送给目的IP的分片将设置紧急指针。如果紧急指针指示紧急指针之前的数据还没有被接收进程消耗掉的话,接收TCP将向接收进程标记紧急条件。Urgent的目的是激发接收者处理紧急数据,并指示接收者什么时候所有当前已知的紧急数据已经被接收。发送用户标识紧急的次数不一定同接收用户被通知紧急数据出现的次数相等。
    如果没有在OPEN中指定外部socket,但是连接建立了(比如,因为一个正在监听的连接由于外部分片到达本地socket变得特殊),则指定的缓冲被发送到隐含的外部socket。以未指定的外部socket使用OPEN的用户可以在不曾明确知道外部soeket的地址的情况下使用SEND。
    但是,如果SEND在外部socket确定之前被调用,将返回一个错误。用户可以使用STATUS调用来判断连接的状态。在某些实现中,TCP在一个未指定的socket被绑定时可能通知用户。
    如果指定了超时,这个连接的当前用户超时将变成新的值。
    在最简单的实现中,SEND将不会返回控制给发送进程,直到传输完成或者超时。但是,这种简单的方法可能会导致死锁(比如,连接的两端可能会在做任何RECEIVE之前尽力去SEND)和提供低性能,所以不被推荐。一个比较成熟的实现是立即返回,允许进程同网络I/0同时运行。多个SENDs以先到先服务的顺序被服务,因此TCP将那些它不能马上服务的接口调用放到队列中。我们暗中指定了一个用户接口,通过这个接口,SEND后来会从服务TCP发送某个种类的信号量或者伪中断。一个可以替代的方法是立即返回一个响应。比如,SENDs可能会立即返回一个本地的确认,即使要发送的分片还没有被远端TCP确认。我们乐观地假定事件成功。如果我们错误了,连接将由于超时关闭。在这种同步方式的实现中,也将会有一些异步信号量,但这些信号量将由连接自己处理,而不是由特定的分片或者缓冲。
    为了进程能够在不同的SENDs中区分错误或者成功指示,返回缓冲地址和SEND请求的响应编码是比较合适的。将在后面进行讨论的TCP-to-user信号量指示了必须返回给调用进程的信息。

    Receive
    格式:RECEIVE(本地连接名字(local connection name),缓冲地址(buffer address),字节数(byte count)-à字节数(byte count),紧急标志(urgent flag),push标志(push flag)
    这个命令分配了一个接收缓冲同指定连接联系在一起。如果之前没有OPEN或者调用进程没有权利使用这个连接,会返回一个错误。
    在最简单的实现中,控制权将不会返回给调用程序,除非缓冲满了,或者错误发生了,但是这种方式很容易引起死锁。一个比较成熟的实现是允许几个RECEIVEs同时出现。当分片到达的时候,相应的缓冲就被填充。这个策略允许以更加复杂的架构(可能是异步的)来通知调用程序PUSH已经被发送或者缓冲填满的代价来增加吞吐量。
    如果足够充满缓冲的数据在PUSH发现之前到达,PUSH标志将不会被设置来响应RECEIVE。缓冲将填充尽可能多的数据。如果PUSH标志在缓冲满之前发现,缓冲将在部分满的情况下返回,并指示PUSH标志。
    如果有紧急数据,紧急数据一到达,将通过一个TCP-to-user的信号通知用户。然后接收用户就进入紧急模式(urgent mode)。如果URGENT标志打开,额外的紧急数据保留着。如果URGENT标志关闭,RECEIVE的调用返回所有的紧急数据,用户可能现在离开紧急模式(urgent mode)。注意跟在紧急指针后面的数据(非紧急数据)不能在同一块缓冲里面同前面的紧急数据被投递给用户,除非边界被清晰地标注。
    为了区分几个显著的RECEIVEs以及照顾到缓冲没有被完全充满的情况,缓冲指针和一个指示真正收到的数据的真实长度的字节数同返回码一起返回。
    一个RECEIVE的替代的实现可能由TCP分配缓冲,或者TCP可以同用户共享一个环型缓冲。

    Close
    格式:CLOSE(本地连接名字(local connection name))
    这个命令将关闭指定的连接。如果连接没有打开或者调用进程没有权利使用该连接,将返回一个错误。关闭连接是个优雅的操作,所有的那些显著的SENDs将会被发送(和重传),直到所有都被服务。因此,在一个CLOSE之后进行几次SEND调用是可以被接收的,且期望所有的数据被发送到目的地。也要清楚用户必须继续在一个CLOSING的连接上RECEIVE,因为另一端可能正在发送剩下的数据。因此,CLOSE意思是“我没有数据要发送”,而不是“我不再接收任何数据”。有可能正在关闭的一端无法在超时前清除所有的数据(如果用户级别协议没有经过仔细考虑的话)。在这种情况下,CLOSE转入ABORT,正在关闭的TCP放弃。
    用户可能在任何时候主动CLOSE连接,或者响应从TCP到来的各种提示(比如,远端关闭执行了,传送超时,目的地无法到达)
    因为关闭一个连接需要同外部TCP通信,连接可以保留在CLOSING状态一小段时间。在TCP回复CLOSE命令前尝试重新打开一个连接将导致错误响应。
    关闭也暗示了一个push功能。

    Status
    格式:STATUS(本地连接名字(local connection name))–> 状态数据
    这是依赖于实现的用户命令,可以被排除,没有不利的影响。返回的信息通常来自于同连接联系的TCB。
    这个命令返回一个数据块,包含下面的信息:
    本地socket(local socket),
    外部socket(foreign socket),
    本地连接名字(local connection name),
    接收窗口(receive window),
    发送窗口(send window),
    连接状态(connection state),
    等待确认的缓冲的数目(number of buffers awaiting acknowledgment),
    等待接收的缓冲数目(number of buffers pending receipt),
    紧急状态(urgent state),
    优先级(precedence),
    安全性/分隔(security/compartment)
    传送超时(transmission timeout)
    基于连接的状态或者实现本身,有些信息可能无法获取或者没有意义。如果调用进程没有权利使用这个连接,将返回一个错误。这防止了未授权进程获取关于连接的信息。

    Abort
    格式:ABORT(本地连接名字(local connection name))
    这个命令将放弃所有悬而未决的SENDs和RECEIVEs,TCB被清除,特殊的RESET信息将被发送给连接另一端的TCP。依赖于实现,用户可能接收到每个显著的SEND或者RECEIVE的指示,或者可能只是收到一个ABORT确认。
    TCP-to-User信息
    假定操作系统为TCP提供了一个异步发送信号给用户程序的方法。当TCP发信号给用户程序的时候,一些信息传给了用户。在规范中,信息通常会是一个错误信息。在其它情况下,有可能是相关于执行SEND或者RECEIVE或者其它用户调用的完成情况的信息。
    下面的信息被提供:

    本地连接名字(Local Connection Nmae)                   总是提供
    响应字串(Resonse String)                               总是提供
    缓冲地址(Buffer Address)                               Send & Receive
    字节个数(Byte Cound(Counts bytes received))            Receive
    Push标志                                              Receive
    Urgent标志                                           Receive
    

    TCP/底层协议接口
    TCP调用一个底层协议模块来在网络上接收和发送信息。在ARPA 互联网系统中,底层协议是Internet协议(IP)
    如果底层级别的协议是IP,它提供了服务类型(type of service)和生存时间(time to live)等参数。TCP对于这些参数使用了下列设置:
    Type of Service = Precedence(优先级):routine(常规),Delay(延迟):normal(正常),Throughput(吞吐量):normal(正常),Reliability(可靠性):normal(正常);or 00000000
    Time to Live(生存时间) = 1分钟,或者00111100
    注意假定最大分片生命周期是2分钟。这里我们明确要求分片如果在1分钟内不能被投递,它必须被销毁。
    如果底层协议是IP(或者其它提供这个特性的协议),且使用源地址选路,接口必须允许选路信息进行通信。这特别重要,因为这样保证在校验和中使用的源地址和目的地址是初始的源和最后的目的地址。保留返回路径来响应连接请求也很重要。
    任何底层协议需要提供源地址,目的地址和协议字段,以及一种确定“TCP长度”的方式,既可以提供功能性的等同IP服务,也可以在TCP检验和中使用。

    3.9 事件处理(Event Processing)

    本章节描述的处理是一种可能实现的例子。其它实现可能在处理顺序上略有不同,但是他们仅在细节上有不同的地方,而不是本质上。
    TCP的活动可以被概括为对事件的响应。发生的事件可以分为三种类型:用户调用(user calls),分片到达(arriving segments),和超时(timeouts)。本章节描述TCP响应这些事件所做的处理。在很多情况下,处理需要依赖于连接的状态。
    发生的事件:
    用户调用
    OPEN
    SEND
    RECEIVE
    CLOSE
    ABORT
    STATUS
    到来分片
    SEGMENT ARRIVES
    超时
    用户超时(USER TIMEOUT)
    重传超时(RETRANSMISSION TIMEOUT)
    时间等待超时(TIME-WAIT TIMEOUT)
    TCP/用户接口的模型是用户命令接收到一个立即返回且可能有一个通过事件或者伪中断的延时响应。在下面的描述中,术语“信号(signal)”意味着导致了一个延时响应。
    错误的响应以字符串的形式给出。比如,执行一个不存在的连接的命令收到“error:connection not open”。
    请注意下面所有同系列号,确认号码,窗口等相关所有算法,都是对系列号空间大小进行模2**32。同时注意”=<”标识小于或者等于(模2**32)。
    一个处理到来分片的自然的方式是想像他们首先测试是否是正确的系列号码(比如,他们的内容在系列号空间上是否位于期望的“接收窗口”范围内)然后他们通常被放入队列中,以系列号顺序被处理。
    当一个分片同其它已经接收到的分片交迭在一起,我们重建分片,仅包含新的数据,然后调整头部字段以保持一致。
    注意如果没有提到状态改变,TCP停留在同一个状态。

    OPEN调用
    CLOSED状态(如,TCB不存在)
    创建一个新的传输控制块(TCB)来保存连接状态信息。填充本地socket标识,外部 socket,优先级,安全性/分隔(compartment)和用户超时信息。注意有些外部socket可能在一个被动的OPEN调用里没有被指定,且将在到来SYN分片的参数里面被填充。允许用户验证所要求的安全性和优先级,如果不满足返回“error: precedence not allowed”或者“error:security/compartment not allowed“。如果是被动的,进入LISTEN状态,返回。如果是主动的,且外部socket没有被指定,返回”error: foreign socket unspecified“;如果是主动的,且外部socket指定了,发起一个SYN分片。选择一个初始的发送系列号码(ISS:initial send sequence number)。一个<SEQ=ISS><CTL=SYN>形式的SYN分片被发送。设置SND.UNA成ISS,SND.NXT为ISS+1,进入SYN-SENT状态,返回。
    如果调用者没有访问指定的本地socket的权利,返回“错误:连接对该进程非法(connection illegal for this process)”。如果没有空间创建一个新的连接,返回”错误:资源不足(insufficient resources)”。
    LISTEN状态
    如果是主动方式且指定了外部socket,则将连接从被动改成主动,选择一个ISS。发送一个SYN分片,设置SND.UNA为ISS,SND.NXT为ISS+1。进入SYN-SENT状态。同SEND联系在一起的数据可以同SYN分片一起发送或者放到队列中,在进入ESTABLISHED状态后发送。如果在命令中包含紧急位,紧急位必须同该命令的数据分片一起被发送。如果没有空间来将请求放入队列,响应”错误:资源不足(insufficient
    resources)”。如果外部socket没有指定,返回“错误:外部socket未指定(foreign socket unspecified)”
    SYN-SENT STATE
    SYN-RECEIVED STATE
    ESTABLISHED STATE
    FIN-WAIT-1 STATE
    FIN-WAIT-2 STATE
    CLOSE-WAIT STATE
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT STATE
    返回”错误:连接已经存在(connection already exists)”.

    SEND调用
    CLOSED状态(比如,TCB不存在)
    如果用户没有访问这个连接的权限,返回”error:connection illegal for this process”.
    否则,返回”error:connection does not exist”.
    LISTEN状态
    如果指定了外部socket,则将连接从被动改成主动,选择一个ISS。发送一个SYN 分片,将SND.UNA设置成ISS,SND.NXT设置成ISS+1。进入SYN-SENT状态。
    同SEND联系的数据可以同SYN分片一起发送,或者放到队列中,进入
    ESTABLISHED状态后发送。如果命令中要求了紧急位,紧急位必须同命令的数据分片一起发送。如果没有空间将请求放到队列中,响应“error:insufficient resources”。
    如果外部socket未指定,返回”err:foreign socket unspecified”。
    SYN-SENT状态
    SYN-RECEIVED状态
    将数据放到队列中,进入ESTABLISHED状态后发送。如果队列中没有空间,响应”error:insufficient resources”。
    ESTABLISHED状态
    CLOSE-WAIT状态
    将缓冲组装成分片,发送该分片和一个确认(确认值=RCV.NXT)。如果没有足够的空间来记录这个缓冲,简单地返回”error:insufficient resources”。
    如果设置了紧急标志,则SND.UP<-SND.NXT-1,并在外出分片中设置紧急指针。
    FIN-WAIT-1 状态
    FIN-WAIT-2状态
    ClLOSING状态
    LAST-ACK状态
    TIME-WAIT状态
    返回”error:connection closing”,不服务请求。

    RECEIVE调用
    CLOSED状态(比如,TCB不存在)
    如果用户没有访问这个连接的权限,返回”error:connection illegal for this process”. 否则,返回”error:connection does not exist”.
    LISTEN状态
    SYN-SENT状态
    SYN-RECEIVED状态
    放到队列中,等到TCB进入ESTABLISHED状态后处理。如果队列中没有空间放置该请求,响应”error: insufficient resource”
    ESTABLISHED状态
    FIN-WAIT-1 状态
    FIN-WAIT-2状态
    如果没有充足的分片在队列中来满足请求,将请求放到队列中。如果没有队列空间来记住RECEIVE,响应 ”error: insufficient resource”. 重新组装在队列中的到来分片,放到接收缓冲中,返回给用户。如果发现PUSH标志,标记“push seen”
    如果RCV.UP在当前传给用户的数据之前,通知用户紧急数据的存在。
    当TCP负起了投递数据给用户的责任,必须通过发送一个确认来告诉发送者。确认的组成在下面处理到来分片中描述。
    CLOSE-WAIT状态
    因为远端已经发送了FIN,RECEIVEs必须被已经在手上的文本满足,但不投递给用户。如果没有文本等待投递,RECEIVE将获得“error:connection closing “响应。否则,任何还保留的文本可以用来满足RECEIVE。
    CLOSING 状态
    LAST-ACK 状态
    TIME-WAIT状态
    返回”error:connection closing”

    CLOSE调用
    CLOSED状态(如,TCB不存在)
    如果用户没有权限访问这个连接,返回“error:connection illegal for this process”.
    否则,返回”error:connection does not exist”。
    LISTEN状态
    任何显著的RECEIVEs都会返回“error:closing”响应。删除TCB,进入CLOSED状态,返回
    SYN-SENT状态
    删除TCB,返回”error:closing”响应给任何队列中的SENDs,或者RECEIVEs。
    SYN-RECEIVED状态
    如何没有SENDs被发起且没有悬而未决的数据要发送,则组装一个FIN分片并发送它,然后进入FIN-WAIT-1状态,否则放到队列中,在进入ESTABLISHED后处理。
    ESTABLISHED状态
    放到队列中,在所有的SENDs组装发送后,组装一个FIN分片并发送它。任何情况下,进入FIN-WAIT-1状态。
    FIN-WAIT-1状态
    FIN-WAIT-2状态
    严格地说,这是一个错误,将收到”error:connection closing”响应。一个“ok”响应也可以被接受,只要第二个FIN没有被发送(但第一个FIN可能被重传)
    CLOSE-WAIT状态
    将请求放到队列中,直到所有之前的SENDs被组装成分片,然后发送一个FIN分片,进入CLOSING状态。
    CLOSING状态
    LAST-ACK状态
    TIME-WAIT状态
    响应“error:connection closing”

    ABORT调用
    CLOSE状态(比如,TCB不存在)
    如果用户没有权限访问这个连接,返回“error:connection illegal for this process”.
    否则,返回”error:connection does not exist”。
    LISTEN状态
    任何显著的RECEIVEs都会返回“error:closing”响应。删除TCB,进入CLOSED状态,
    返回
    SYN-SENT状态
    给所有的队列中的SENDs和RECEIVEs发送“connection reset”通知,删除TCB,进入CLOSED状态,返回
    SYN-RECEIVED
    ESTABLISHED
    FIN-WAIT-1
    FIN-WAIT-2
    CLOSE-WAIT状态
    发送一个reset分片:
    <SEQ=SND.NXT><CTL=RST>
    给所有的队列中的SENDs和RECEIVEs发送“connection reset”通知.所有等待传输(除了上面所述的RST)和重传的分片被清空,删除TCB,进入CLOSED状态,返回。
    CLOSING
    LAST-ACK
    TIME-WAIT
    响应“ok”,删除TCB,进入CLOSED状态,返回

    STATUS调用
    CLOSED状态(如,TCB不存在)
    如果用户没有权限访问这个连接,返回“error:connection illegal for this process”.
    否则,返回”error:connection does not exist”。
    LISTEN状态
    返回”state=LISTEN”,返回TCB指针
    SYN-SENT 状态
    返回”state=SYN-SENT”,返回TCB指针
    SYN-RECEIVED状态
    返回”state=SYN-RECEIVED”,返回TCB指针
    ESTABLISHED状态
    返回”state=ESTABLISHED”,返回TCB指针
    FIN-WAIT-1状态
    返回”state=FIN-WAIT-1”,返回TCB指针
    FIN-WAIT-2状态
    返回”state=FIN-WAIT-2”,返回TCB指针
    CLOSE-WAIT状态
    返回”state=CLOSE-WAIT”,返回TCB指针
    CLOSING状态
    返回”state=CLOSING”,返回TCB指针
    LAST-ACK状态
    返回”state=LAST-ACK”,返回TCB指针
    TIME-WAIT状态
    返回”state=TIME-WAIT”,返回TCB指针

    分片到达
    1)如果处于CLOSED状态(如TCB不存在),则
    所有到来分片的数据被丢弃。包含RST的到来分片被丢弃。不包含RST的到来分片将导致RST作为响应被发送。确认和系列号字段值被选择用来使reset系列号可被发送分片的TCP接受。
    如果ACK位关闭,系列号0被使用
    <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
    如果ACK位打开
    <SEQ=SEG.ACK><CTL=RST>
    2)如果处于LISTEN状态,则
    第一步,检查下 RST
    一个到来的RST必须被忽略,返回
    第二步,检查下ACK
    任何在连接仍然处于LISTEN状态下到来的确认是错误的。一个可以接受的reset分片必须为任何一个到来的ACK产生分片形成。RST格式如下:
    <SEQ=SEG.ACK><CTL=RST>
    返回
    第三步,检查SYN
    如果设置了SYN位,检查安全性。如果到来分片的安全性(security)/分隔(compartment)没有同TCB的安全性和分隔完全匹配,则发送一个reset并返回。
    <SEQ=SEG.ACK><CTL=RST>
    如果SEG.PRC大于TCB.PRC,则如果用户允许,系统设定TCB.PRC=SEG.PRC,
    如果不允许,则发送一个reset,返回。
    <SEQ=SEG.ACK><CTL=RST>
    如果SEG.PRC小于TCB.PRC则继续。
    设定RCV.NXT为SEG.SEQ+1,IRS设置成SEG.SEQ,其它的控制或者文本被放到队列中以后处理。ISS必须被选择,一个如下形式的SYN分片被发送:
    <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
    SND.NXT被设置成ISS+1,SND.UNA设置成ISS。连接状态必须被改成SYN-RECEIVED.注意任何其它的到来控制或者数据(同SYN结合)将在SYN-RECEIVED状态被处理,但是不用重复处理SYN和ACK。如果listen没有被完全指定(如,外部socket没有完全指定),则未指定字段现在必须被填充。
    第四步,其它文本或者控制
    任何其它控制或者文本产生分片(不包括SYN)都有ACK,则会被一个ACK处理丢掉。一个到来的RST分片不能是合法的,因为它不可能是为了响应任何这个连接的化身发送的。所以你不太可能收到一个RST分片,但如果收到了,丢弃分片,返回。
    3)如果处于SYN-SENT状态,则
    第一, 检查ACK位
    如果设置了ACK位
    如果SEG.ACK=<ISS,或者SEG.ACK>SND.NXT,发送一个reset(除非设置了RST位,如果是这样的话,丢弃分片,返回 )
    <SEQ=SEG.ACK><CTL=RST>
    并且丢弃那个分片,返回。
    如果SND.UNA=<SEG.ACK=<SND.NXT则ACK可以被接受
    第二, 检查RST位
    如果设置了RST位
    如果ACK是可以接受的,则发信号通知用户”error:connection reset”,丢弃分片,进入CLOSED状态,删除TCB,返回。否则(没有ACK)丢弃分片返回。
    第三, 检查安全性和优先级
    如果分片的安全性/分隔同TCB的安全性/分隔没有完全匹配,发送一个 reset
    如果有一个 ACK
    <SEQ=SEG.ACK><CTL=RST>
    否则
    <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
    如果有一个 ACK
    分片的优先级必须同TCB的优先级匹配,如果不匹配,发送一个 reset
    <SEQ=SEG.ACK><CTL=RST>
    如果没有 ACK
    分片的优先级高于TCB的优先级,则如果用户允许,系统就提高TCB的优先级到分片的优先级,如果不允许提高优先级,就发送一个 reset
    <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
    如果分片的优先级小于TCB的优先级则继续
    如果发送了reset,丢弃分片,返回
    第四, 检查SYN位
    这一步只有当ACK是ok的时候才需要,或者当没有ACK,且分片不包含一个RST。
    如果SYN位打开,且安全性/分隔和优先级可以被接受,则设置RCV.NXT为SEG.SEQ+1,设置IRS为SEG.SEQ。SND.UNA被提高到等于SEG.ACK(如果有一个ACK),且被确认过的在重传队列的任何分片必须被移走。
    如果SND.UNA>ISS(我们的SYN已经被确认),改变连接状态到ESTABLISHED,组装一个ACK分片
    <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK)
    然后发送它。在队列中等待重传的数据或者控制可以被包括在里面。如果分片中有其它控制或者文本,则在下面的第六步被处理(在第六步中会检查URG位),否则返回
    否则进入SYN-RECEIVED,组装一个SYN,ACK分片
    <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
    然后发送它。如果分片中有其它的控制或者文本,放到队列中,等到ESTABLISHED状态后进行处理,返回。
    第五, 如果SYN和RST位都没有被设置,则丢弃分片,返回

    4)否则,
    第一步,检查系列号
    SYN-RECEIVED
    ESTABLISHED
    FIN-WAIT-1
    FIN-WAIT-2
    CLOSE-WAIT
    CLOSING
    LAST-ACK
    TIME-WAIT状态
    分片按系列号顺序被处理。对到来分片进行初始测试以丢弃老的复制分片,但进
    一步的处理以SEG.SEQ顺序进行。如果分片的内容跨越了老的分片和新的分片的
    边界,则只有新的部分必须被处理。
    对到来分片,有4种可接受性测试:
    分片接收测试

    长度         窗口   
    -------        --------     ------------------------------------------------------------------
    0            0         SEG.SEQ = RCV.NXT
    0            >0        RCV.NXT=<SEG.SEQ <RCV.NXT+RCV.WND
    >0           0         不能接受
    >0           >0       RCV.NXT=<SEG.SEQ<RCV.NXT+RCV.WND
                          或者
    RCV.NXT=<SEG.SEQ+SEG.LEN-1<RCV.NXT+RCV.WND
    

    如果RCV.WND是0,则不能接受分片,但是合法的ACKs,URGs和RSTs例外。
    如果到来分片是不被接受的,必须发送一个确认进行响应(除非设置了RST位,如果这样的话,丢弃分片并返回):
    <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
    发送确认以后,丢弃不能被接受的分片并返回。
    接下来的例子假定分片是一个理想化的分片,它开始于RCV.NXT,没有超过窗口。可以通过去掉任何位于窗口之外的部分(包括SYN和FIN)对真正的分片进行剪裁以满足这个假设,且仅当分片开始于RCV.NXT时做进一步的处理。开始于高一些的系列号的分片留到以后进行处理。
    第二步:检查RST位
    SYN-RECEIVED状态
    如果设定了RST位
    如果连接是由一个被动的OPEN触发(比如,从LISTEN状态变更过来),则将该连接返回到LISTEN状态并返回。不需要通知用户。如果连接是通过一个主动的OPEN触发(比如,从SYN-SENT状态转化过来),则连接被拒绝,告知用户“connection refused”.在上面两种情况中的任何一种,所有在重传队列的分片必须被从队列中移走。在主动OPEN的情况中,进入CLOSED状态,删除TCB,返回。
    ESTABLISHED
    FIN-WAIT-1
    FIN-WAIT-2
    CLOSE-WAIT
    如果设置了RST位,则任何显著的RECEIVEs和SEND都会收到“reset”响应。所有分片队列都会被清空。用户也会接收一个未被恳求的“connection reset”信号。进入CLOSED状态,删除TCB,返回。
    CLOSING状态
    LAST-ACK状态
    TIME-WAIT状态
    如果设置了RST,则进入CLOSED状态,删除TCB,返回
    第三步:检查安全性和优先级
    SYN-RECEIVED
    如果分片的安全性/分隔和优先级同TCB的安全性/分隔,优先级没有完全匹配,
    发送一个reset,返回
    ESTABLISHED状态
    如果分片的安全性/分隔和优先级同TCB的安全性/分隔,优先级没有完全匹配,发
    送一个reset,任何显著的RECEIVEs和SEND将接收到“reset”响应。所有的分片队列必须被清空。用户也会接收到一个未被期待的“connection reset”信号。进入CLOSED状态,删除TCB,返回。
    注意这个检查是在系列号检查之后,以防一个老的连接的分片有不同的安全性或者优
    先级,导致当前连接被终止掉。
    第四步:检查SYN位
    SYN-RECEIVED
    ESTABLISHED
    FIN-WAIT STATE-1
    FIN-WAIT STATE-2
    CLOSE-WAIT STATE
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT STATE
    如果SYN在窗口内,它是一个错误,发送一个reset,任何显著的RECEIVEs和
    SEND将收到“reset”响应,所有的分片队列将被清除,用户也将收到一个未被
    恳求的“connection reset”信号,进入CLOSED状态,删除TCB,返回。
    如果SYN不在窗口内,不会到这个步骤且在第一个步骤一个ack将被发送(系
    列号检查)。
    第五步,检查ACK字段
    如果ACK位没有打开,丢弃分片,返回
    如果ACK位打开了
    SYN-RECEIVED状态
    如果SND.UNA=<SEG.ACK=<SND.NXT,则进入ESTABLISHED状态,继续处理。
    如果分片确认是不被接受的,组装一个reset分片
    <SEQ=SEG.ACK><CTL=RST>
    并发送它
    ESTABLISHED状态
    如果SND.UNA<SEG.ACK=<SND.NXT,设置SND.UNA=SEG.ACK.任何因此被确认的重传队列中的分片被移走。用户将接收到已经发送且完全被确认的缓冲的积极的确认(如,SEND缓冲将以“OK”响应返回)。如果ACK是一个重复分片(SEG.ACK<SND.UNA),它将被忽略。如果ACK确认了没有被发送的分片(SEG.ACK>SND.NXT)则发送一个ACK,丢弃分片,返回。
    如果SND.UNA<SEG.ACK=<SND.NXT,则发送窗口将被更新。如果(SND.WL1<SEG.SEQ 或者(SND.WL1=SEG.SEQ且SND.WL2=<SEG.ACK)),设置SND.WND=SEG.WND,设置SND.WL1=SEG.SEQ,并设置SND.WL2=SEG.ACK。
    注意SND.WND是从SND.UNA的一个偏移,SND.WL1记录了用来更新SND.WND的最后一个分片的系列号。这里的检查避免了使用老分片来更新窗口。
    FIN-WAIT-1 状态
    除了ESTABLISHED状态要求的处理,如果我们的FIN现在被确认则进入FIN-WAIT-2状态,并在那个状态中继续处理
    FIN-WAIT-2状态
    除了ESTABLISHED状态的处理,如果重传队列是空的,用户的CLOSE可以被确认(“OK”),但并不删除TCB
    CLOSE-WAIT状态
    同ESTBLISHED状态的处理相同
    CLOSING状态
    除了ESTABLISHED状态的处理,如果ACK确认了我们的FIN,则进入TIME-WAIT状态,否则忽略分片
    LAST-ACK状态
    在这个状态唯一要做的事情是我们的FIN的确认。如果我们的FIN现在被确认了,就删除TCB,进入CLOSED状态,返回
    TIME-WAIT状态
    这个状态能够到来的分片是远端FIN的重传。确认它,并重新启动2 MSL的超时。
    第六步,检查URG位
    ESTABLISHED STATE
    FIN-WAIT-1 STATE
    FIN-WAIT-2 STATE
    如果设置了URG位,将RCV.UP设置成max(RCV.UP,SEG.UP),如果紧急指针在消耗掉的数据之前,告知用户远端有紧急数据。如果对于这个连续系列的紧急数据用户已经被告知(或者仍然处理“紧急模式”) ,不用再通知用户。
    CLOSE-WAIT STATE
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT
    这不应该发生,因为已经收到了远端的FIN。忽略URG。
    第七步,处理分片文本
    ESTABLISHED STATE
    FIN-WAIT-1 STATE
    FIN-WAIT-2 STATE
    一旦处于ESTABLISHED状态,传送分片文本到用户接收缓冲就成为可能。分片的文本可以等到缓冲满或者分片为空的时候移到缓冲。如果分片为空并携带了一个PUSH标志,则当缓冲返回的时候告知用户接收到PUSH。
    当TCP负责投递数据给用户的时候,它必须对数据的收到进行确认。
    一旦TCP接管了数据,他提升RCV.NXT到接收的数据,并调整RCV.WND为适合于当前缓冲可能性的值,RCV.NXT和RCV.WND的总值不能被减少。
    请注意3.7节的窗口管理建议
    发送一个如下格式的确认:
    <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
    这个确认必须同一个分片在一块,该分片如果可能必须不延迟被传送。
    CLOSE-WAIT STATE
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT STATE
    这不会发生,因为已经从远端收到了一个FIN。忽略分片文本。
    第八步,检查FIN位
    如果处于CLOSED,LISTEN或者SYN-SENT状态,不要处理FIN,因为SEG.SEQ
    不是合法的;丢弃分片,返回
    如果设置了FIN位,告知用户“connection closing”,返回给任何悬而未决的RECEIVEs
    同一条信息,提高RCV.NXT到FIN,发送一个FIN的确认。注意,FIN暗示了任何
    未投递给用户的分片文本的PUSH操作。
    SYN-RECEIVED 状态
    ESTABLISHED 状态
    进入CLOSE-WAIT 状态
    FIN-WAIT-1 状态
    如果我们的FIN已经被确认(可能就在这个分片里面做了确认),则进入
    TIME-WAIT,启动time-wait定时器,关闭其它定时器;否则进入CLOSING状

    FIN-WAIT-2 状态
    进入TIME-WAIT状态,启动time-wait定时器,关闭其它定时器
    CLOSE-WAIT 状态
    维持在CLOSE-WAIT状态
    CLOSING 状态
    维持在CLOSING状态
    LAST-ACK状态
    维持在LAST-ACK状态
    TIME-WAIT状态
    维持在TIME-WAIT状态,重新启动一个2 MSL time-wait超时。
    然后返回。

    用户超时(USER TIMEOUT)
    任何状态,如果用户超时已经到达,清空队列,告知用户“error:connection aborted due
    to user timeout“, 对于任何显著的调用,也返回该消息,删除TCB,进入CLOSED
    状态并返回

    重传超时(RETRANSMISSION TIMEOUT)
    任何状态,如果重传队列中的一个分片重传超时到达,将分片再一次放到重传队列的前
    面,重新初始化重传定时器,并返回

    TIME-WAIT超时
    如果一个连接的time-wait到达,删除TCB,进入CLOSED状态并返回。

    展开全文
  • TCP协议RFC793

    2015-08-21 15:47:20
    TCP - Transport Control Protocol,TCP协议RFC793
  • 1.TCP报头格式 TCP段作为IP段数据发送。IP报头携带几个信息字段,包括:源主机地址和目标主机地址。TCP头紧跟在IP头后面,提供特定的TCP协议信息。如下图所示: TCP报文头格式: ...该字段包含由这个主机随机选择的...

    1.TCP报头格式

    TCP段作为IP段数据发送。IP报头携带几个信息字段,包括:源主机地址目标主机地址TCP头紧跟在IP头后面,提供特定的TCP协议信息。如下图所示:

    • TCP报文头格式:
    • TCP报文头说明:
    NameLenghtFunc Description
    Source Port16 bits源端口号
    Destination Port16 bits目标端口号
    Sequence Number32 bits会话的序列号;
    该字段包含由这个主机随机选择的初始序号ISNInitial Sequence Number),发送数据的第一个字节序号为ISN+1,因为 SYN 标志会占用一个序号。
    Acknowledgment Number32 bits确认序号;
    确认序号就包含接收端所期望收到的下一个报文的Sequence Number为该确认序号。
    Data Offset4 bits数据区域偏移量(又名:首部长度);
    这指明数据从哪里开始,同时说明了TCP Header的大小。计算公式:Data Index = Data Offset * 4(Data Index单位:Bytes)
    Reserved6 bits预留,必须是零。
    Control Bits6 bitsFlags标记字段:
    URG:首部中的紧急指针字段标志,如果是 1 表示紧急指针字段有效。
    ACK:首部中的确认序号字段标志,如果是 1 表示确认序号字段有效。
    PSH:该字段置一表示接收方应该尽快将这个报文段交给应用层。
    RST:重新建立 TCP 连接。
    SYN:用同步序号发起连接。
    FIN: 中止连接。
    Window16 bits窗口大小;
    每一端通过提供的窗口大小来实现TCP流量控制,窗口大小为字节数。
    Checksum16 bits检验和字段;
    检验和覆盖了整个的 TCP 报文段:TCP 首部和 TCP 数据区域,由发送端计算和填写,并由接收端进行验证。
    Urgent Pointer16 bits紧急指针字段;
    只有当 URG标志置 1 时紧急指针才有效,紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
    Optionsvariable选项字段;
    在下面专门解释…
    Paddingvariable填充字段;
    TCP报头填充用于确保TCP报头以32位对齐结束,数据以32位对齐开始。填充由零组成。
    • Options选项字段说明
      当前定义的选项列表如下:
    Kind (Option Code)LengthMeaning
    0x00-End of option list.
    0x01-No-Operation.
    0x024Maximum Segment Size.

    具体选项定义:

    1. End of Option List0x00
      这个Option Code(0x00)表示选项列表的末尾。
    2. No-Operation0x01
      这个Option Code(0x01)可能被用在2个Options之间,为了后面的Option能够字对齐开始。
    3. Maximum Segment Size0x02
      在这里插入图片描述
      如上图所示,Maximum Segment Size Option 长度为4个byte(包括:Kind、Length和Max Seg Size)。如果存在这个选项,它将指示发送这个报文的TCP端最大可接收报文的大小。这个字段只能在初始连接请求时发送(即,在发送SYN报文中)。如果这个选项没有被使用,那么将允许任何大小的报文通信。

    2.TCP连接状态总图

    一个TCP连接进程在生命周期会经历一系列的状态变化。这些状态是:LISTEN, SYN-SENT, SYN-RECEIVED,ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT,CLOSED。TCP连接状态总图如下:
    在这里插入图片描述

    NameFunc Description
    LISTEN表示等待任何一个远程的TCP连接请求。
    SYN-SENT表示已经发送了一个连接请求后,等待一个匹配连接请求。
    SYN-RECEIVED表示在接收和发送连接请求后,等待一个确认连接请求ACK。
    ESTABLISHED表示已经建立连接,接收到的数据可以传递给用户。这个是数据传输阶段的正常状态。
    FIN-WAIT-1表示等待来自远程TCP的连接终止请求,或对先前发送的连接终止请求的确认。
    FIN-WAIT-2表示等待来自远程TCP的连接终止请求。
    CLOSE-WAIT表示等待本地用户的连接终止请求。
    CLOSING表示等待来自远程TCP的连接终止请求ACK。
    LAST-ACK表示等待之前发送到远程TCP的连接终止请求的ACK(包括对其连接终止请求的ACK)。
    TIME-WAIT表示等待足够的时间以确保远程TCP接收到连接终止请求的ACK。
    CLOSED表示没有在连接状态。

    3. TCP Client连接状态图

    TCP Client连接状态图是根据TCP Client发起连接、传输数据和断开连接这个个过程所经历的状态。如下图(此图相当于上面的总图分解):
    在这里插入图片描述

    4.TCP Server连接状态图

    待验证更新...

    5.TCP三次握手和四次握手

    • TCP三次握手(左)TCP四次握手(右)
    • TCP连接、发送数据、断开

    6.Wireshark如何显示通信时的时序图

    在这里插入图片描述

    在这里插入图片描述

    展开全文
  • tcp协议,rfc793,pdf格式,英文版,如果有需要但是没有分的私信我,留下邮箱,我单独发给你。
  • RFC793-TCP中文翻译

    2021-10-29 15:54:54
    简介 动机(Motivation) 范围(Scope) 关于本文档(About this Document) 接口(Interfaces) 操作(Operation) TCP介绍 基本数据传输 可靠性(序号、超时、确认、重传) ...

    简介

    翻译自:rfc793

    TCP是一个高可靠的主机到主机之间的通讯协议,这些主机通常位于分组交换网络(基于IP协议)之中。

    本文档主要描述TCP的一些基本功能、实现时的相关步骤、以及在使用TCP时用户和程序需要关心的相关接口。

    动机(Motivation)

    计算机通讯系统在军事、政府、社会中越来越重要。 本文档主要关注军事方面的通讯需求,特别是如何实现可靠的通讯以及在出现网络拥塞时如何保证网络可用性,这些问题也同样出现在政府和社会通讯系统中。

    随着战略战术计算机通讯网络的研发和部署,有必要提供一些连通这些网络的方法以及可以支持大部分应用进程可以互相通讯的标准协议。出于对这种标准的需要,负责研究与工程的副部长助理将本文讨论的TCP协议作为美国国防部系统中进程通讯协议标准的基础。

    TCP是面向连接的、端到端的可靠通讯协议,它被设计到一个支持多网络应用的层级网络协议栈中。TCP主要为两个不同主机中的进程提供可靠的通讯服务,这两个主机处于独立并且相互连通的计算机通讯网络中。在网络协议栈(OSI七层或者TCP/IP五层网络架构)中,处于TCP下面的网络协议不保证数据通讯的可靠性。TCP假定会从下层协议获取简单的但是可能乱序的数据报服务。原则上TCP可以在很多种类的通讯系统上运行,其中包括分组交换网络、电路交换网络(虚电路)、硬线连接的网络等。

    TCP基于Cerf 和 Kahn在【Cerf, V., and R. Kahn, "A Protocol for Packet Network Intercommunication", IEEE Transactions on Communications,Vol. COM-22, No. 5, pp 637-648, May 1974.】论文中提出的相关概念。在网络协议栈中,TCP位于IP之上,IP协议可以为TCP提供接收与发送可变长度的TCP数据段服务,IP协议把这些数据段放在网际报文'信封'中。网络报文可以在不同网络中定位源TCP(端口)和目的TCP(端口)。当TCP数据段需要在多个网络和互连网关转发和传输时,IP协议需要处理TCP数据段的拆包和重组问题。IP协议可以携带关于优先级、安全等级、TCP数据段拆包相关信息,这些信息可以穿过不同的网络保证了端(进程)到端之间的通信。

                               Protocol Layering
    
                            +---------------------+
                            |     higher-level    |
                            +---------------------+
                            |        TCP          |
                            +---------------------+
                            |  internet protocol  |
                            +---------------------+
                            |communication network|
                            +---------------------+
    
                                    Figure 1
    

    本文档约定TCP协议实现程序需要和主机中的上层协议实现程序共同运行。有些计算机系统需要通过一个前端机去联网,这个前端机安装有TCP和IP协议软件,同样也包括其他相关的网络软件。TCP规范为上层网络协议提供了一套接口,这套接口同样适合前端机场景,只要主机到前端机之间实现了相关的通讯协议(host-to-front end protocol)。

    范围(Scope)

    TCP为在多网络环境中的主机进程提供进程到进程的可靠通讯服务。TCP希望成为多种网络环境中广泛使用的通讯协议。

    关于本文档(About this Document)

    本文档描述了实现TCP的具体规则,包括与上层协议交互时的规则、与其他TCP程序进行交互所需要的规则。本章剩余部分对协议接口和具体操作进行了详细地描述。第二章总结了TCP设计的哲学基础。第三章描述了不同的事件(新数据段到达、用户调用、出错等)出现的时候TCP采用的动作和TCP数据段的数据格式。

    接口(Interfaces)

    TCP接口包括面向用户或者应用进程的接口和面向底层协议(比如IP协议)的接口。

    我们会比较详细的介绍应用程序和TCP之间的接口,这个接口包括一组调用,这种调用类似于操作系统为应用程序提供的操作文件的系统调用。比如用来打开链接和关闭链接的调用,在建立的链接上发送数据和接收数据的调用。同时TCP组件和应用程序异步通讯的功能比较受期待。针对特定的操作系统平台,TCP在实现的过程中是可以有合理的自由度去设计接口,对于TCP/user之间的接口,任何合理的实现都应该保持一个约定的最小范围内的功能列表。

    TCP和底层协议之间没有规定具体的实现细节,但是假设有一种机制可以在两层协议间进行异步的消息传递。通常人们希望底层协议会定义一个这样的异步接口。这里的底层协议在本文档中一般指IP协议。

    操作(Operation)

    正如上面文章描述的,TCP主要的目的是为两个不同进程间提供可靠的、安全的逻辑电路(logical circuit)或者连接(connection)服务。在不可靠的网络通讯系统中提供这种稳定可靠的服务需要在以下几个领域具备基本的实现(facilities):

    1. 基本数据传输
    2. 可靠性
    3. 流量控制
    4. 多路复用
    5. 长连接
    6. 优先级和安全

    关于TCP这几个实现的基本操作(basic operation)都在后续章节进行介绍。

    TCP介绍

    基本数据传输

    TCP可以在两个用户进程之间进行双向的字节流传输,它是通过把一定数量的字节放在数据段中并在网络上传输来实现(字节流传输)的。通常,TCP自主决定什么时候对数据进行打包(block)并发送出去。

    有时候用户需要确定提交给TCP的数据已经被TCP发送出去了。为了实现这个功能,TCP定义了一个推数据(PUSH)的功能。发送者需要指定要把数据推送(PUSH)到接收方,才能保证提交给TCP的数据被发送出去。一个推的指令会立即把在那个时刻收集到的数据(data up to that point)发送到接收方。发送PUSH指令的准确时间接收方可能不会知道,并且推送功能也不会提供这次推送的数据量相关信息。

    可靠性(序号、超时、确认、重传

    TCP必须能解决由通讯网络造成的数据损坏、丢失、重复、或者乱序问题。TCP通过给每个字节添加递增序列号并且要求接收方收到数据后发送一个确认(ack)来解决上述问题。如果在一定时间内没有收到确认,发送方会再次发送数据。接收方通过序列号为乱序的字节排序,并且可以把重复的数据段丢弃。通过给每个传送的数据段添加校验和可以检测数据是否被破坏,接收方收到数据后检测校验和并丢弃受到破坏的数据段。

    只要TCP运行良好,并且通讯网络不会出现完全独立的两个子网(网络分区),数据的传输错误一般都不会影响数据的正常分发。TCP也通常可以从通讯网络错误中恢复。

    流量控制(Flow Control)

    TCP为接收方提供一种可以控制发送方发送数据量的方法。接收方在每次发送确认信息(ACK)的时候携带一个win参数来通知发送方剩余的可发送字节数。

    多路复用(Multiplexing)

    为了可以让在同一个主机上的多个进程同时使用TCP提供的通讯组件,TCP为每个主机提供了多个端口。Socket把TCP和底层的网络传输层串联起来。一对Socket独立标识一个连接。一个Socket可以同时用在多个连接中(java 中的serversocket便是如此,主动模式下的FTP 20数据端口也可以)。

    每个主机会把不同的进程绑定到不同的端口上。通常会把经常使用的应用程序(日志或者日期服务,http,ft应用)绑定到固定的端口上。这些应用程序提供的服务之后可以通过这些端口地址访问。

    连接(Connections)

    上面文章讨论的可靠性和流量控制机制需要TCP为每个数据流建立和维护相关的状态信息。Socket、字节序列号、窗口大小还有其他类似的状态信息统称为连接。每个连接都被一对Socket唯一定义,每个Socket定义连接的一端。

    当两个进程想要通讯,他们必须使用TCP建立一个连接(在每个端建立状态信息)。当他们通讯结束后,相对应的连接会被中断或者关闭以释放资源让其他进程继续使用。

    因为连接可能建立在不可靠的计算机和不可靠的通讯网络上,所以基于时钟序号的握手机制可以避免错误地初始化连接。

    优先级和安全(Precedence and Security)

    TCP用户可以为连接设置相关的安全和优先级。当这些功能没有被显式设置的时候,会被设置一些默认值。

    设计的哲学(PHILOSOPHY)

    网络系统中的元素(Elements of the Internetwork System)

    互连网络包括通讯网络以及连接到这些网络的主机,通讯网络通过网关(ip层面上,网关一般就是我们连的路由器,链路层或者物理层是指具体的网关设备)相互连接。这里假设通讯网络有可能是本地网络(以太网)也有可能是大的广域网(阿帕网),这些通讯网络(跨网通讯,不管是跨越多个局域网,还是跨越多个地区的城域网)都是基于分组交换技术。位于通讯网络不同层次的通讯协议、网关、以及主机共同组成了可以保证进程相互通讯的系统,这个系统支持在两个进程端口间的逻辑链路上进行双向的数据传输。

    这里的packet一般指主机和网络之间一次交换的数据。我们一般不用关心数据包在网络间传输的格式。

    主机就是连接到网络的电脑,从通讯网络角度来看,主机就是数据包的来源地和目的地。进程就是主机中活跃的元素(进程通常的定义就是执行中的程序)。 即使是终端、文件、io设备也被认为是进程间的通讯方式。

    因为一个进程需要区分与其他多个进程之间的通讯,所以每个进程都可以申请多个端口。

    操作模型(Model of Operation)

    进程把需要发送的数据以参数的形式传给TCP的系统调用。TCP收到数据后,从参数中获取数据并打包成多个数据段,然后调用IP层提供的接口来把数据段发送到目的进程。负责接收的TCP把数据段传递到用户进程设置的缓存中,并通知用户进程。TCP数据段包括一些控制信息,从而保证可靠的有序的的数据传输。

    网络通讯模型就是,每个TCP都有关联的网络通讯模块,TCP为上层用户提供了一个可以连接本地网络的接口。这个网络通讯模块把TCP数据段打包成数据报文并把这些数据报文路由到目的网络模块或者离本地网络最近的网关。为了通过本地网络,数据段是被包装在本地网络报文(local network packet)中的。

    分组交换程序可能会进行打包、分段、或者其他的一些操作来保证可以通过本地分组(物理链路层的数据包)到达目的网络模块。

    位于不同本地网络之间的网关,负责将IP报文从本地网络报文中拆解出来,然后找到下一个IP报文需要通过的本地网络。将IP报文包装到下一个本地网络协议包中并路由到目的网络的相关网关(网关和网关之间也是一个本地网络连接),或者直达目的网络模块(比如计算机)。

    通过下一个本地网络时,网关有可能会把网络报文拆分成更小的网络报文(下一个本地网络MTU比较小)。在后面的传输过程中,有可能会继续拆分网络报文。网络报文段数据格式可以方便数据接收方(网络模块)把网络报文段重新组成一个网络报文。

    网络模块(接收方)把TCP数据段从IP报文中拆解出来发送到上层的TCP模块。

    上述操作模型看着非常简单,但是实际上整个过程是非常复杂的。一个重要的特性就是可以选择服务类型。服务类型可以指导网关或者网络模块在发送数据包到下一个网络的时候设置合适的参数。服务类型中就包括一个数据包优先级。报文也有可能会携带相关安全设置来允许主机或者网关在不同级别的安全环境中运行以便隔离数据包。

    主机环境(The Host Environment)

    TCP是操作系统的一个模块。用户使用TCP就像他们操作本地文件一样。TCP有可能会调用其他系统功能,比如操作数据结构。与网络相关的接口被相应的设备驱动(网卡)模块管理。TCP不直接调用网络设备驱动而是调用网络模块,网络模块会直接调用相应的设备驱动。

    前端处理机可以方便的实现TCP的相关功能和机制,TCP并不会阻碍这种实现方式。但是,在这种实现方式下,主机到前端处理机之前的通讯协议必须支持本文档规定的相关功能,并且把相应的功能接口提供给上层用户。

    接口(Interfaces)

    TCP/User接口可以为使用TCP的用户提供打开、关闭、获取连接状态的功能。这些系统调用与用户操作文件进行的系统调用是一样的,比如打开文件、读取数据、关闭文件。TCP/IP接口为主机的TCP模块提供与网络系统中任何其他地方的TCP模块进行数据收发的功能。这些系统调用可以传递地址、服务类型、优先级、安全或者其他控制信息。

    与其他协议的关系(Relation to Other Protocols)

    下面展示了TCP在协议栈中的位置:

           +------+ +-----+ +-----+       +-----+
           |Telnet| | FTP | |Voice|  ...  |     |  Application Level
           +------+ +-----+ +-----+       +-----+
                 |   |         |             |
                +-----+     +-----+       +-----+
                | TCP |     | RTP |  ...  |     |  Host Level
                +-----+     +-----+       +-----+
                   |           |             |
                +-------------------------------+
                |    Internet Protocol & ICMP   |  Gateway Level
                +-------------------------------+
                               |
                  +---------------------------+
                  |   Local Network Protocol  |    Network Level
                  +---------------------------+
    
                             Protocol Relationships
    
                                   Figure 2.
    

    TCP应该比较高效地支持上层协议。TCP为上层提供的接口应该与IP为TCP提供的接口一样简单易用。

    可靠通讯(Reliable Communication)

    在TCP连接上发送的数据流会可靠、按顺序地到达目的地。通过递增序列号和反馈机制保证传输的可靠性。概念上,每个字节都会赋予一个序列号。数据段传输的一段字节数据,第一个字节的编码就是这个数据段的编码。发送出去的数据段也会携带一个确认序列码,指示期望的下一个数据段的起始字节序列号。当TCP发送数据段的时候,会复制一份数据到重发队列中,并启动定时器。当收到对这个数据段的确认时,这个数据段会从重发队列中删除。如果数据段没有收到确认,并且定时器超时,TCP会重新发送对应的数据段。

    确认机制只能确保TCP模块收到了数据,并不能确保应用程序确实收到了数据。

    为了控制TCP模块之间的数据流量,流量控制机制被引入。接收方向发送方报告一个窗口(win)。这个窗口表示从接收方收到的最后一个字节开始,还能接收的字节数(接收方剩余的read buffer空间)。

    连接的建立与清除

    每个TCP端口都是独立选择的,所以端口号有可能会重复。为了保证TCP地址的唯一性,我们把IP地址与端口号连接在一起,IP:PORT,这两个可以保证全网唯一的连接。

    一个连接会被两端的socket唯一确定。一个本地的socket可能会参与多个连接,每个连接的另一端就是其他主机上的socket。一个连接可以在两个方向传递数据,这个被称为全双工。

    TCP可以为进程自由选择端口号。通常会为那些常用的socket(端口号)绑定常用的应用进程。我们可以假定某些进程拥有某些固定的端口号,这些进程只能绑定到那些它们拥有的端口上。(实现进程和端口的绑定规则由使用者自己规定,但是现在我们假定用户请求为一个进程绑定一个端口,或者可以把一组端口独立的赋予一个进程,比如把某些端口的高位为某些数值的一组端口赋予一个给定的进程。)

    OPEN方法中指定的本地端口和远端socket确定了一个连接。之后,用户会使用tcp提供的一个本地连接来进行后续的操作。我们假设有Transmission Control Block(TCB)数据结构来记录连接的相关信息。一个关于代码实现的建议就是把指向TCB的指针当作这个连接的名字。OPEN调用也可以指定连接是主动建立还是被动建立(client connect、server listen)。

    被动的OPEN调用表示进程想接收建立连接的请求而不是主动去建立一个连接。通常被动OPEN可以接收任何建立连接的请求(来自任何应用)。Foreign address都为零表示未指定socket,未指定socket只有在被动OPEN调用时能使用。如下图是在centos服务器上用netstat -nltp查看的。

    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
    tcp        0      0 0.0.0.0:5601            0.0.0.0:*               LISTEN      30577/node
    tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      21514/mysqld
    

    一个对外提供服务的进程如果要为其他未知的进程提供服务,就需要一个被动OPEN并且Foreign Address都指定为0的接口调用。之后外部进程就可以向本地进程请求建立连接。如果这个服务使用的端口号是比较知名的端口号,那么请求服务时会比较方便。

    一些标准服务都是提前绑定到了比较常见的一些端口上。比如,Telent服务被永久的绑定到了特定的端口上,其他的端口也为文件传输、远程job、文本生成器、回声、或者sink处理等服务预留(后面三个服务只为了测试使用)。有的端口会提供服务查询功能,并且会根据的查询信息在相应的端口上启动新的服务。预留端口是TCP协议规范的一部分,但是端口与服务的绑定关于超出了本文档的讨论范围。

    进程可以打开一个被动的连接,等待其他进程向本进程发起建立连接的请求,当新连接建立时,本进程会收到相关的通知。两个进程同时发起向对方建立的申请,连接依然会正常建立。在分布式计算情况下,每个计算机都是相互独立异步运行,这种灵活建立连接的方式很方便地为分布式计算提供支持。

    在本地等待被连接的服务和外部主动请求连接的OPEN请求,有两个规则可以确定本地服务是否会接收外部的连接请求。第一种情况下,本地服务准确指定了可以建立连接的外部地址。另一种情况是本地服务没有指定可以建立连接的外部地址,任何外部连接请求都会被处理。其他处理规则包括部分匹配限制。

    如果存在多个本地服务去请求绑定同一个端口,并且有的指定 了外部服务地址(有的没有指定),那么当一个主动建立连接的请求到来的时候,指定了外部服务地址的本地服务会优先得到匹配(如果匹配的话)。

    建立连接的过程中使用到了SYN控制标识符并且涉及到三个消息的交换。这个交换被称为三次握手。

    当用户通过OPEN指令发送一个连接请求的时候,请求的数据段会携带SYN到达服务器,这个时候新的连接会被初始化,同时一个等待处理的TCB数据结构也会被建立并保存。本地和外部地址的匹配程度确定何时去初始化一个连接。当交互的两端同步了各自的初始序列号,这个连接便会正确建立。

    关闭连接同样需要交换数据段,在这种情况下数据段中会包含FIN控制位。

    数据通讯(Data Communication)

    可以把数据看做是连接上的字节流。发送方通过SEND指令发送数据,如果发送的数据中指定了PUSH标志,数据会立即从发送方的缓存中发送出去。

    TCP模块可以缓存用户传过来的数据,并以一定的方式发送出去,但是如果上层用户指定了PUSH标志,TCP模块必须把缓存的数据发送出去。如果接收方收到带有PUSH标志的数据,那么它应该马上把数据传递到用户进程。

    push函数调用与发送数据段的大小无关。数据段中的数据有可能来自一个send调用,两个send调用或者多个send调用。

    push函数和PUSH标志是为了把数据尽快发送到接收方,这个操作没有相关记录的服务。

    push函数和介于TCP/USER之间的buffer有一定的关系。每次带有PUSH标志的数据放入用户读缓存的时候,这个buffer会立即提交到用户进程(无论buffer是不是满的),如果buffer满了,没有收到PUSH标志,那么buffer会直接提交到用户进程。

    当前进程正在处理数据流,可以在数据流之后的某个时刻为某个数据标记为紧急,TCP提供了这种与接收方进行交流的方式。TCP不规定当收到紧急数据时用户进程如何处理,但是标准流程是,当收到紧急数据时,用户进程应该尽快处理相关数据。

    优先级和安全(Precedence and Security)

    TCP基于IP模块提供的服务类型字段和安全选项为每个建立连接的用户提供优先级和安全方面的服务。并不是所有的TCP模块都需要在不同级别的安全环境(multilevel secure,IBM Docs )中运行,有些可能只在非安全情况下使用,或者有些只在一级隔离中运行。有些TCP实现和服务有可能只会在多级别安全的某些场景下使用。

    运行在多环境下的TCP模块,必须为每个发送出去的数据段添加安全、隔离区、优先级。TCP模块也必须为上层协议或者用户提供可以设置安全级别、隔离区、优先级的接口。

    健壮性规则(Robustness Principle)

    TCP遵循一般健壮性准则:对于要做的事情持保守态度,外部的东西要保持欢迎的态度。

    功能规格(FUNCTIONAL SPECIFICATION)

    头部格式(Header Format)

    TCP数据段被包装成IP数据报文发送。IP数据报文包括几个信息字段,包括源地址和目的地地址。TCP头紧随IP头之后,表示TCP报文需要的几个字段。这么划分IP和TCP数据头可以为以后主机协议(host level protocols)提供更好的扩展。

    TCP Header Format

        0                   1                   2                   3
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |          Source Port          |       Destination Port        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                        Sequence Number                        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                    Acknowledgment Number                      |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |  Data |           |U|A|P|R|S|F|                               |
       | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
       |       |           |G|K|H|T|N|N|                               |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |           Checksum            |         Urgent Pointer        |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                    Options                    |    Padding    |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       |                             data                              |
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
                                TCP Header Format
    
              Note that one tick mark represents one bit position.
    
                                   Figure 3.
    

    源端口: 16 bits

     源端口号

    目的端口:16 bits

     目的端口号

    序列号:32 bits

     如果没有SYN标志,数据段的序列号就是第一个字节的序列号。如果有SYN标志,序列号就是初始序列号,第一个要发送的字节序列号是初始序列号+1。

    确认序列号:32 bits

      当ACK标志被设置,确认序列号表示接收方期望收到的下一个数据段开头的字节序号。连接建立后,该序列号一直会被传送。

    数据偏移地址:4 bits

      表示数据数据体开始的位置。TCP的头部长度是32位的整数倍。

    预留:6 bits

      为以后功能扩展预留,必须为0

    控制位:6 bits (自左到右):

    1. URG: 紧急标识位
    2. ACK: 确认标识位
    3. PSH: 推送标志位
    4. RST: 复位连接标识位
    5. SYN: 同步标志位
    6. FIN: 结束标志位

    窗口:16 bits

      接收方发送的确认字段中会携带一个数字,这个数字表示接收方最后一次成功确认之后还能接收的字节数。

    校验和:16 bits

      伪首部、TCP报头、TCP数据按照16位分组,对这16位分组分别进行反码求和运算(二进制反码求和是怎样求的?_百度知道IP和TCP头校验和计算算法详解_jiangqin115的专栏-CSDN博客 关于二进制反码求和运算需要说明的一点是,先取反后相加与先相加后取反,得到的结果是一样的),最后把得到的数据进行反码运算获得的16数据就是校验和。 反码求和运算:

    1. 把校验和字段置为0;
    2. 对IP头部中的每16bit进行二进制求和;
    3. 如果和的高16bit不为0,则将和的高16bit和低16bit反复相加,直到和的高16bit为0,从而获得一个16bit的值;
    4. 将该16bit的值取反,存入校验和字段。
    5. 反码运算时,其符号位与数值一起参加运算。

    如果最后字节数是奇数,为了获得校验码,需要在最后添加一个字节,这个字节的每位都是0。添加的字节不会被发送(只是为了计算校验和)。计算校验和的时候会在TCP头部前面添加96位的伪头部。伪头部包括源地址(ipv4)、目的地址(ipv4)、协议、TCP数据长度。这样TCP可以防止数据包错误路由。这些信息(地址信息)会通过TCP/IP接口从IP模块获取到。

    
                         +--------+--------+--------+--------+
                         |           Source Address          |
                         +--------+--------+--------+--------+
                         |         Destination Address       |
                         +--------+--------+--------+--------+
                         |  zero  |  PTCL  |    TCP Length   |
                         +--------+--------+--------+--------+
    

    TCP长度包括TCP头部和数据长度,不包括添加的12个字节伪头部。

    紧急指针:16 bits

     这个值与数据段的第一个字节的序列号相加就是紧急数据的起始位置。这个字段只有在紧急标志位设置了才会被处理。

    选项:长度可变

     选项出现在TCP尾部,并且是长度是8位的倍数。所有的选项也会被计算在校验和内。选项可以从任意自己开始。选项有两种格式:

    1. 单字节,表示选项类型
    2. 单字节选项类型、一个字节表示选项长度、实际的选项数据

    选项长度包括选项类型、选项具体的数据值。

    选项列表的数据长度可能要比TCP头部指定的数据的起始位置要少。在End-of-Option与实际数据之间使用值为0的字节填充。

    目前定义的选项包括:

          Kind     Length    Meaning
          ----     ------    -------
           0         -       选项列表结束标志
           1         -       误操作
           2         4       最大段长度
    

    具体定义:

    • 选项列表结束标志位:
            +--------+
            |00000000|
            +--------+
             Kind=0
    

    表示选项列表的结束,这个可能与TCP头部的数据起始位置(TCP头部结束位置)不相符。这个表示整个选项列表的结束,不是每个选项的结束。

    • 无操作:
            +--------+
            |00000001|
            +--------+
             Kind=1
    

    这个选项一般用在其他选项之间,比如用来将某些选项对齐到某个字上。发送方可能不会使用这种选项,接收方应该时刻准备接收没有对齐到字的选项。

    • 最大段长度:16位
            +--------+--------+---------+--------+
            |00000010|00000100|   max seg size   |
            +--------+--------+---------+--------+
             Kind=2   Length=4
    

    这个选项表示接收方可以接收的最大数据段,这个选项一般会在连接建立时刻指定(SYN)。如果没有指定这个字段的值,则表示数据段大小不受限制。

    • 填充选项:可变长度 填充选项用来保证TCP头部结束和数据开始部分对齐与32位数据边界。

    术语(Terminology)

    在我们详细讨论TCP操作的具体细节前,需要对一些重要的关键词做一些解释。需要几个变量来维护TCP连接的状态,把保存这种变量的数据结构叫做TCP控制块或者TCB。这些变量包括:本地或者对端端口号,连接的安全和优先级,指向用户发送或者接收数据buffer缓存的指针,指向重发队列和当前发送数据段的指针。关于发送和接收的序列号的信息也被存储在TCB中。

     发送序列变量

          SND.UNA - 发送但未确认的
          SND.NXT - 下次发送的字节序列号
          SND.WND - 发送窗口
          SND.UP  - 紧急数据指针
          SND.WL1 - 上次更新窗口使用的字节序列号
          SND.WL2 - 上次更新窗口使用的确认序列号
          ISS     - 初始序列号
    

     接收序列变量

          RCV.NXT - 下次希望接收的字节序列号
          RCV.WND - 接收窗口大小
          RCV.UP  - 紧急数据指针
          IRS     - 连接建立时初始序列号
    

    下图有助于理解这几个变量:

      发送序列号空间
    
                       1         2          3          4
                  ----------|----------|----------|----------
                         SND.UNA    SND.NXT    SND.UNA
                                              +SND.WND
    
            1 - 已经被确认的字节序列
            2 - 未确认的字节序列
            3 - 新数据发送可以使用的字节序列
            4 - 还没有分配的,未来会用的字节序列号
    
                              Send Sequence Space
    
                                   Figure 4.
    
    

    图4中的3表示发送窗口(发送窗口可以控制应用向TCP写数据的速度)大小。

      接收序列号空间
    
                           1          2          3
                       ----------|----------|----------
                              RCV.NXT    RCV.NXT
                                        +RCV.WND
    
            1 - 被确认的序列号
            2 - 打算接收的序列号
            3 - 还未被分配的序列号
    
                             Receive Sequence Space
    
                                   Figure 5.
    

    图5中的2表示接收窗口。

    本节讨论的常用变量的数据都是从当前数据段中的相关字段中获取的。

        Current Segment Variables
    
          SEG.SEQ - 数据段序列号
          SEG.ACK - 数据段确认序列号
          SEG.LEN - 数据段长度
          SEG.WND - 发送方窗口大小
          SEG.UP  - 数据段紧急数据指针
          SEG.PRC - 数据段优先级值
    

    一个连接在一个生命周期会经历很多状态。这些状态包括: LISTEN, SYN-SENT, SYN-RECEIVED,ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK,TIME-WAIT和不存在的状态:CLOSED。CLOSED是不存在的是因为没有相关的TCB数据块,因此也没有连接。这些状态更详细的解释:

    1. LISTEN-代表接收来自任何其他TCP组件的连接请求。
    2. SYN-SENT-发送完SYN标志位之后,等待对方发送ACK标志。
    3. SYN-RECEIVED 等待接收之前发送的syn的ack。
    4. ESTABLISHED 连接已经建立,用户进程可以收发数据。
    5. FIN-WAIT-1 等待之前发送fin的ack或者连接对端的fin标志。
    6. FIN-WAIT-2 等待对端的fin标识数据。
    7. CLOSE-WAIT 等待本地用户进程发送关闭指令(一般是半关闭,比如java socket的shutdownOutput)。
    8. LAST-ACK 等待之前接收发送的fin的ack。
    9. TIME-WAIT 对端收到本地发送回去fin ack的最长时间。
    10. CLOSED标识连接已经拆除。

    事件驱动TCP连接状态的变化。事件包括:用户进程调用的方法,OPEN, SEND, RECEIVE, CLOSE,ABORT, and STATUS,收到的数据段、特别是包含了 SYN, ACK, RST and FIN 这些标志的数据段,还有超时等。

    下图6描述了TCP的状态变化,同时也标注了导致状态变化的事件、TCP的相关行为,但是没有涉及一些异常条件和一些没有导致TCP状态变化的动作。在后续章节会详细描述TCP对于事件处理细节。

    注意:这张图只是一个大概总结,并不是TCP规范的全部。

                                  +---------+ ---------\      active OPEN
                                  |  CLOSED |            \    -----------
                                  +---------+<---------\   \   create TCB
                                    |     ^              \   \  snd SYN
                       passive OPEN |     |   CLOSE        \   \
                       ------------ |     | ----------       \   \
                        create TCB  |     | delete TCB         \   \
                                    V     |                      \   \
                                  +---------+            CLOSE    |    \
                                  |  LISTEN |          ---------- |     |
                                  +---------+          delete TCB |     |
                       rcv SYN      |     |     SEND              |     |
                      -----------   |     |    -------            |     V
     +---------+      snd SYN,ACK  /       \   snd SYN          +---------+
     |         |<-----------------           ------------------>|         |
     |   SYN   |                    rcv SYN                     |   SYN   |
     |   RCVD  |<-----------------------------------------------|   SENT  |
     |         |                    snd ACK                     |         |
     |         |------------------           -------------------|         |
     +---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+
       |           --------------   |     |   -----------
       |                  x         |     |     snd ACK
       |                            V     V
       |  CLOSE                   +---------+
       | -------                  |  ESTAB  |
       | snd FIN                  +---------+
       |                   CLOSE    |     |    rcv FIN
       V                  -------   |     |    -------
     +---------+          snd FIN  /       \   snd ACK          +---------+
     |  FIN    |<-----------------           ------------------>|  CLOSE  |
     | WAIT-1  |------------------                              |   WAIT  |
     +---------+          rcv FIN  \                            +---------+
       | rcv ACK of FIN   -------   |                            CLOSE  |
       | --------------   snd ACK   |                           ------- |
       V        x                   V                           snd FIN V
     +---------+                  +---------+                   +---------+
     |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
     +---------+                  +---------+                   +---------+
       |                rcv ACK of FIN |                 rcv ACK of FIN |
       |  rcv FIN       -------------- |    Timeout=2MSL -------------- |
       |  -------              x       V    ------------        x       V
        \ snd ACK                 +---------+delete TCB         +---------+
         ------------------------>|TIME WAIT|------------------>| CLOSED  |
                                  +---------+                   +---------+
    
                          TCP Connection State Diagram
                                   Figure 6.
    

    序列号(Sequence Numbers)

    TCP连接发送的每个字节都有一个序列号,这是设计中比较基础的概念。因为每个字节都编号了,所以可以对每个字节进行确认。确认机制具有累计的效果,比如X编号表示X之前的字节都收到了(不包括X)。这种机制可以实现重发情况下的数据去重。数据段中紧随header之后的字节编号最小,之后的字节序号累加。

    必须要记住,序列空间是有限的,但是非常大。序列空间从0 to 2^32 - 1。因为序列号空间是有限的,所以所有处理序列号的算法都应该进行对232的取模运算。无符号的算法保留了序列号的关系,因为序列号总是从232到0来回循环往复。计算模运算的时候会有一些细微差距,所以在比较这些序列号时必须格外小心。"=<"表示小于等于(与2^32进行模运算)。

    TCP必须包括的序列号比较有:

    • 确定那些对发送出去的数据进行确认的序列号。
    • 确定一个数据段包含的所有序列号都已经被确认(从重发队列里删除数据段)。
    • 确定接收的数据段包含接收方期望的序列号。(比如数据段与接收窗口重叠)

    TCP发出去数据后会收到对应的确认。以下几个比较运算被用来处理确认。

    SND.UNA = 最久没有确认的数据序列号 SND.NXT = 下一个发送数据的序列号 SEG.ACK = 连接对端期望接收到的下一个序列号 SEG.SEQ = 接收数据段的第一个字节的序列号 SEG.LEN = 数据段中的数据占用的字节数,包括SYN和FIN两个标志位占用的。 SEG.SEQ+SEG.LEN-1 =接收的某个数据段的最后一个字节。

    一个新的确认满足以下不等式:

    SND.UNA < SEG.ACK =< SND.NXT

    处于重发队列中的数据段,如果它的序列号和长度小于或者等于接收的数据段的确认序列号,那么这个数据段整个的被确认了。

    当数据收到时,以下几个比较运算是必要的。

    RCV.NXT =期望接收的下一个数据段的序列号,是接收窗口的左边沿或者窗口最左边。 RCV.NXT+RCV.WND-1 = 接收方期望接收的数据段的最后一个序列号,或者是接收窗口的最大序列号或者右边延。 SEG.SEQ = 数据段的第一个字节的序列号 SEG.SEQ+SEG.LEN-1 = 数据段最后一个字节的序列号

    一个数据段占用了正常的接收序列号空间,如果满足:

    RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND

    或者满足

    RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND

    第一个检查是判断数据段开始字节的序列号是否落入接收窗口中,第二个检查是判断数据段最后一个字节是否也落入接收窗口中。只要通过一个检查,就被认为数据段有数据可以放入接收窗口中。

    实际要比这里描述的复杂得多。由于0大小的窗口和0长度的数据段,对于一个数据段,我们有四种接收的可能。

        Segment Receive  Test
        Length  Window
        ------- -------  -------------------------------------------
    
           0       0     SEG.SEQ = RCV.NXT
    
           0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
    
          >0       0     not acceptable
    
          >0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
                      or RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND
    

    需要注意的是,当接收窗口大小为0时,除了ack数据段以外其他数据段都不会被接收。所以,TCP可以在窗口为0的情况下继续发送数据和接收确认细腻。然而,即使接收窗口为0,TCP也必须接收带有RST和URG标识的数据段。

    我们使用编号方法同样也保护了某些控制信息。隐式地为某些控制信息编号,可以对这些控制信息进行重发和确认防止出现混乱(可以防止对一个控制信息进行多次处理)。控制信息不会占用数据段的数据部分(空间)。所以,我们必须有规则地为控制信息进行隐式编码。SYN和FIN是需要隐式编号的两个控制信息,并且这两个控制信息只用在连接建立和拆除过程。为了编码目的,SYN的编号是在它所处的数据段中第一个字节数据的编号之前,FIN的编号是在它所处的数据段中最后一个字节数据的编号之后。SEG.LEN包括数据字节编号和控制信息编号。当一个数据段有SYN标志时,SEG.SEQ就是SYN的编号。

    初始编号的选择 一个TCP连接可以重复使用,规范没有任何限制。一个连接被一对socket确定。连接的新实例被称为连接实体。那么有问题就是,连接如何确定从上一个连接实体发送过来的重复数据段。当连接关闭并且被快速的重新打开时或者由于系统内存不足造成的连接断开然后重新建立时这种问题更为明显。

    为了避免冲突,当之前连接实体发送的数据段还在网络上传输时,新的连接实体应该避免使用相同的序列号。即使TCP宕机或者丢失了所有关于之前使用的序列号的信息,我们也要保证不出现冲突。当一个新的连接实体建立时,一个初始序列号(ISN)选择器会用来选择一个新的32的初始编号。选择器使用一个32位的时钟(有可能是虚拟的),这个时钟的低位会以4微妙涨一个的速度增长。所以ISN会大概4.5个小时循环一次。因为我们假定一个数据段在网络中最多存留一个MSL(最大段生命周期)时间段,并且MSG比4.5个小时短很多,所以我们判断,ISN是唯一的。

    每一个连接都有一个接收序列号和发送序列号。发送初始序列号由发送数据的TCP模块选择,接收序列号是在连接建立时从对面传过来的。

    连接建立时,两端必须要同步对方的初始序列号。双方通过同步带有SYN标志和初始序列号的数据段来实现。所以为了实现上述目的,一般需要一个初始化序列号选择器并且需要一些握手机制来同步初始化序列号。

    同步机制需要发送自己的初始化序列号给对方,并且接收对方发送过来的确认信息。连接的两端必须接收对方发送过来的初始序列号并且发回确认信息。

        1) A --> B  SYN my sequence number is X
        2) A <-- B  ACK your sequence number is X
        3) A <-- B  SYN my sequence number is Y
        4) A --> B  ACK your sequence number is Y
    

    因为第二步和第三步可以合并成一步,所以一般称为三次握手。

    三次握手步骤是必须的,因为序列号不是在网络空间中全局同步的,并且每个TCP模块都有自己不同的初始化序列号选择机制。接收方收到第一个SYN的时候没有办法确定这个SYN是否是之前连接实例发送的老旧的SYN数据包除非接收方一直保存之前连接使用的最后一个序列号(不是一直都可以记录的。),所以一般接收方需要发送方来检验这个SYN(通过向发送方返回ACK信息)。三次握手和时钟驱动的序列号选择器在【Dalal, Y. and C. Sunshine, "Connection Management in Transport Protocols", Computer Networks, Vol. 2, No. 6, pp. 454-473,December 1978.】中有详细讨论。

    知道什么时候保持静默 TCP在丢失以前连接序列号的情况下(宕机后重启)必须保持一个MSL时间段,并且在这个时间段内不创建新的包含序列号的数据段,因为这个序列号可能会与网络上传输的老旧数据段的序列号重复。本规范中MSL采用2分钟。这是一个工程上的选择,根据各方面的经验,可以修改该值。如果TCP重新初始化,并且保留了之前的字节序号状态,这时不需要保持静默,但是必须保证后续的字节序号要比之前的大。(注:如果不存在宕机或者重启等情况,TCP建立新连接是不需要保持2分钟的静默时间的,因为TCP模块会记录之前连接使用的序列号。)

    TCP静默时间概念 如果在一个主机组成的计算机网络系统中主机突然宕机了并且没有保存所有处于活跃状态(Closed以外的其他状态)的连接的相关状态信息的情况下,主机重启后需要等待约定的一段时间(MSL)才能开始重新建立连接。以下段落会继续介绍该概念。在网络系统中,可以冒着上个连接的数据被新连接接收或者新数据被当做重复数据丢掉的风险不去等待一段时间。

    数据包每次从产生到发送到网络中时都会消耗一定的序号空间。TCP中的重复判断和序号计算算法都依赖于数据段唯一绑定到序列号空间中,并且在网络传输的数据段被确认之前以及这些数据段的副本在从网络中清除掉之前,后续发送的数据序列号不能循环超过2^32。如果没有这个规定,可以想象两个不同的数据段有可能会被赋予相同的序列号或者交叉的序列号,导致接收方没办法区分哪个是新的数据包,哪个是老的数据包。数据段包含的字节越多,该数据段占用的序列号空间就越多。

    正常情况下,TCP跟踪发送下一个字节的序号,并且保存需要确认的最早的那个序号,这样可以防止TCP错误使用之前被使用过并且得到确认的序列号再次被使用。这种方式不能保证旧的重复数据从网络上剔除,所以序列空间被设置非常大,来减少因为网络中传输的老重复数据段导致数据传输的问题。在每秒2兆位的网速中,大概需要4.5个小时消耗掉2^32 的序号空间。数据段在网络中的最大存在周期一般不会超过几十秒,这被认为对现有的网络有足够的保护空间,即使网速到了10兆位/s。在100兆位/s的网速中用掉2^32的序号空间需要5.4分钟,虽然比较少,但是依然够用。

    在TCP连接的一端,如果TCP模块没有记录上次使用的序列号,那么基本的重复判断和序号算法将会失效。比如当一个TCP模块建立了几个连接,每个连接的初始序号为0,经过宕机然后重启之后,连接有可能会重新创建之前的连接实例(这个时候的连接有可能处于半打开状态),这个链接实例发送的数据序列号有可能与之前连接实例发送的并且在网络中传输中的数据序列号相等或者有重叠。在不了解一个指定的连接之前的序号的情况下,TCP规范规定TCP发送模块应该等待一个MSL时间,来让之前连接实例发送的数据段从网络上排空。

    即使主机能记住时间并且可以根据时间选择初始序列号,依然不能免于上述问题。

    (注:前面几句话和后面没联系,故删除)现在假设突然宕机了,然后恢复,然后建立了该连接的另个一新的实例,实例的初始化序号是S1=ISN(t)(上一个连接实例使用的最后一个字节序列)。如果恢复得非常快,上一个连接实例发送的在S1周围的数据段(序号大于S1),会被接收方认为是新连接实例的数据。

    现在问题是重启后的主机不知道宕机了多久,也不知道网络上是否还有上一个连接实例发送的老数据包。

    解决这种问题的办法,就是在从宕机中恢复过来后,在发送数据前静默一段时间。如果主机不担心两个连接实例发送的新老数据包可能造成数据问题的话,可以在发送数据前不用等待一段时间。TCP实现应该为用户提供在宕机重启后是否选择等待这一选项,或者为用户提示性的为所有连接实现静默等待。然而在启动后至少保持MSL这些时间后,再提示用户选择是否等待显得不是那没有必要。

    最后总结:每一个发送出去的数据段占用一个或者多个序列号,一个数据段发送之后,数据段的序列号处于使用或者繁忙状态中直到MSL时间过去,当遇到宕机时,连接发送的最后一段数据段会占用一段序列号空间,如果很快地建立了一个新的连接实例并且使用了上一个连接实例使用的数据序列的话,那么将会在接收端造成数据错乱的问题。

    建立一个连接(Establishing a connection)

    三次握手是建立连接的一个过程。这个过程一般是由一端发起,然后由另一端响应。当两端同时发起连接请求时,三次握手也同样适用。当同时发起连接建立请求时,发送方发出去SYN数据段之后,并没有收到对方对SYN的确认,而是收到对端同时发送过来的SYN数据段。对于接收方来说,有可能会在处于同时建立连接状态时又收到一个老旧的SYN标志数据段。使用reset标志段可以解决这个问题。

    以下有几个建立连接的实例。尽管这几个实例没有使用带有用户数据的数据段来做连接同步操作,TCP也需要确保在能保证接收缓冲的数据都是有效的时候才会把数据传递到上层应用(只有把数据缓冲到连接建立阶段,数据才是有效的)。三次握手可以减少假连接的发生概率。这是协议实现在内存和消息检验之间做的权衡。

    最简单的三次握手过程如下图。图的具体解释如下。每行都有序号为了方便引用描述。右箭头表示TCP数据段从A发送到B,左箭头则相反。省略号(...)表示数据段依然在网络中传输。"XXX"表示数据段丢失或者被拒绝接收。TCP状态表示离开或者接收到某些数据段(中间横线表示数据段的内容)之后触发的。数据段同时带有序列号、控制字段、ACK字段。为了明确起见,其余的字段比如窗口大小、地址、长度、文本什么的没有描述出来。

          TCP A                                                TCP B
    
      1.  CLOSED                                               LISTEN
    
      2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               --> SYN-RECEIVED
    
      3.  ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED
    
      4.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK>       --> ESTABLISHED
    
      5.  ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED
    
              Basic 3-Way Handshake for Connection Synchronization
    
                                    Figure 7.
    

    第二行表示A使用序号100发送数据。第三行B发送了一个SYN标志,同时对A发送的SYN做了一个反馈。B对A的反馈中期望下一个接收的序列号是101,这个SYN占用了100这个序列号。第四行A使用空的数据段对B的SYN做了一个反馈。第五行,A发送了一些数据。注意第五行序列号与第四行序列号是一样的,因为ACK数据段不占用序号空间(如果ACK占用序号空间,那我们也要对ACK标志位做ACK,这样会一直处于往返确认的循环中)。

    同时建立稍微有些复杂,如下图。每个TCP的状态从CLOSED 变化到 SYN-SENT 再到 SYN-RECEIVED 再到ESTABLISHED.

          TCP A                                            TCP B
    
      1.  CLOSED                                           CLOSED
    
      2.  SYN-SENT     --> <SEQ=100><CTL=SYN>              ...
    
      3.  SYN-RECEIVED <-- <SEQ=300><CTL=SYN>              <-- SYN-SENT
    
      4.               ... <SEQ=100><CTL=SYN>              --> SYN-RECEIVED
    
      5.  SYN-RECEIVED --> <SEQ=100><ACK=301><CTL=SYN,ACK> ...
    
      6.  ESTABLISHED  <-- <SEQ=300><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED
    
      7.               ... <SEQ=101><ACK=301><CTL=ACK>     --> ESTABLISHED
    
                    Simultaneous Connection Synchronization
    
                                   Figure 8.
    
    

    使用三次握手的主要原因是可以防止上一个连接实例发送的老旧的连接建立标志会对现在进行的连接初始化造成影响。为了防止这种老旧包对当前连接建立造成影响,引入了reset控制包。如果接收TCP(发送方处理逻辑请看图9)处于一个非同步的状态(比如SYN-SENT, SYN-RECEIVED)时接收到了一个reset控制包,他会返回到LISTEN状态。如果TCP处于已经同步状态(ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT)他会拆除当前连接,并且通知上层连接用户(java中的connection reset by peer异常)。我们接下来讨论当连接处于半打开情况下,出现这种异常的场景。

          TCP A                                                TCP B
    
      1.  CLOSED                                               LISTEN
    
      2.  SYN-SENT    --> <SEQ=100><CTL=SYN>               ...
    
      3.  (duplicate) ... <SEQ=90><CTL=SYN>               --> SYN-RECEIVED
    
      4.  SYN-SENT    <-- <SEQ=300><ACK=91><CTL=SYN,ACK>  <-- SYN-RECEIVED
    
      5.  SYN-SENT    --> <SEQ=91><CTL=RST>               --> LISTEN
    
    
      6.              ... <SEQ=100><CTL=SYN>               --> SYN-RECEIVED
    
      7.  SYN-SENT    <-- <SEQ=400><ACK=101><CTL=SYN,ACK>  <-- SYN-RECEIVED
    
      8.  ESTABLISHED --> <SEQ=101><ACK=401><CTL=ACK>      --> ESTABLISHED
    
                        Recovery from Old Duplicate SYN
    
                                   Figure 9.
    

    图9简单描述了从重复老旧包中恢复的过程。第三行,之前连接实例的老包到达了B。B没法确认这是新包还是老包,所以它正常的对这个包进行了回馈。A收到回馈包后发现进行确认的序列号不对,所以他向B发送了一个reset控制包,这个控制包还是使用了91这个老包的序列号。B收到reset控制包之后重新进入LISTEN状态。当新的SYN(100)包到达B时,连接正常建立。如果第六行的SYN包早于RST到达B的话,情况会有些复杂。

    半打开连接和其他异常状态状态的连接 连接的一端已经关闭连接并且没有通知对方或者由于突然宕机导致连接的一端关于TCP状态的记录都丢失,这种连接被称为半打开连接。如果它试图向对方发送数据,这种连接会被重置。

    如果连接在A端的状态已经不存在,那么在B端的应用试图给A端发送数据的时候会收到A端发送过来的RESET控制消息。这个消息通知B,该连接出了某些问题,可以拆除该链接。

    假设A和B正在通过TCP通讯,之后由于A宕机,导致A关于连接的状态都丢失了。有些系统有故障恢复机制,这个取决于A使用的TCP模块所在的操作系统实现。当A主机重启后,A仿佛重新开始,或者从某个恢复点恢复过来。所以A试图重新建立连接,或者在他认为已经建立的连接上发送数据。在第二种情况下,他会收到TCP模块的连接已经关闭的通知。第一种情况下,A试图发送SYN数据包。这种情况会在图十中描述。

          TCP A                                           TCP B
    
      1.  (CRASH)                               (send 300,receive 100)
    
      2.  CLOSED                                           ESTABLISHED
    
      3.  SYN-SENT --> <SEQ=400><CTL=SYN>              --> (??)
    
      4.  (!!)     <-- <SEQ=300><ACK=100><CTL=ACK>     <-- ESTABLISHED
    
      5.  SYN-SENT --> <SEQ=100><CTL=RST>              --> (Abort!!)
    
      6.  SYN-SENT                                         CLOSED
    
      7.  SYN-SENT --> <SEQ=400><CTL=SYN>              -->
    
                         Half-Open Connection Discovery
    
                                   Figure 10.
    

    当第三行的SYN到达B时,B处于同步状态,并且SYN携带的序列号超出了B的窗口范围,所以B返回了100,通知A希望下次发送100的序号。A收到100的反馈后发现,并不是自己曾经发送过的序列号,数据不同步,所以发送reset控制到到B。B在第五行关掉连接。A还是尝试与B建立连接。这又回到了图7描述的三次握手过程上。

    当A宕机并且B还认为连接是同步的并且打算发送数据时,情况会比较有意思。这在图11有描述。在这种情况下A不会接收B发送过来的数据,因为A已经不存在之前的连接了,所以A发送了一个RST控制包。B收到RST控制包后重置了连接。

            TCP A                                              TCP B
    
      1.  (CRASH)                                   (send 300,receive 100)
    
      2.  (??)    <-- <SEQ=300><ACK=100><DATA=10><CTL=ACK> <-- ESTABLISHED
    
      3.          --> <SEQ=100><CTL=RST>                   --> (ABORT!!)
    
               Active Side Causes Half-Open Connection Discovery
    
                                   Figure 11.
    

    图12描述了A和B都处于被动打开状态(LISTEN)。上一个连接实例的SYN数据包到达了B,B进行了状态转换。B对A发送了一个对老包的确认。A收到确认后,向B发送了一个RST控制包,B重新进入监听状态。

          TCP A                                         TCP B
    
      1.  LISTEN                                        LISTEN
    
      2.       ... <SEQ=Z><CTL=SYN>                -->  SYN-RECEIVED
    
      3.  (??) <-- <SEQ=X><ACK=Z+1><CTL=SYN,ACK>   <--  SYN-RECEIVED
    
      4.       --> <SEQ=Z+1><CTL=RST>              -->  (return to LISTEN!)
    
      5.  LISTEN                                        LISTEN
    
           Old Duplicate SYN Initiates a Reset on two Passive Sockets
    
                                   Figure 12.
    
    

    还有很多其他情况也可能发生,RST的产生和处理情况准守一下规则:

    重置的产生(Reset Generation)

    通常情况下如果收到一个不属于本连接实例的数据段的话都要产生并发送重置控制包。如果不确定这个包是不是这个连接的,不能发送重置控制包。

    有三种状态:

    1. 当连接不存在时除了reset控制包之外的其他数据包发送过来时,都应该向对端发送一个重置控制包。特别的,如果SYN试图与一个不存在的TCP模块建立连接,也应该通过发送重置控制包通知对方。如果接收到的数据段有ACK标志,重置数据包使用ACK的序列号,否则重置数据包使用序列号0,并且ACK字段设置为0与数据段长度的和。最后连接还是保持关闭状态。
    2. 如果连接处于非同步状态(LISTEN,SYN-SENT,SYN-RECEIVED)收到了未曾发送过的数据段的确认信息或者收到的数据段的安全等级、隔离级别与当前连接要求的不符合,接收端将会向发送端发送一个重置消息。如果我们发送的SYN还没有得到确认(同时打开的情况下)并且收到的数据段的优先级比本地的优先级高,那我们要么升级(如果用户或者系统允许的话,这里TCP应该会提供相应的控制接口)本地的优先级要么发送reset控制包到对端;或者如果接收的数据包的优先级比本地端要求的低,我们会继续处理(如果发送方不能把自己的优先级提升到与我们本地一样的优先级的话,连接会被关闭,这个是通过后续的数据段来检测到的)。如果我们的SYN已经被确认了,那么接收到的数据包的优先级必须与我们本地的优先级一样,否则会向对端发送reset控制包。如果接收到的数据段有ACK标志,重置数据包使用ACK的序列号,否则重置数据包使用序列号0,并且ACK字段设置为0与数据段长度的和。最后连接还是保持关闭状态。
    3. 如果连接已经处于同步状态(ESTABLISHED, FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT)并且收到不可接收的数据段(超过接收窗口大小或者不可接收的确认---没有发送过的数据包的确认,通过SND.UNA与SND.NXT确定)的时候,需要发送一个带有SND.NXT序列号的空数据包与一个期望下次收到的序列号的确认包,TCP保持现有状态。如果数据包包括安全级别、隔离、优先级,但是不满足本地连接对安全级别、隔离、优先级的要求,本地会关闭连接并进入到关闭状态,并发送RESET控制包,RESET控制包使用发送过来的确认字段中的序列号。

    RESET处理(Reset Processing) 除SYN-SENT以外的其他状态,reset包都是通过SEQ字段来检查是否是正常的。如果reset包的序列号在窗口内,那么它就是合法的。在SYN-SENT状态中,如果接收到了对本地之前发送的SYN的确认的话,收到的reset包就是正常的。

    接收方收到reset包之后先检查是不是正常的(在接收窗口内),如果是正常的然后改变状态。如果连接处于LISTEN状态,忽略这个reset包。如果连接处于SYN-RECEIVED状态并且之前是处于LISTEN状态的话,连接重新进入LISTEN状态,否则的话连接关闭连接并且进入CLOSED状态。如果连接处于其他状态,直接关闭连接并且通知上层用户进程。

    关闭连接

    CLOSE操作意味着我没有数据需要发送了。对于全双工的连接来说,CLOSE操作可能显得比较模糊,并且没有对对端做任何的说明。执行CLOSE操作的一端仍然可以继续读取数据,除非对端也停止发送数据。所以一个程序可以发送多个send指令最后跟着一个close,然后等待对面发送数据过来直到对面停止发送数据。当对面没有数据发送的时候,我们可以通知用户,让用户可以优雅的关闭连接。TCP在连接关闭前会可靠地把数据分发出去,如果一个用户不需要新数据了,那么他可以监听连接直到收到对面关闭连接的通知(-1),这样可以确保自己发送的数据对面都已经接收到了。用户发送完数据并且关闭连接后,需要一直读取连接传递上来的数据直到收到连接关闭的通知(-1)。

    有三个关闭场景:

    1. 本地用户进程主动关闭连接

    把位于等待发送队列中最后一个的数据段打上FIN标志。本地TCP不再接收用户发送的数据,并且本地连接进入FIN-WAIT-1状态。这个状态中的连接还可以继续接收数据。发送出去的数据包括FIN会一直重试发送直到收到确认。当连接对端收到所有数据并且为刚才发送的FIN返回了一个确认,并且也发送一个FIN,本地连接会确认对端发送的FIN。注意:收到对面的FIN指令后会对这个FIN做ACK,但是本地的FIN需要等待本地用户调用CLOSE方法来触发发送。

    1. 远端用户进程发送FIN指令关闭连接

    收到FIN数据后,TCP会通知本地用户连接对端停止发送数据。用户会调用CLOSE方法来关闭本地连接,TCP把本地数据都发送到对端之后,发送FIN数据到对端。等收到FIN的确认信息后,本地连接被关闭并删除。如果发送出去的FIN很久没有得到确认,超过用户设置的一段时间后,连接被关闭。

    1. 两端用户进程同时关闭连接

    连接两段同时关闭连接,两段会同时发送FIN控制包。当FIN控制包之前的数据都被处理和确认后,TCP可以对FIN控制包进行确认。两段收到ACK后,关闭连接。

          TCP A                                                TCP B
    
      1.  ESTABLISHED                                          ESTABLISHED
    
      2.  (Close)
          FIN-WAIT-1  --> <SEQ=100><ACK=300><CTL=FIN,ACK>  --> CLOSE-WAIT
    
      3.  FIN-WAIT-2  <-- <SEQ=300><ACK=101><CTL=ACK>      <-- CLOSE-WAIT
    
      4.                                                       (Close)
          TIME-WAIT   <-- <SEQ=300><ACK=101><CTL=FIN,ACK>  <-- LAST-ACK
    
      5.  TIME-WAIT   --> <SEQ=101><ACK=301><CTL=ACK>      --> CLOSED
    
      6.  (2 MSL)
          CLOSED
    
                             Normal Close Sequence
    
                                   Figure 13.
    
    
    
          TCP A                                                TCP B
    
      1.  ESTABLISHED                                          ESTABLISHED
    
      2.  (Close)                                              (Close)
          FIN-WAIT-1  --> <SEQ=100><ACK=300><CTL=FIN,ACK>  ... FIN-WAIT-1
                      <-- <SEQ=300><ACK=100><CTL=FIN,ACK>  <--
                      ... <SEQ=100><ACK=300><CTL=FIN,ACK>  -->
    
      3.  CLOSING     --> <SEQ=101><ACK=301><CTL=ACK>      ... CLOSING
                      <-- <SEQ=301><ACK=101><CTL=ACK>      <--
                      ... <SEQ=101><ACK=301><CTL=ACK>      -->
    
      4.  TIME-WAIT                                            TIME-WAIT
          (2 MSL)                                              (2 MSL)
          CLOSED                                               CLOSED
    
                          Simultaneous Close Sequence
    
                                   Figure 14.
    
    

    优先级和安全

    两个端口必须有相同的隔离级别和安全才能建立连接,并且连接的优先级要高于或者等于两个端口的优先级。

    TCP使用的优先级和安全参数就是IP中定义的相关参数。本协议规范中的安全/隔离都是指IP中的安全相关的参数,包括安全、隔离、用户组和相关限制。

    如果一个带有不相符的安全/隔离连接请求发送过来,或者优先级比较低,应该拒绝这个连接请求。应该在发送SYN之后收到对面的ACK时(给发送方一个提升自己优先级的机会),发现连接请求的优先级还是低的时候,需要拒绝这个链接。

    需要注意的是,即使是TCP使用默认的安全配置,在接收到数据包时仍然需要检查数据包的优先级,并且在必要的时候需要试着提升自己的优先级。

    即使是在非安全环境中,安全参数也有可能会使用(值可能表示数据是不需要加密的),所以当非安全环境中的主机TCP模块收到用户的安全参数时仍然需要接收这些参数,但是不需要把这些参数发送出去。

    数据交流

    连接一旦建立,数据就可以通过数据段的方式在连接上传递。因为数据段有可能会丢失或者有可能会校验出错,或者网络拥塞,TCP通过重发机制(超时)保证每个数据段都可以可靠传递。因为网络问题或者重传机制,重复的数据段有可能会被接收到。在序列号章节已经讨论过,TCP通过对接收数据段的序列号和确认序列号进行测试,来确定接收的数据段是不是可以接收的。

    发送端通过SND.NXT变量来跟踪下一个发送的数据使用的序列号。接收端通过RCV.NXT变量跟踪下一个期望收到的数据使用的序列号。发送端使用SND.UNA变量跟踪最久发送的但是没有收到确认的序列号。如果数据通道短暂的空闲,并且发送出去的数据都得到确认了,上述三个变量应该是相等的。

    当发送端将数据发送出去,发送端会增加SND.NXT变量的值。当接收端收到数据的时候它会增加RCV.NXT变量,并且会发送确认包。当发送方收到数据的确认信息后,他会增加SND.UNA变量的值。这三个值的差值可以看作是对通讯延迟的一个基本衡量。这三个变量的变化量是发送数据段的长度。注意,连接建立状态下,所有的数据段都应该包含当前的确认信息。

    用户的CLOSE函数调用类似一个推的过程,FIN控制标志包含有PUSH标志的功能。

    重发超时 因为组成互连网络的各种网络与使用TCP的用户之多,导致超时时间需要动态的确认。下面介绍一种计算超时时间的方法。

    从发送一个带有序列号的字节出去到收到对这个字节序列的确认信息(确认包确认的序列号不必与发送数据的序列号相等)的时间叫做RTT。接下来计算平滑的SRTT:

    SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT)

    基于上述公式,计算超时时间:

    RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]]

    UBOUND是超时时间的上限,LBOUND是下限,ALPHA是平滑因子(e.g., .8 to .9),BETA 是延迟变化因子 (e.g., 1.3 to 2.0)。

    紧急信息的传递

    TCP紧急机制可以允许发送方让接收方接收某些紧急数据,并且允许接收方确定什么时候把紧急数据都接收完成(紧急数据边界)。这个机制允许在数据流中放一个指针来表示紧急数据的结尾。当前数据段如果有紧急指针,并且有紧急标志,那么TCP会通知用户进入紧急模式;当处理到紧急指针时,TCP通知用户进入紧急模式。当用户处于紧急模式时,紧急指针被更新了,用户是不会被通知的。

    每个数据段都有紧急指针字段,紧急标志表明TCP必须把紧急指针字段的值与当前段序列号相加获得紧急指针的具体值。如果没有紧急标志的话,表明当前段没有紧急数据。

    发送紧急数据最少也要一个字节。如果紧急数据段同时也标志了PUSH标志,那么数据会被尽快发送到对端用户进程。

    管理窗口

    窗口是发送方用来表明它还可以接收的数据序列号范围。这个窗口基本就可以认为是接收方的接收缓存大小。

    大的窗口可以加快数据传输。如果传送过来的数据多于窗口大小,多余的数据会被丢弃。这会导致不必要的数据重传,增大网络负荷。过小的窗口会产生更多的传输时延。

    这个机制允许一开始使用大的窗口,随后如果没有更多缓存空间的话,可以慢慢减小窗口尺寸。这被称作窗口缩小。可靠性规则制定,TCP不会主动减小窗口的大小,而是要根据对面TCP的情况来改变窗口大小。

    即使发送窗口为0,TCP发送模块在收到用户传过来的数据后,也要发送最少一个字节。即使接收窗口为0,发送方也要定时发送数据到接收方。当接收窗口为0时,发送方的数据重试间隔为2分钟。当任一个TCP接收窗口为0时,定时发送数据会把接收方重新打开的窗口通知到发送方,这样是很有必要的。

    当接收窗口为0时,如果收到了数据,也应该返回一个回馈来表示下一个期望收到的字节序列号。

    为了适应接收窗口的大小,TCP把需要发送的数据打包成窗口大小,在重新发送队列中有可能会重新打包。这种重新打包不是必须的,但是也许很有帮助。

    在一个单向的数据流连接中,确认信息中有相关窗口大小,这个确认信息使用相同的序列号,这样在多个确认包乱序到达时TCP没办法对这些确认包进行重新排序。这不是很严重的问题,但是这样会导致窗口会根据老确认包中的窗口大小来发送数据。这个问题的改善方法就是,对于收到接收方的确认包中,他确认的序列号必须比上一次处理的确认序列号大,发送方才会调整发送窗口。

    窗口的管理对通讯性能有很大的影响。下面是对TCP实现的几个建议。

    1. 过小的窗口会让数据通过很小的数据段发送,如果网络性能提高,可以稍微增大窗口的大小。
    2. 接收方可以延迟更新窗口大小,当窗口大小可以达到最大窗口大小的一定比例后再更新(比例有可能是20%或者是40%)。
    3. 对于发送方的一个建议就是为了防止发送小数据包,可以等窗口到达一定大小后再发送数据。当用户调用PUSH函数时,TCP应该立即发送数据,不管是多小的包。
    4. 确认包不应该被延迟发送,否则对端会因超时重新发送数据。当收到小数据包,发送确认包时不更新窗口大小,等到窗口变大时,再通过确认信息更新窗口大小。
    5. 探测接收方窗口大小的数据包有可能会被截断成更小的数据包。如果一个字节的数据包被接收方接收了,那么这个字节会占用刚开始释放的窗口的一个字节的空间。当窗口不在是0时,发送方尽可能发送多的数据出去,那么这个数据有可能会被打包成很多大的或者小的数据包。随着时间推移,接收方会短暂停止分配更多的窗口空间,导致大的数据包会被切分成更小的数据包。时间久了数据会通过很多小数据包来传输。

    这里给TCP实现的建议就是尽可能的将小窗口合并成大窗口。因为一些简单的TCP实现总是会分配一些小窗口。

    接口(Interfaces)

    有两种接口:用户进程到TCP的接口,TCP到更底层服务的接口。我们对用户进程到TCP之间的接口有详细的定义,但是TCP到底层协议之间的接口这里没有表述,因为这个是需要底层协议进行定义。这里底层协议就是IP,这里我们只涉及TCP使用的几个IP参数。

    用户与TCP之间的接口

    以下用户使用的TCP函数都是虚构的,因为每个操作系统的实现都不尽相同。所以,我们必须提示读者,每个TCP实现有可能有不同的TCP接口。但是所有的TCP实现都应该提供最小的一个服务子集,这个子集可以维持TCP的运行。这部分描述了所有TCP实现应该实现的功能接口。

    用户命令

    接下来的章节会功能性的描述每个用户/TCP接口。每个接口的名字和大多数高级语言中的函数方法名字一样,这种命名方法并不是排除一些自陷类型的服务调用(比如SVCs,UUOs,EMTs)。

    下面的用户命令指定了TCP用于支持通讯所需的最基本的操作。不同的实现可能定义不同的方法格式,并且它们有可能会合并一些基础方法为一个方法,或者把一些基础方法分解成多个方法。特别的,用户可能希望当他们在对一个连接进行第一次的SEND 或者 RECEIVE时,TCP会自动打开这个连接。在提供通讯功能时候,TCP实现不能只接收用户请求,也同时给它服务的进程返回相关信息。相关信息包括:

    1. 连接的基本信息。(中断,远程关闭,绑定一个未指定的远程主机)
    2. 对于用户的方法调用给予反馈,比如成功或者各种类型的失败。

    Open

    格式:OPEN (local port, foreign socket, active/passive [, timeout] [, precedence] [, security/compartment] [, options]) -> local connection name

    我们假设TCP知道他服务的进程身份,并且进程在使用连接的时候TCP会检查进程有没有相应的权限。根据TCP实现的不同,连接的源地址的与端口是由TCP与IP来提供的。这些都是基于安全考虑,这样的话,就不会出现一个TCP主机装扮成另一个TCP主机。所以一个进程就不能通过与TCP合作来装扮成另一个进程。

    当主动/被动标志被设置为被动时,相当于调用LISTEN方法,来等待连接。被动连接有可能会指定特定的外部主机地址来等待特定主机的连接,或者不指定主机地址,可以接收所有的主机发起连接的请求。指定了特定远端主机的连接通过SEND指令可以转变成主动连接。

    一个TCB(传输控制块)数据结构被创建并且使用OPEN命令传过来的参数来填充部分字段的值。

    主动的OPEN调用会立即启动连接建立(同步)步骤。

    用户提交到TCP的数据如果在指定的超时时间段内没有发送到目的主机的话,TCP会关闭这个链接。这个全局的超时时间配置是5分钟。

    TCP或者操作系统的某些组件会检查用户是否有权限设置打开一个指定了优先级或者安全/隔离级别参数的连接。如果这些参数没有设置,参数默认值变会指定。

    TCP只会接收安全/隔离级别与OPEN设置相等的并且安全等级比OPEN设置的安全等级相等或者高的数据段。

    连接的优先级取OPEN参数中指定的值与接收到的数据(SYN或者ACK中的)中优先级之间最大的那个值,并且这个优先级在之后的连接生命周期中一直固定不变。TCP实现也许允许用户控制优先级协商。比如,用户可以指定只能优先级相等,或者在提升优先级等级时必须得到用户的确认。

    TCP会把本地连接的名字返回给用户。这个连接名字可以被认为是由<本地 socket, 外部 socket>指定的连接的别名。

    Send

    格式:SEND (local connection name, buffer address, byte count, PUSH flag, URGENT flag [,timeout])

    这个调用会把用户指定缓存中的数据通过指定的连接发送出去(未必真的发送)。如果连接没有打开,SEND方法可能会触发报错异常。一些TCP实现也许允许先执行SEND命令然后自动打开连接。如果用户进程没有使用这个连接的权限则会报错。

    如果PUSH标志被设置了,数据会马上传送到接收方,并且在创建的最后的数据段中会设置PUSH标志。如果PUSH没有设置,那么数据有可能会等待下次的SEND调用然后合并在一起再发送。

    如果URGENT紧急标志设置了,发送到目的地的数据段都会设置紧急指针字段值。如果紧急指针之前的紧急数据都没有被用户进程处理的话,TCP会把紧急条件报告给用户进程。紧急标志就是为了让接收方尽快处理相关数据,同时也可以标记什么时候紧急数据都被接收方收到了。发送方指定的紧急数据的次数与接收方收到的紧急数据通知的次数可能不一样。

    如果在OPEN函数调用中没有指定外部地址,但是连接已经建立了(LISTEN建立的连接),一个指定的buffer窗口大小会被发送到连接的另一端。用户使用OPEN时没指定外部地址的话,在使用SEND时可以不用了解连接另一端的地址。

    然而,在连接另一端没有确定前,用户调用SEND会报错。用户可以通过STATUS函数查看链接状态。在某些TCP实现中绑定一个未指定的外部地址会通知用户。

    当SEND提供超时时间时,连接的超时时间会被这个时间替换。

    在最简单的TCP实现中,调用SEND会一直阻塞,直到数据发送完成或者超时。但是这种简单实现会导致死锁(比如连接两端都发送数据而不接收数据)并且性能也不高,所以不推荐。稍微复杂的实现会让SEND方法立即返回,这样可以让用户进程与网络io同时进行,况且也可以调用多次SEND函数。SEND函数调用遵守先来后到准则,所以如果TCP来不及处理这些数据,会把这些请求数据放入队列中。

    我们隐式的引出了异步的用户接口,SEND调用后,底层TCP模块会通过信号或者模拟中断来通知用户进程。一个可选的实现就是立即返回一个响应给调用进程。比如,SEND会立即返回一个本地确认,即使发送出去的数据还没有被远端TCP接收到。我们可以乐观的认为最终会成功。如果我们错了,连接会因为超时断开。在这种同步的调用实现中,也会有一些信号通知,只不过这些通知,但是这些通知是与连接相关的并不是关于数据收发的。

    为了分别处理多个SEND发送之后的结果(成功或者失败),每个SEND调用都应该返回对应的响应状态码与存放接收数据缓存的内存地址。TCP到用户的接口会在下文讨论,主要讨论函数调动应该返回什么的信息给用户进程。

    Receive

    格式: RECEIVE (local connection name, buffer address, byte count) -> byte count, urgent flag, push flag

    这个命令会为相关连接分配一个接收数据的缓存。如果在这个命令前没有执行OPEN命令或者进程没有相关权限,那么会抛出异常。

    在最简单的实现里,调用这个函数的进程会一直阻塞,直到接收缓存(进程传递的缓存不是TCP的接收缓存)填满或者某些异常出现,但是这种实现方式会导致死锁。复杂的实现版本允许多个RECEIVE调用。这些RECEIVE的接收缓存会在数据段到达时被填充。精心设计的系统(有可能是异步系统)可以在收到PUSH标志或者RECEIVE接收缓冲被填满时通知调用进程,这样大大提升了系统吞吐量。

    如果在收到PUSH标志前,接收到的数据已经可以填满RECEIVE接收缓冲,那么RECEIVE方法调用会返回,但是PUSH标志不会被标记。RECEIVE接收缓存会尽可能的被填充,但是如果在没被填满前收到了PUSH标志,那么数据会被立即返回,并且会设置PUSH标记。

    如果有紧急数据到达,那么TCP会通过TCP-to-user信号通知用户进程。用户进程之后进入紧急模式。如果URGENT标志开启,需要等待更多的紧急数据。如果URGENT标志关闭,RECEIVE会返回接收到的所有紧急数据,并且用户进程进入正常模式。需要注意的是,紧急数据不能与非紧急数据处于同一个RECEIVE接收缓存中,除非有标志来明确这两种数据的边界。

    为了区分多个正在等待接收数据的RECEIVE调用,同时也考虑有些接收缓存不会被填满,在调用返回时会跟着一个接收缓存的地址,以及读取的字节数量。

    其他TCP实现,可能允许进程调用RECEIVE时使用TCP的内部接收缓存,或者TCP与用户进程共享一个环形缓存。

    Close

    格式: CLOSE (local connection name)

    这个命令会将指定的连接关闭。如果连接没有打开,或者进程没有相关权限,异常会被返回。关闭命令是一个优雅的操作,当多个SEND命令发送的数据在传输中(或者重传)并且流量控制允许,这些数据会在成功发送到对面后,连接才会关闭。所以,可以在多个SEND命令后跟一个CLOSE命令,这样也会保证发送的数据会到达目的地。需要注意的是,即使连接处于半关闭状态,但是进程仍然可以通过RECEIVE命令收数据,因为对面进程仍然会发送数据。所以Close命令是我不想发送数据了,但是不代表我不想接收数据了。在超时前,关闭连接的一方有可能不能抛弃剩余的数据。在这种场景下Close命令可以转成ABORT命令,关闭连接。

    用户可以在任何时间主动关闭连接,有可能会因为TCP的某些提示(远程关闭,重发超时,远端不可达)关闭连接。

    因为关闭连接需要与对端进行通讯,所以关闭连接的一方会短暂地以处于closing状态。在TCP还没有对close做出响应前重新打开连接会报错。

    Close有着与push一样的语义。

    Status

    格式:STATUS (local connection name) -> status data

    不同的TCP实现,这个命令可能不一样,没有这个指令对整体TCP接口并没有影响。状态信息一般是从连接关联的TCB查询获得的。

    该命令返回包含如下信息的数据块:

    1. 本地socket
    2. 外部socket
    3. 本地连接名称
    4. 接收窗口
    5. 发送窗口
    6. 连接状态
    7. 等待确认的缓存数量
    8. 等待接收的缓存数量
    9. 紧急状态
    10. 优先级
    11. 安全/隔离
    12. 超时时间

    根据连接状态以及实现,某些字段可能没有或者没有任何意义。如果进程没有权限调用这个命令,则会返回一个异常,这样会防止没有授权的进程获取相关连接的状态。

    Abort

    格式:ABORT (local connection name)

    这个命令会丢弃所有等待发送的数据和等待接收的数据,同时会移除TCB控制块,并且向对端发送一个RESET消息。根据实现的不同,用户进程可能会收到每个发送或者接收数据块的删除通知,或者只会收到一个ABORT确认。

    TCP-to-User Messages

    假定操作系统提供了TCP异步通知用户进程的方式。当TCP通知用户进程时,相关数据会传递给用户进程。本协议一般指异常消息。在其他情况下,有可能会通知关于SEND,RECEIVE或者其他命令的处理返回结果。

    一般包含一下信息:

    1. 本地连接名称 经常
    2. 相应字符串 经常
    3. 缓存地址 发送或者接收
    4. 接收的字节数 接收
    5. PUSH标志 接收
    6. 紧急标志 接收

    TCP/Lower-Level Interface

    TCP实际上通过调用底层的协议模块来在网络上实现发送和接收数据。一个例子就是ARPA网中,底层模块是IP协议。 当底层协议是IP时,它提供服务类型参数和数据包存活时间选项。TCP使用如下参数来对接ip的这些参数:

          Type of Service = Precedence: routine, Delay: normal, Throughput:
          normal, Reliability: normal; or 00000000.
    
          Time to Live    = one minute, or 00111100.
          
          最大的数据段生命周期是2分钟。我们明确如果数据在一分钟内不能分发出去,便把这个数据段丢弃。
    

    如果底层协议是IP(其他也能提供类似特性的协议)并且源地址路由( Source Routing_achejq的专栏-CSDN博客 )被使用,TCP提供的接口必须提供可以操作此类信息的接口。这个功能非常重要,这样TCP就可以获取源地址与目的地地址来计算校验和(头部的伪地址)。这个也可以保留请求的返回路由信息。

    任何底层的协议必须提供源地址、目的地址、协议字段、检测TCP数据长度的方式,这些可以提供与ip一样的服务并且可以用来计算TCP的校验和。

    事件处理(Event Processing)

    这个章节描述的处理过程只是一个可能实现的示例。其他的实现可能有不同的处理序列,但是他们同本章描述的只有细微的差别,而不应该有很大的实质性的差别。

    TCP的活动可以简单描述成对事件的响应。事件可以被分成三类:用户调用、数据段到达和超时。本章描述TCP处理这些事件的过程。在很多场景中,TCP的处理流程依赖当前连接所处的状态。

        Events that occur:
          User Calls
            OPEN
            SEND
            RECEIVE
            CLOSE
            ABORT
            STATUS
          Arriving Segments
            SEGMENT ARRIVES
          Timeouts
            USER TIMEOUT
            RETRANSMISSION TIMEOUT
            TIME-WAIT TIMEOUT
    

    TCP/user接口模型是指用户命令会立即返回,并且延迟一段时间后会得到相应结果。在下文,‘signal’是指延迟获得结果。异常响应被描述为一段字符串。比如用户引用了不存在的连接会得到:error:connection not open这个响应。

    在下文描述的针对序列号、确认序列号、窗口等等的运算都是基于232这个序列号空间做的取模。同样=<意思是等于或者小于某个数值(依旧是对232做取模运算后的)。

    处理到达数据包的第一步就是检验序列号(比如数据段中的序列号位于接收窗口中),然后被放入队列中,然后通过序列号排序。

    如果我们收到的数据段与之前已经收到的数据段有一部分重叠,那么我们重新构造这个数据段,只包含新的数据,然后重新组织头部信息来保持一致。

    注意,如果没有提及连接状态变更,那么连接一直保持这个状态不变。

    OPEN Call

    CLOSED STATE (i.e., TCB does not exist)
            创建一个新的TCB来保存连接的状态,保存本地socket标识符(变量引用)、外部socket地址、优先级、安全/隔离、以及超时相关的信息。
            注意,被动打开的连接在没有指定外部socket地址,TCB中的相关信息需要等待外部进程进行SYN同步的时候进行填充。检查这个用户被允许的安全与优先级,
            如果不符合则返回“error: precedence not allowed”或者返回“error:  security/compartment not allowed.”如果是被动打开的话,连接进入LISTEN状态并且返回。
            如果是主动打开,但是没有指定外部socket地址,返回“error:foreign socket          unspecified”,如果主动打开,并且指定了外部socket地址,发送一个SYN数据包。
            选择一个初始化序列号,一个<SEQ=ISS><CTL=SYN>这样的格式的数据包被发送。把SND.UNA 值设置为初始化序列号,SND.NXT值设置为初始化序列号+1,进入SYN-SENT状态并返回。
            如果用户进程没有创建连接的权限,返回“error:  connection illegal for this process”。如果没有足够的内存空间来创建连接,返回“error:  insufficient resources”。
    LISTEN状态
            如果打开时模式是主动模式,并且外地socket地址指定了,那么连接状态从被动转成主动,并选择一个初始化序列号。
            发送一个SYN数据包,把SND.UNA 值设置为初始化序列号,SND.NXT值设置为初始化序列号+1,进入SYN-SENT状态。与SEND命令发送的数据可能与SYN数据包一起发送,也有可能缓存起来等到连接进入ESTABLISHED时再发送。
            如果SEND命令设置了紧急标志,那么这个标志必须与数据包一起发送出去。如果没有内存空间来缓存发送的数据,返回“error:  insufficient resources”。
            如果外部地址没有指定那么返回“error:  foreign socket unspecified”。
    
    SYN-SENT STATE
    SYN-RECEIVED STATE
    ESTABLISHED STATE
    FIN-WAIT-1 STATE
    FIN-WAIT-2 STATE
    CLOSE-WAIT STATE
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT STATE
            返回:error:  connection already exists
    

    SEND Call

    CLOSED STATE (i.e., TCB does not exist)
            如果用户没有权限访问此链接,返回“error:  connection illegal for this process”。
            否则返回:“error:  connection does not exist”。
    LISTEN STATE
            如果指定了外部socket地址,那么连接状态从被动转成主动,并选择一个初始化序列号。
            发送一个SYN数据包,把SND.UNA 值设置为初始化序列号,SND.NXT值设置为初始化序列号+1,进入SYN-SENT状态。与SEND命令发送的数据可能与SYN数据包一起发送,也有可能缓存起来等到连接进入ESTABLISHED时再发送。
            如果SEND命令设置了紧急标志,那么这个标志必须与数据包一起发送出去。如果没有内存空间来缓存发送的数据,返回“error:  insufficient resources”。
            如果外部地址没有指定那么返回“error:  foreign socket unspecified”。
    SYN-SENT STATE
    SYN-RECEIVED STATE
            保存数据,等到状态变成ESTABLISHED时,把数据发送出去。如果没有足够内存来保存数据,返回“error:  insufficientresources”。
    ESTABLISHED STATE
    CLOSE-WAIT STATE
            把数据打包成数据段,并且把确认信息、希望收到的下一个数据段序列号一并发送出去。如果没有足够的内存空间,返回“error:insufficient resources”。如果设置了紧急状态字段,把TCP维护的SND.UP(紧急指针) 值更新为SND.NXT-1并设置数据段的紧急状态指针。
    FIN-WAIT-1 STATE
    FIN-WAIT-2 STATE
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT STATE
            返回“error:  connection closing”,并且不会为当前请求服务。
    

    RECEIVE Call

    CLOSED STATE (i.e., TCB does not exist)
            如果用户没有权限访问此链接,返回“error:  connection illegal for this process”。
            否则返回:“error:  connection does not exist”。
    LISTEN STATE
    SYN-SENT STATE
    SYN-RECEIVED STATE
            保存数据,等到状态变成ESTABLISHED时,把数据发送出去。如果没有足够内存来保存数据,返回“error:  insufficientresources”
    ESTABLISHED STATE
    FIN-WAIT-1 STATE
    FIN-WAIT-2 STATE
            如果收到的数据不能满足receive请求,缓存receive请求。如果没有足够的内存空间来记录receive状态,返回“error:  insufficientresources”。
            把收到的数据段组合起来放进receive的接收缓存,返回给用户。如果在传递给用户数据的前面有紧急数据,那么通知用户进程。如果TCP负责把数据分发到用户进程,那么TCP必须给数据发送者一个确认信息。在下面的处理数据段来介绍具体的确认格式。
    CLOSE-WAIT STATE
            因为对端已经停止发送数据,所以receive只能从TCP接收缓存中获取数据,如果接收缓存中没有数据,返回“error:  connection closing”。
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT STATE
            返回“error:  connection closing”。
            
    

    CLOSE Call

    CLOSED STATE (i.e., TCB does not exist)
            如果用户没有权限访问此链接,返回“error:  connection illegal for this process”。
            否则返回:“error:  connection does not exist”。 
    LISTEN STATE        
            所有等待接收的数据都被返回“error:  closing”。删除TCB,进入CLOSED状态,返回。
    SYN-SENT STATE
            删除TCB,任何等待处理的receive和send数据都被返回“error:  closing”。
    SYN-RECEIVED STATE
            如果没有SENDs命令,或者没有要发送的数据,构造一个FIN数据段并发送。
    ESTABLISHED STATE
            缓存这个请求,直到所有以前的发送请求都完成,然后生成一个FIN数据段发送出去。然后进入FIN-WAIT-1状态。
    FIN-WAIT-1 STATE
    FIN-WAIT-2 STATE
            严格讲,这是一个错误操作,应该返回“error:
          connection closing”。返回ok也是可以接收的只要第二个FIN没有发出去,因为第一个FIN有可能在进行重发。
    CLOSE-WAIT STATE
            缓存这个请求,直到所有的send指令都完成,然后发送一个FIN标志出去,之后进入CLOSING状态。
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT STATE
            返回“error:  connection closing”。
    
    

    ABORT Call

    CLOSED STATE (i.e., TCB does not exist)
            如果用户没有权限访问此链接,返回“error:  connection illegal for this process”。
            否则返回:“error:  connection does not exist”。
    LISTEN STATE
            所有缓存的receive都会收到“error:connection reset”。删除TCB,进入CLOSED状态然后返回。
    SYN-SENT STATE
            所有的SENDs和RECEIVEs操作都应该收到“connection reset”通知,删除TCB,进入CLOSED状态然后返回。
    SYN-RECEIVED STATE
    ESTABLISHED STATE
    FIN-WAIT-1 STATE
    FIN-WAIT-2 STATE
    CLOSE-WAIT STATE
            发送一个重置数据包:
                <SEQ=SND.NXT><CTL=RST>
            所有的SENDs 和 RECEIVEs操作都应该收到“connection reset”通知,所有的数据段都应该清空(rst数据段除外),所有的重发队列的数据都应该清空,删除TCB,进入CLOSED状态然后返回。
    CLOSING STATE
    LAST-ACK STATE
    TIME-WAIT STATE  
            返回ok,删除TCB,进入CLOSED状态然后返回。
            
    

    STATUS Call

    CLOSED STATE (i.e., TCB does not exist)
          If the user should not have access to such a connection, return
          "error:  connection illegal for this process".
          Otherwise return "error:  connection does not exist".
    LISTEN STATE
          Return "state = LISTEN", and the TCB pointer.
    SYN-SENT STATE
          Return "state = SYN-SENT", and the TCB pointer.
    SYN-RECEIVED STATE
          Return "state = SYN-RECEIVED", and the TCB pointer.
    ESTABLISHED STATE
          Return "state = ESTABLISHED", and the TCB pointer.
    FIN-WAIT-1 STATE
          Return "state = FIN-WAIT-1", and the TCB pointer.
    FIN-WAIT-2 STATE
          Return "state = FIN-WAIT-2", and the TCB pointer.
    CLOSE-WAIT STATE
          Return "state = CLOSE-WAIT", and the TCB pointer.
    CLOSING STATE
          Return "state = CLOSING", and the TCB pointer.
    LAST-ACK STATE
          Return "state = LAST-ACK", and the TCB pointer.
    TIME-WAIT STATE
          Return "state = TIME-WAIT", and the TCB pointer.
    

    SEGMENT ARRIVES

    如果状态是CLOSED
        所有接收的数据都会被丢弃,包含RST标志的数据段也会被丢弃。不包含RST标志的数据段,会返回一个包含RST的数据段返回给发送者。
        获取数据包中的确认序列号,然后把RST数据包的序列号设置为这个序列号,返回给发送者。如果没有设置ACK标志,那么RST数据包使用序列号0:
        <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
        如果ACK标志被设置了:
        <SEQ=SEG.ACK><CTL=RST>
    如果状态是LISTEN
        1.首先检查是不是RST数据包
            如果是RST数据包,直接忽略。
        2.然后检查是不是ACK数据包
            如果连接处于LISTEN状态,收到确认信息是不正常的。对于收到的任何确认信息都应该返回RST数据包:
            <SEQ=SEG.ACK><CTL=RST>
        3.再然后检查是不是SYN数据包
            如果收到SYN数据包,检查安全性。如果数据包的安全/隔离不满足TCB里记录的值,直接返回一个RST数据包。
            <SEQ=SEG.ACK><CTL=RST>
            如果数据包的SEG.PRC(优先级)大于TCB里记录的TCB.PRC,如果用户允许,那么设置TCB.PRC<-SEG.PRC,如果不允许,直接返回一个RST数据包。
            <SEQ=SEG.ACK><CTL=RST>
            如果数据包的SEG.PRC(优先级)小于TCB里记录的TCB.PRC,继续。
            设置RCV.NXT的值为SEG.SEQ+1,IRS被设置为SEG.SEQ,其他控制信息和数据应该被缓存起来等待后续处理。选择一个ISS,然后发送一个格式为如下的SYN包:<SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
            SND.NXT被设置为ISS+1,SND.UNA 设置为ISS。连接状态变更为SYN-RECEIVED。注意,收到的控制信息和数据可以在SYN-RECEIVED状态中处理,但是SYN和ACK不能重复处理。如果在listen时没有指定外部socket信息,那么现在可以通过数据包中的信息来填充相关信息了。
        4.最后关于其他控制信息和数据
            其他控制信息和数据段(没有SYN标志),都应该有反馈信息,收到反馈信息的数据会被丢弃(发送方丢弃)。一个RST包可能是非法的,因为这个数据包并不是之前发送过的任何数据的响应信息。程序流程一般到不了这里,如果走到这里,直接把数据删除。
    如果状态是SYN-SENT
        1.检查ACK标志
            如果设置
                如果SEG.ACK =< ISS或者SEG.ACK > SND.NXT,发送一个RST控制包(除非接收的数据包RST标志被设置了),删除数据段,返回。
                <SEQ=SEG.ACK><CTL=RST>
                如果SND.UNA =< SEG.ACK =< SND.NXT
        2.然后检查RST标志
            如果设置
                如果ACK被标志了并且是可以接收的,那么通知用户“error:
              connection reset”,删除数据段,进入CLOSED状态,删除TCB,返回。如果没有ACK标志,丢弃RST控制包,直接返回。
        3.检查安全性和优先级
            如果数据包的安全/隔离与TCB记录的不符合,生成一个RST控制包,发送。
                如果有ACK标志
                    <SEQ=SEG.ACK><CTL=RST>
                如果没有ACK标志
                  <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
                如果有ACK标志
                    数据段优先级必须与TCB优先级相符合否则:
                        <SEQ=SEG.ACK><CTL=RST>
                如果没有ACK标志
                    如果数据包优先级比TCB优先级高,并且用户进程允许的话,提升TCB优先级,如果用户进程不允许提升TCB优先级,那么返回一个RST控制包
                        <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
                    如果数据段优先级比TCB优先级低,直接忽略。
                如果RST控制包发送了,丢弃收到的数据包,然后返回。
        4.检查SYN标志
            这步骤只应该在ACK已经标记或者ACK没有标记RST也没有标记的情况下。
            如果有SYN标记,并且优先级和安全/隔离都符合,那么RCV.NXT设置为SEG.SEQ+1,IRS设置为SEG.SEQ。如果有ACK标记,那么SND.UNA应该和SEG.ACK相等。在重发队列的被确认的数据段应该被删除。
            如果SND.UNA > ISS(我们的SYN已经被确认了),连接状态变更为ESTABLISHED,生成一个ACK控制包:
                <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
            发送队列中的数据段或者控制包可能会与ACK一起发送。如果数据段中还有其他控制信息,那么将在下面检查URG标志的第6步中处理,否则将会返回。
            其他情况下,进入SYN-RECEIVED状态,生成SYN,ACK数据段并发送:
                <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
            如果有其他控制信息或者数据,缓存这些数据,等到连接进入ESTABLISHED状态再去处理。
        5.如果SYN和RST都没有设置,直接丢弃数据包,返回。
    其他状态
        1.检查序列号
            SYN-RECEIVED STATE
            ESTABLISHED STATE
            FIN-WAIT-1 STATE
            FIN-WAIT-2 STATE
            CLOSE-WAIT STATE
            CLOSING STATE
            LAST-ACK STATE
            TIME-WAIT STATE
                数据段按序列号顺序处理。数据段的初步判断主要用来去除重复的数据,然后按照SEG.SEQ顺序处理。如果一个数据段既有老数据又有新数据,那么新数据会得到处理。
                下面有四种情况用来检验数据段的序列号:
                Segment Receive  Test
                Length  Window
                ------- -------  -------------------------------------------
        
                   0       0     SEG.SEQ = RCV.NXT
        
                   0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
        
                  >0       0     not acceptable
        
                  >0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
                              or RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND
                    
                如果RCV.WND是0,那么不会接收新数据,但是ACK、URG、RST仍然会被处理。
                如果数据段不能被接收,那么应该返回一个响应。(如果数据段的RST被设置了,接收方就不用返回确认信息了,之后丢弃收到的数据段并返回。):
                     <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
                发送确认信息后,丢弃数据包。
                接下来,讨论的数据段都是理想的数据段,这些数据段的序列号起始于RCV.NXT,没有超过接收方的窗口大小。有可能会把多余窗口的数据段剪裁成一个新的数据段(包括SYN和FIN),等待后续处理。
                比当前期望接收的序列号大的数据段有可能会被缓存以备后续处理。
        2.检查RST标志
            SYN-RECEIVED STATE
                如果设置了RST标志
                    如果接收端连接状态之前是LISTEN状态,连接重置为LISTEN状态,然后返回,不用通知用户。如果用户是主动打开的(当前状态是从SYN-SENT状态转变过来的),通知用户“connection refused”,然后拒绝建立连接。上述两种情况都会把重发队列中的数据清空,在主动打开情况下,进入CLOSED状态,然后删除TCB,返回
                
            ESTABLISHED STATE
            FIN-WAIT-1 STATE
            FIN-WAIT-2 STATE
            CLOSE-WAIT STATE
                如果设置了RST标志
                    所有正在处理或者缓存的RECEIVEs与SEND方法都会收到“reset”。所有队列会被清空。用户进程会收到“connection reset”通知。连接进入CLOSED状态,删除TCB,返回。
            CLOSING STATE
            LAST-ACK STATE
            TIME-WAIT
                如果设置了RST标志,直接进入CLOSED状态,删除TCB,返回。
                
        3.检查优先级、安全/隔离
            SYN-RECEIVED
                如果接收数据段的优先级、安全/隔离与TCB的不符合,直接返回reset控制包,然后返回。
            ESTABLISHED STATE
                如果接收数据段的优先级、安全/隔离与TCB的不符合,直接返回reset控制包,未处理的RECEIVEs 和 SEND都应该收到“reset”。在队列中的数据段都应该清空。用户进程收到“connection reset”通知,进入CLOSED状态,删除TCB,返回。
            这个检查放在序列号检查之后,为了防止上一个连接实例的数据段导致现在连接被关闭。
        4.检查SYN标记
            SYN-RECEIVED
            ESTABLISHED STATE
            FIN-WAIT STATE-1
            FIN-WAIT STATE-2
            CLOSE-WAIT STATE
            CLOSING STATE
            LAST-ACK STATE
            TIME-WAIT STATE
                如果SYN的序列号在接收窗口中,那么这是一个错误包,直接发送reset控制包,任何RECEIVEs 和 SEND都应该收到“reset”,所有队列中的数据都应该清空,用户进程也应该收到“connection reset”通知,然后进入CLOSED状态,删除TCB,返回。
                SYN控制包不在窗口中,流程不应该能走到这里。序列号确认那个流程中应该会返回确认信息。
            
        5.检查ACK标记
            如果没有ACK标记直接丢弃并返回。
            如果有ACK
                SYN-RECEIVED STATE
                    如果SND.UNA =< SEG.ACK =< SND.NXT直接进入ESTABLISHED状态,继续处理。
                    如果ACK不可接收,直接返回reset控制包
                        <SEQ=SEG.ACK><CTL=RST>
                ESTABLISHED STATE
                    如果SND.UNA < SEG.ACK =< SND.NXT,那么SND.UNA 设置为 SEG.ACK。重发队列中的被确认的数据都可以删除。用户应该收到正数的确认序号,所有发送的数据都被完全确认(同步情况下,SEND函数返回ok)。如果ACK是重复确认(SEG.ACK < SND.UNA)直接忽略。如果确认了没有发送过的数据,直接把数据包丢弃,返回(因为是同步状态,这种情况下直接丢弃就行,但是非同步状态下,就需要特殊处理了)。
                    如果 SND.UNA < SEG.ACK =< SND.NXT,应该更新发送窗口大小。如果SND.WL1 < SEG.SEQ or (SND.WL1 = SEG.SEQ SND.WL2 =< SEG.ACK)设置SND.WND <- SEG.WND,SND.WL1 <- SEG.SEQ,   SND.WL2 <- SEG.ACK
                    SND.WND是相对于SND.UNA的偏移量,SND.WL1记录上次更新SND.WND时数据段的序列号,SND.WL2记录上次更新SND.WND时ACK中的序列号。这里检查为了防止老数据段更新窗口大小。
                FIN-WAIT-1 STATE
                    如果发出去的FIN得到确认,直接进入FIN-WAIT-2状态。
                FIN-WAIT-2 STATE
                    如果重发队列是空的,那么可以确认连接对端发送的FIN,但是不删除TCB。
                CLOSE-WAIT STATE
                    逻辑与ESTABLISHED状态时一样。
                CLOSING STATE
                    如果发送的FIN得到确认直接进入TIME-WAIT状态,其他情况直接删除数据包。    
                LAST-ACK STATE
                    这个状态唯一可以接收的数据,就是对发送的FIN进行的确认信息。如果FIN得到确认,进入CLOSED状态,删除TCB,返回。
                TIME-WAIT
                    唯一可以接收的数据就是对面重发过来的FIN消息,对这个IFN进行确认,然后重新等待2 MSL的时间。 
        6.检查URG标志
            ESTABLISHED STATE
            FIN-WAIT-1 STATE
            FIN-WAIT-2 STATE
                如果URG标记被设置,RCV.UP <- max(RCV.UP,SEG.UP),并且通知用户对端有紧急数据需要处理。如果用户已经在紧急模式中,不用重复通知用户。
            CLOSE-WAIT STATE
            CLOSING STATE
            LAST-ACK STATE
            TIME-WAIT
                不会到达这个流程,因为之前收到了FIN,直接忽略数据段。
        7.处理用户数据
            ESTABLISHED STATE
            FIN-WAIT-1 STATE
            FIN-WAIT-2 STATE
                一旦进入ESTABLISHED状态,收到的数据段应该分发到用户的RECEIVE缓冲中。数据段可以分配到用户缓冲中,直到用户缓冲满了或者数据段没数据了。如果数据段是空的,但是有PUSH标志,这是会把用户缓冲返回给用户进程,并且会通知用户进程,接收到了PUSH标志。
                如果TCP负责把数据传递给用户进程,那么TCP负责确认收到的数据。
                一旦TCP接收到数据,他就把RCV.NXT前进到接收的数据字节数,并把RCV.WND调整到目前TCP接收缓存可用的内存大小。RCV.NXT与RCV.WND的总和不能减少,只能前进。
                请在数据交流章节了解窗口管理。
                确认数据包格式为:
                    <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
                这个确认标识应该和数据包一起发送,但是延迟时间应该控制在合理的范围内。
            
            CLOSE-WAIT STATE
            CLOSING STATE
            LAST-ACK STATE
            TIME-WAIT STATE
                不应该走到这个流程,因为收到了FIN标志,直接删除数据包。
        8.检查FIN标志
            如果连接处于CLOSED, LISTEN 或者 SYN-SENT状态,不用处理FIN标志,因为无法检验SEG.SEQ是否合法,删除数据包,返回。
            如果数据段标记了FIN,提示用户“connection closing”,对所有的RECEIVEs返回同样的信息。设置RCV.NXT为FIN的数据包的序列号+1,对FIN返回一个确认信息。FIN和PUSH有相同的作用,把所有已经接收但是还没分发到用户进程的数据,分发到用户进程。
                SYN-RECEIVED STATE
                ESTABLISHED STATE
                    进入CLOSE-WAIT状态。
                FIN-WAIT-1
                    如果之前发送的FIN得到确认(接收的FIN包中有ACK信息),进入TIME-WAIT状态,然后启动time-wait定时器,关掉其他定时器。否则进入CLOSING状态。
                FIN-WAIT-2 STATE
                    进入TIME-WAIT状态,然后启动time-wait定时器,关掉其他定时器。
                CLOSE-WAIT STATE
                    继续保持CLOSE-WAIT状态
                CLOSING STATE
                    继续保持CLOSING状态
                LAST-ACK STATE  
                    继续保持LAST-ACK状态
                TIME-WAIT STATE
                    继续保持TIME-WAIT状态,重启2 MSL定时器。
    

    USER TIMEOUT

        USER TIMEOUT(用户指定的时间)
            不管连接处于什么状态,用户指定的超时时间超时,清空所有队列中的数据,通知用户进程“error:  connection aborted due to user timeout”,删除TCB,进入CLOSED状态,返回。
        RETRANSMISSION TIMEOUT(重发超时)
            不管连接处于什么状态,重发队列中的某个数据段超时,直接把数据段前移到队列前端,重启定时器。
        TIME-WAIT TIMEOUT
            如果超时,删除TCB,纳入CLOSED状态,返回。
    
    展开全文
  • RFC793

    2008-06-04 10:27:46
    TCP protocol
  • RFC793中文资料

    热门讨论 2011-03-08 10:09:22
    传输控制协议(TCP)打算用作在分组交换计算机通迅网络和这类网络的内部连接系统 的主机间高可靠主机对主机协议。 本文描述传输控制协议将执行的功能,实现的程序,及提供给要求其服务的程序或用户 ...
  • RFC 793 翻译

    2011-06-27 20:55:25
    RFC 793 部分翻译,讲述TCPIP的中文资料
  • rfc793 TCP协议标准 PDF

    2009-11-12 15:08:23
    IETF Internet Transport Protocol协议RFC标准 PDF格式,英文版
  • RFC793中文.txt

    2009-02-20 15:52:50
    RFC793中文文档 TCP协议的说明文档
  • TCP RFC 793

    2014-12-17 21:51:57
    TCP描述文档,权威,全面,精确. 通过本文档可以全面的了解TCP的设计和原理。
  • rfc793 tcp协议

    2008-10-14 11:30:33
    This document describes the DoD Standard Transmission Control Protocol (TCP). There have been nine earlier editions of the ARPA TCP specification on which this standard is based, and the present text ...
  • RFC793 : TCP传输控制协议

    千次阅读 2013-10-17 15:55:43
    RFC: 793 TRANSMISSION CONTROL PROTOCOL DARPA INTERNET PROGRAM PROTOCOL SPECIFICATION September 1
  • RFC793中文版(TCP)

    2009-01-02 19:20:17
    RFC793中文文档,传输控制协议规范(TCP)。
  • rfc793(tcp协议)中文翻译文档。
  • tcp - rfc793

    2021-12-21 23:17:32
    tcp-rfc793
  • RFC793 TCP

    2010-05-12 20:33:08
    RFC793 对TCP最原始的描述(1981)
  • <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
  • RFC793,1323,1122,2018,2581源代码,构成TCP/IP协议。有感兴趣的可下载!!
  • 读 TCP 协议 RFC-793

    2022-09-07 09:40:55
    现行协议版本被IETF标准化为RFC-793。 虽然TCP主要运行在互联网的IP协议层之上,但原则上,TCP能够在多种通信系统上进行操作,从硬连线连接到分组交换或电路交换系统。 概述 互联网系统元素 Internet环境由连接在...
  • A variety of other cases are possible, all of which are accounted for by the following rules for RST generation and processing. Reset Generation As a general rule, reset (RST) must be sent w...
  • 我是如何查找RFC官方资料的

    千次阅读 2021-01-05 10:41:19
    目录 01、关于TCP协议头中保留位 02、Wireshark工具 03、RFC官方协议下载 01、关于TCP协议头中保留位 读者疑问: ...部分读者对上一篇《TCP协议详解》中的...这本书讲的没有错,因为在TCP标准文档(RFC793)中也是这样
  • 紧急数据的分歧对于RFC793和RFC1122都说法不一,如下图所示: 如上图所示,RFC1122认为:紧急数据序列号=SEQ+Urgent Pointer,RFC793则认为:紧急数据序列号=SEQ+Urgent Pointer-1。实际上 RFC 793 是错误的,RFC ...
  • RFC 793 记录

    千次阅读 2007-08-08 16:40:00
    RFC793规定最大分片生存时间MSL为2分钟   静止时间最好要实现.否则可能出现序列号重复出现.旧的分片被当成新的分片被接收. 3.4 建立连接 建立连接的三次握手是一个同步双方初始序列号的过程. 三次握手的...
  • RFC英文文档完整版.zip

    2020-10-10 17:47:32
    完整的RFC协议英文文档,其中RFC793是TCP协议的文档 Request For Comments(RFC),是一系列以编号排定的文件。文件收集了有关互联网相关信息,以及UNIX和互联网社区的软件文件。RFC文件是由Internet Society(ISOC...
  • rfc 793 (tcp protocal)

    千次阅读 2007-09-12 23:26:00
    http://www.faqs.org/rfcs/rfc793.html
  • RFC793中文翻译三(dlmu2001)

    千次阅读 2006-09-08 15:33:00
    3.功能性规范(FUNCTIONAL SPECIFICATION)3.1 头部格式TCP分片作为internet数据报发送。Internet协议头部携带了一些信息头部,包括源主机地址和目的主机地址[2]。TCP头部也仿效Internet头部,提供TCP协议的一些特定...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,172
精华内容 3,268
关键字:

rfc793