全部博文(58)
分类: 系统运维
2009-05-19 05:22:40
输层: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中展示了这三个分节。