Chinaunix首页 | 论坛 | 博客
  • 博客访问: 207072
  • 博文数量: 58
  • 博客积分: 1420
  • 博客等级: 上尉
  • 技术积分: 662
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-01 11:56
文章分类

全部博文(58)

文章存档

2009年(50)

2008年(8)

我的朋友

分类: 系统运维

2009-05-19 05:22:40

传输层:TCP、UDP和SCTP2.6 TCP连接的建立和终止

输层:TCP、UDP和SCTP2.1    概述

    本章提供本书例子所用TCP/IP协议的概貌。我们的目的是从网络编程角度提供足够的细节以理解如何使用这些协议,同时提供有关这些协议的实际设计、实现及历史的具体描述的参考点。
    本章的焦点是传输层,即TCP、UDP和SCTP(Stream Control Transmission Protocol)。绝大多数的客户/服务器网络应用使用TCP或UDP。SCTP是一个较新的协议,最初设计用于跨因特网传输电话信令。这些协议都转而使用网络层协议IP:IP版本4(IPv4)或IP版本6(IPv6)。尽管可以绕过传输层直接使用IPv4或IPv6,但这种技术(称为原始套接口)较少使用。因此,我们把IPv4和IPv6以及ICMPv4和ICMPv6的详细描述安排在附录A。
    UDP是一种简单的、不可靠的数据报协议,而TCP是一种精致的、可靠的字节流协议。SCTP与 TCP一样也是一种可靠的传输协议,不过它还提供消息边界、传输级别多宿(multihoming)支持以及将头端阻塞(head of line blocking)减少到最小的一种方法。我们必须了解由这些传输层提供给应用进程的服务,这样才能弄清这些协议处理什么,我们的应用进程又需要处理什么。
    一旦理解TCP的某些特性,我们就能更容易地编写健壮的客户和服务器程序,更容易地使用诸如netstat等工具来调试我们的客户和服务器程序。本章将阐述下述主题:TCP的三路握手、TCP连接终止序列、TCP的TIME_WAIT状态;SCTP的四路握手和SCTP的连接终止;以及套接口层的TCP、UDP和SCTP缓冲机制等等。

2.2    总图


    虽然称为“TCP/IP”协议族,但是本协议族还有许多其他成员。图2.1展示了这些协议的概貌。
    在这个图中,我们展示了IPv4和IPv6。从右向左观察这个图,最右边的5个网络应用使用 IPv6,这涉及到第3章中的AF_INET6常值和sockaddr_in6结构。另外的6个网络程序使用IPv4。
    最左边的网络应用(tcpdump)直接使用BPF(BSD分组过滤器)或DLPI(数据链路提供者接口)同数据链路层进行通信。右边9个网络应用的下面用虚线标记出API,它通常是套接口或XTI。使用BPF或DLPI的接口不用套接口或XTI。
    这种情况的一种例外将在第28章详细描述:Linux使用一种称为SOCK_PACKET的特殊套接口类型提供数据链路层的访问。
图2.1TCP/IP协议概貌

    在图2.1中,我们还注意到traceroute程序使用两种套接口:IP套接口和ICMP套接口。在第 28章我们将开发ping和traceroute这两个网络应用的IPv4和IPv6版本。
    下面我们讲解图2.1中的每一种协议。
    IPv4,网际协议,版本4(Internet Protocol version 4)。IPv4(我们通常就称之为IP) 自20世纪80年代早期以来一直是网际协议族的主力协议。它使用32位的地址(A.4节)。IPv4 给TCP、UDP、SCTP、ICMP和IGMP提供递送分组的服务。
    IPv6,网际协议,版本6(Internet Protocol version 6)。IPv6设计于20世纪90年代中期,用以替代IPv4。主要变化是使用128位的大地址(A.5节)以应对90年代因特网爆发性的增长。IPv6给TCP、UDP、SCTP和ICMPv6提供递送分组的服务。
    当无需区别IPv4和IPv6时,我们经常使用IP这个形容词,如IP层、IP地址等等。
    TCP,传输控制协议(Transmission Control Protocol)。TCP是一种面向连接的协议。它给用户进程提供可靠的全双工的字节流。TCP套接口是流套接口(stream socket)的一种。 TCP关心诸如确认、超时和重传等具体细节。大多数因特网应用程序使用TCP。注意,TCP既可以使用IPv4,也可以使用IPv6。
    UDP,用户数据报协议(User Datagram Protocol)。UDP是一种无连接协议。UDP套接口是数据报套接口(datagram socket)的一种。UDP数据报不能保证最终到达它们的目的地。与 TCP一样,UDP既可以使用IPv4,也可以使用IPv6。
    SCTP,流控制传输协议(Stream Control Transmission Protocol)。SCTP是一个面向连接的提供可靠全双工关联(association)的协议。“关联”一词用于指称SCTP中的连接,因为SCTP是多宿的,每个关联涉及的源宿两端各有一组IP地址和单个端口号。SCTP提供消息服务,也就是维护来自应用层的记录边界。与TCP和UDP一样,SCTP既可以使用IPv4,也可以使用IPv6,而且能够在同一个关联中同时使用它们。
    ICMP,网际控制消息协议(Internet Control Message Protocol)。ICMP处理路由器和主机间的错误和控制消息。这些消息一般由TCP/IP网络软件自身(而不是用户进程)产生和处理,不过图中展示的ping和traceroute程序也使用ICMP。有时我们称这个协议为ICMPv4,用于区别ICMPv6。
    IGMP,网际组管理协议(Internet Group Management Protocol)。IGMP用于多播(第21 章),它在IPv4中是可选的。
    ARP,地址解析协议(Address Resolution Protocol)。ARP把IPv4地址映射到硬件地址 (如以太网地址)。ARP一般用于广播网络,如以太网、令牌环网和FDDI,而不用于点到点网络。
    RARP,反向地址解析协议(Reverse Address Resolution Protocol)。RARP把硬件地址映射到IPv4地址。它有时用于无盘节点(如X终端)的引导。
    ICMPv6,网际控制消息协议,版本6(Internet Control Message Protocol version 6) 。ICMPv6综合了ICMPv4、IGMP和ARP的功能。
    BPF,BSD分组过滤器(BSD packet filter)。它为进程提供访问链路层数据的接口,一般在源自Berkeley的内核中可以找到它。
    DLPI,数据链路提供者接口(datalink provider interface)。该接口也提供访问数据链路层的能力,一般由SVR4内核提供。
    所有网际协议由一个或多个称为RFC(Request for Comments)的文档定义,这些RFC就是它们的正式规范。习题2.1的解答说明如何获得各个RFC。
    我们使用术语“IPv4/IPv6主机”或“双栈主机”表示同时支持IPv4和IPv6的主机。
    有关TCP/IP协议的其他细节见TCPv1。TCP/IP在4.4BSD中的实现见TCPv2。

2.3    UDP:用户数据报协议

    UDP是一个简单的传输层协议,在RFC 768[Postel 1980]中详细说明。应用进程往一个UDP 套接口写入一个消息,该消息接着被封装(encapsulating)进一个UDP数据报,该UDP数据报又进而被封装成一个IP数据报,然后发送到目的地。UDP不保证UDP数据报到达其最终目的地,不保证各个数据报的先后顺序跨网络保持不变,也不保证每个数据报只到达一次。
    我们使用UDP进行网络编程所碰到的问题是缺乏可靠性。如果一个数据报尽管到达其最终目的地,但是校验和检测发现错误,或者就在网络传输途中被丢弃了,它就无法被投递给宿端 UDP套接口,也不会被源端自动重传。如果要确保一个数据报到达其目的地,我们必须在应用程序里建立一大堆的特性:来自对端的确认、本端的超时与重传,等等。
    每个UDP数据报都有一个长度。如果数据报正确地到达其最终目的地,那么数据报的长度将随数据一道传递给接收端应用进程。我们已经提到过TCP是一个字节流(byte stream)协议,没有任何记录边界(1.2节),这与UDP不同。
    我们也称UDP提供无连接的(connectionless)服务,因为UDP客户与服务器不必存在长期的关系。例如,一个UDP客户可以创建一个套接口并发送一个数据报给一个服务器,然后立即用同一个套接口发送另一个数据报给另一个服务器。同样,一个UDP服务器可以用同一个UDP套接口从若干个不同的客户一连串接收多个数据报。

2.4    TCP:传输控制协议

    向应用进程提供的TCP服务不同于UDP服务。TCP在RFC 793[Poste1 1981c]中详细说明,然后由RFC 1323[Jacobson, Braden, and Borman 1992]、RFC 2581[Allman, Paxson, and Stevens 1999]、RFC 2988[Paxson and Allman 2000]和RFC 3390[Allman, Floyd, an d Partridge 2002]加以更新。首先,TCP提供客户与服务器的连接(connection)。一个TCP 客户建立与一个给定服务器的连接,并跨该连接与那个服务器交换数据,然后终止连接。
    其次,TCP提供可靠性(reliability)。当TCP向另一端发送数据时,它要求对端返回一个确认。如果确认没有收到,TCP自动重传数据并等待更长时间。在数次重传失败后,TCP才放弃。重传数据所花的总时间传统上是4~10分钟(与实现有关)。
    注意,TCP并不保证数据一定会被对方端点接收,因为这是不可能做到的。如果可能的话,TCP就把数据投递到对方端点,否则就(通过放弃重传并中止连接这一手段)通知用户。这么说来,TCP也不能被描述成是100%可靠的协议;它提供的是数据的可靠投递或故障的可靠通知。
    TCP含有用于动态估算客户到服务器往返所花时间(round trip time,简称RTT)的算法,因此它知道等待一个确认需要多少时间。举例来说,RTT在一个局域网上大约是几毫秒,而跨越广域网则可能是数秒钟。另外,TCP还持续估算一个给定连接的RTT,这是因为RTT受网络传输拥挤程度的变化而变化。
    第三,TCP通过给所发送数据的每一个字节关联一个序列号进行排序(sequencing)。举例来说,假设一个应用进程写2048字节到一个TCP套接口,导致TCP发送2个分节:第1个分节所含数据的序列号为1~1024,第2个分节所含数据的序列号为1025~2048(分节是TCP传递给IP的数据单元)。如果这些分节非顺序到达,接收端的TCP将根据它们的序列号重新排序,再把结果数据传递给应用进程。如果TCP接收到重复的数据(譬如说对端认为一个分节已丢失并因而重传,而它并没有真正丢失,只是刚才网络通信过于拥挤),它也可以判定数据是重复的(根据序列号),从而把它丢弃掉。
    UDP提供不可靠的数据报传送。UDP本身不提供确认、序列号、RTT估算、超时及重传等机制。如果一个UDP数据报在网上被复制,两份拷贝就可能都递送到接收端的主机。同样,如果一个UDP客户发送两个数据报到同一个目的地,它们可能被网络重新排序,颠倒顺序后到达目的地。UDP应用程序必须处理这些情况,我们将在22.5节中详细介绍。
    第四,TCP提供流量控制(flow control)。TCP总是告诉对端它能够接收多少字节的数据,这称为通告窗口(advertised window)。该窗口在任何时刻都指出接收缓冲区中的可用空间,从而确保发送端发送的数据不会溢出接收缓冲区。该窗口时时刻刻动态地变化着:当接收来自发送端的数据时,窗口大小减小,而当接收端应用进程从缓冲区中读取数据时,窗口大小增大。窗口的大小减小到0是有可能的:TCP的接收缓冲区满,它必须等待应用进程从这个缓冲区读取数据后才能再接收从发送端来的数据。
    UDP不提供流量控制。UDP按发送端的速率发送数据,而不管接收端的缓冲区是否装得下,见8.13节。
    最后,TCP的连接是全双工的(full duplex)。这意味着在给定的连接上应用进程在任何时刻既可以发送也可以接收数据。因此,TCP必须跟踪每个方向数据流的状态信息,如序列号和通告窗口的大小。
    UDP可以是全双工的。

2.5    SCTP:流控制传输协议

    SCTP提供的服务与UDP和TCP提供的类似。SCTP在RFC 2960[Stewart et al. 2000]中详细说明,并由RFC 3309[Stone, Stewart, and Otis 2002]加以更新。RFC 3286[Ong and Y oakum 2002]给出了SCTP的简要介绍。SCTP在客户和服务器之间提供关联,并像TCP那样给应用层提供可靠性、排序、流量控制以及全双工的数据传输服务。SCTP中使用“关联(assoc iation)”取代“连接(connection)”是为了避免这样的内涵:一个连接只涉及两个IP地址之间的通信。一个关联指代可能因为多宿而涉及不止一个地址的两个系统之间的一次通信会话。
    与TCP不同的是,SCTP是面向消息的(message oriented)。它提供各个记录的按序投递服务。与UDP一样,由发送端写入SCTP的每个记录的长度随数据一道传递给接收端应用进程。
    SCTP能够在所连接的端点之间提供多个流,每个流各自可靠地按序投递消息。一个流上某个消息的丢失不会阻塞同一关联其他流上消息的投递。这种做法与TCP正好相反,就TCP而言,在单一字节流中任何位置的字节丢失都将阻塞该连接上其后所有数据的投递,直到该丢失被修复为止。
    SCTP还提供多宿特性,使得单个SCTP端点能够支持多个IP地址。该特性可以增强针对网络故障的健壮性。一个端点可以有多个冗余的网络连接,每个连接所在的网络有各自接入因特网基础设施的通途。通过把端点切换到已与SCTP关联相关的另一个地址,SCTP能够绕过发生在跨越因特网途中某个网络或通路上的故障。
    类似的健壮性在路由协议的辅助下也可以从TCP中获取。举例来说,域内BGP连接(iBGP)往往把赋予路由器内某个虚拟接口的多个地址用作TCP连接的端点。这些域内路由协议确保两个路由器之间只要存在一条路径,该路径就会被用上;要是连接所用地址属于某个变得不工作的实际接口,这一点就不可能做到。SCTP的多宿特性允许主机(而不仅仅是路由器)也多宿,而且允许多宿跨越不同的业务供应商发生,而基于路由的TCP多宿方法不允许跨越多家业务供应商。
    为帮助大家理解connect、accept和close函数并使用netstat调试TCP应用程序,我们必须了解TCP连接如何建立和终止以及TCP的状态转换图。

三路握手

    下述步骤建立一个TCP连接:
    1.服务器必须准备好接受外来的连接。这通过调用socket、bind和listen函数来完成,称为被动打开(passive open)。
2.客户通过调用connect进行主动打开(active open)。这引起客户TCP发送一个SYN( 表示同步)分节,它告诉服务器客户将在(待建立的)连接中发送的数据的初始序列号。一般情况下SYN分节不携带数据,它只含有一个IP头部、一个TCP头部及可能有的TCP选项。
    3.服务器必须确认客户的SYN,同时自己也得发送一个SYN分节,它含有服务器将在同一连接中发送的数据的初始序列号。服务器以单个分节向客户发送SYN和对客户SYN的ACK(表示确认)。
    4. 客户必须确认服务器的SYN。连接建立过程至少需要交换三个分组,因此称之为TCP的三路握手(three way handshake)。图2.2中展示了这三个分节。


图2.2TCP的三路握手

    图2.2给出的客户的初始序列号为J,而服务器的初始序列号为K。在ACK里的确认号为发送这个ACK的一端所期待的对端的下一个序列号。因为SYN只占一个字节的序列号空间,所以每一个SYN的ACK中的确认号都是相应的初始序列号加1。类似地,每一个FIN(表示结束)的 ACK中的确认号为FIN的序列号加1。

    建立TCP连接的日常系统类比是电话系统[Nemeth 1997]。socket函数等同于有电话可用。bind用于告诉其他人你的电话号码,让他们可以向你打电话。listen是打开电话振铃,它使你可以听到一个外来的电话。connect要求你知道对方的电话号码并拨打它。accept是被呼叫回电话。从accept返回客户的标识(即客户的IP地址和端口号)类似于让电话机的呼叫者ID 功能部件显示打电话人的电话号码。然而有点不同的地方是:从accept返回客户的标识是在建立连接以后,而呼叫者ID功能部件显示打电话人的电话号码是在我们选择接或不接电话之前。如果使用域名系统(第11章),那么提供了一种类似于电话簿的服务。getaddrinfo类似于在电话簿查找个人的电话号码。
getnameinfo则类似于有一本按照电话号码而不是用户名排序的电话簿。

   
TCP选项

    每一个SYN可以含有若干个TCP选项。通常使用的选项有:
    ·MSS选项。TCP发送的SYN中带有这个选项是通知对端它的最大分节大小MSS(maximum segment size),即它能接受的每个TCP分节中的最大数据量。发送端TCP使用接收端的MSS 值作为所发送分节的最大大小。我们会看到如何使用TCP_MAXSEG套接口选项获取与设置这个TCP选项(7.9节)。
    ·窗口规模选项。TCP两端能够通知对端的最大窗口大小是65535,因为TCP头部相应的字段只占16位。但是当今因特网上业已普及的高速连接(每秒45兆位或更快,见RFC1323 [Jacobson,Braden,and Borman 1992])或长延迟的路径(卫星链路)要求有更大的窗口以获得尽可能最大的吞吐量。这个新选项指定TCP头部的通告窗口必须扩大(即左移)的位数(0~14),因此所提供的最大窗口几乎是1G字节(65535×214)。两个端系统必须都支持这个选项,否则不具备扩大窗口规模能力。在7.5节我们将看到套接口选项SO_RCVBUF如何影响该选项。

    为提供与不支持这个选项的较早实现间的互操作性,应用如下的规则。TCP可以作为主动打开的一部分内容随它的SYN发送该选项,但只有对端也随它的SYN发送该选项时,它才能扩大窗口的规模。类似地,服务器的TCP只有接收到随客户的SYN来的这个选项时,它才能发送该选项。本逻辑假定实现会忽略它们不理解的选项。这是要求的,而且相当普遍,然而不幸的是无法保证所有实现都是这样。

    ·时间戳选项。这个选项对高速连接是必要的,它可以防止由失而复得的分组可能造成的数据损坏。因为它是新选项,所以其处理类似于窗口规模选项。作为网络编程人员,对于这个选项我们没有必要担心。
    TCP的大多数实现都支持这些选项。其中后两个选项有时也称为“RFC 1323选项”,因为它们是在RFC1323[Jacobson,Braden,and Borman 1992]中说明的。它们也称为“长胖管道 ”选项,因为高带宽或长延迟的网络被称为“长胖管道(long fat pipe)”。TCPv1的第24章对这些新选项有详细的叙述。

TCP连接终止

    TCP建立一个连接需三个分节,终止一个连接则需四个分节。
    1.某个应用进程首先调用close,我们称这一端执行主动关闭(active close)。这一端的TCP于是发送一个FIN分节,表示数据发送完毕。
    2.接收到FIN的另一端执行被动关闭(passive close)。这个FIN由TCP确认。它的接 收也作为文件结束符传递给接收端应用进程(放在已排队等候该应用进程接收的任何其他数 据之后),因为FIN的接收意味着应用进程在相应连接上再也接收不到额外数据。
    3.一段时间后,接收到文件结束符的应用进程将调用close关闭它的套接口。这导致它的TCP也发送一个FIN。 4.接收到这个FIN的原发送端TCP(即执行主动关闭的那一端)对它进行确认。因为每个方向都需要有一个FIN和一个ACK,所以一般需要四个分节。我们使用限定词“一般 ”是因为:有时步骤1的FIN随数据一起发送;另外,执行被动关闭那一端的TCP在步骤2和3 发出的ACK与FIN也可以合并成一个分节。图2.3说明了这些分组的交换过程。
FIN占据1个字节的序列号空间,这与SYN相同。所以每个FIN的ACK确认号是这个FIN的序列号加1。

图2.3TCP连接关闭时的分组交换
    在步骤2与步骤3之间可以有从执行被动关闭端到执行主动关闭端的数据流。这称为半关闭(h alf close),我们将在6.6节随shutdown函数再详细介绍。套接口关闭时,每一端TCP都要发送一个FIN。这种情况在应用进程调用close时会发生,然而在进程终止时,所有打开的描述字将自愿(调用exit或从main函数返回)或不自愿(进程收到一个终止本进程的信号)地关闭,此时仍然打开的TCP连接上也会发出一个FIN。
    图2.3指出客户执行主动关闭,然而不管是客户还是服务器都可以执行主动关闭。通常情况是客户执行主动关闭,但某些协议如HTTP(超文本传送协议)则是服务器执行主动关闭。 

TCP状态转换图

    TCP连接的建立和终止可以用状态转换图(state transition diagram)来说明,见图2.4。
    TCP为一个连接定义了11种状态,并且TCP规则规定如何基于当前状态及在该状态下所接收的分节从一个状态转换到另一个状态。举例来说,当应用进程在CLOSED状态下执行一个主动打开时,TCP将发送一个SYN并从CLOSED状态转换成SYN_SENT状态。如果该TCP接着接收到一个捎带ACK的SYN,它将发送一个ACK并转换成ESTABLISHED状态。这个最终状态是绝大多数数据传送发生的状态。
    有两个外向箭头引出自ESTABLISHED状态的连接终止处理。如果应用进程在接收到文件结束符前调用close(主动关闭),则转换成FIN_WAIT_1状态。如果在ESTABLISHED状态下应用进程接收到FIN,则转换成CLOSE_WAIT态。  
    我们用实线表示客户的状态转换,虚线表示服务器的状态转换。注意我们没有提到的两个转换:一个为同时打开(当两端几乎同时发送SYN并且这两个SYN在网络中彼此交错时),另一个为同时关闭(当两端同时发送FIN时)。
TCPv1的第18章将讨论这两种情况,它们是可能发生的,不过非常罕见。
    展示状态转换图的理由之一是给出11种TCP状态的名称。netstat命令的输出包括这些状态,它是调试客户/服务器应用程序的有用的工具。在第5章中我们将使用netstat去监视这些状态。

观察分组

    图2.5说明一个完整的TCP连接所发生的实际分组交换情况:建立连接、传送数据和终止连接。图中展示了每个端点所历经的TCP状态。

图 2.4 TCP状体转换图
    本例中客户通告的MSS(最大分节大小)值是536(表明该客户只实现了最小重组缓冲区大小),而服务器通告的MSS值为1460(以太网上IPv4的典型值)。不同方向的MSS可以不相同(见习题2.5)。
    一旦连接建立,客户就构造一个请求并发送给服务器。这里我们假设该请求适合于单个TCP分节(即请求大小小于服务器通告的1460字节的MSS)。服务器处理该请求并发送应答,我们假设应答也适合于单个分节(本例即小于536)。这两个数据分节在图中使用粗箭头表示。注意,服务器对客户请求的确认是伴随其应答发送的。这种做法称为捎带(piggybacking),它通常在服务器处理请求并产生应答的时间少于200毫秒时发生。如果服务器耗用更长时间,例如1秒钟,那么我们将看到先是确认后是应答。(TCP数据流机理在TCPv1的第19章和第20章中详细叙述)

图 2.5 TCP连接中的分组交换
    以后4个分节终止连接。注意:执行主动关闭的那一端(客户端)进入TIME_WAIT状态,我们将在下一节讨论这种情况。
    图2.5中值得注意的是,如果连接的整个目的仅仅是发送一个单一分节的请求和接收一个单一分节的应答,那么使用TCP有8个分节的额外开销。如果改用UDP,那么只需交换两个分组:请求和应答。然而从TCP切换到UDP将丧失TCP提供给应用进程的全部可靠性,迫使可靠服务的一大堆细节从传输层(TCP)转移到UDP应用进程。TCP提供的另一个重要特性即拥塞控制也得由UDP应用进程处理。尽管如此,许多网络应用还是使用UDP,因为它们需要交换的数据量较少,而UDP避免了TCP连接建立和终止的额外开销。
阅读(2494) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~