1.3 传输层机制
IP协议定义了OSI模型中的网络层协议。其实仍有一些其他的网络层协议,但我只聚焦在IP上,因为它是目前最流行的网络层协议。OSI模型中,网络层之上的是传输层。正如您所料,传输层有它自己的一组协议簇。我们对两个传输层的协议比较感兴趣:UDP和TCP。本节将分别详细介绍这些协议。
1.3.1 UDP
用户数据报协议(User Datagram Protocol,UDP)是无连接协议,用于DNS查询、SNMP(简单网络管理协议)和RADIUS(远程用户拨号认证系统)等。作为无连接协议,UDP协议的工作方式类似于“发送,然后遗忘”。客户端发送一个UDP数据包(有时称为数据报),并假设服务器将会收到该数据包。它依赖更高层的协议来将数据包按顺序组合。UDP的报头为8字节,见图1.4。
UDP报头以源端口号和目的端口号开始。接下来是包括数据在内的整个数据包的长度。显然,由于UDP报头的长度为8字节,因此这部分的最小值为8。最后的部分是UDP头部的校验和,它包括了数据和报头(对数据和报头一起计算校验和)。
1.3.2 TCP
TCP是传输控制协议(Transmission Control Protocol)的缩写,它是常用的面向连接的协议,常和IP一起使用。TCP作为面向连接的协议,意味着它向上层提供可靠的服务。回想本章前面举出的电话会话的例子。在这个类比中,两个应用程序想要使用TCP进行通信则必须建立一个连接(有时被称为会话)。TCP报头见图1.5。
就像您在图1.5中看到的那样。20字节的TCP头部明显比本章的其他协议头部更加复杂。与UDP协议相似的是,TCP头部也以源端口和目的端口开始。而源端口、目的端口与发送者、接收者的IP地址相结合唯一确定了这个连接。TCP报头有32比特的序列号和32比特的确认序列号。TCP是面向连接的协议并且提供可靠的服务。序列号和确认序列号是(但不是唯一)用于提供可靠性的基础机制。随着数据从传输层向下传递,TCP会将数据划分成它认为合适的大小。这些分片即是TCP报文段(segment)。在TCP沿协议栈向下传递数据的过程中,它创建了序列号,指明了给定报文段中数据的第一个字节。在通信的另一端,接收者发送一个确认消息,指明它已经收到的报文段。发送者维护一个定时器,一旦一个确认序列号未按时接收,该数据段则会被重新发送。
TCP保障可靠性的另一个机制是在报头和数据上计算的校验和。如果接收者接收到的发送者发送的报头中的校验和与接收者计算的校验和不匹配,则接收者将不会发送确认消息。如果确认消息在传输中丢失,则发送者可能会使用同样的序列号再发送一遍报文段。在这种情况下,接收者将简单地丢弃重复的报文段。
一个4比特的域(此处指数据偏移)被用来表示包括所有选项在内的报头长度(单位是32比特)。TCP报头中有许多独立的比特标志:URG、ACK、PSH、RST、SYN、FIN、NS、CWR和ECE。对于这些标志的描述见表1.4。
16比特的窗口域提供了滑动窗口机制。接收者设置窗口值以指明接收者准备接收的数据大小(从确认序号开始的大小)。这是TCP流控制中的一种。
16比特的紧急指针指明了紧急数据结束处的偏移量,该偏移量从序列号开始。它能让发送者指明偏移量内的数据是紧急数据,应该以紧急方式进行处理。这个指针可以和PSH标志结合使用。
现在您对TCP报头有了直观的感觉了,是时候看看TCP连接是如何建立和终止的了。
TCP连接
UDP是无连接的协议,而TCP却是面向连接的协议。UDP中没有连接的概念,在UDP数据报中只有发送者和接收者。对于TCP而言,连接的任一方都可以发送或接收数据,也可以同时接收和发送。TCP是全双工(full-duplex)的协议。建立一个TCP连接的过程有时被称为三次握手(three-way handshake),很快您就会看到这个称呼的来由。
由于是面向连接的协议,在建立TCP连接时,会发生一个特定的过程。在该过程中,存在许多TCP连接的状态。连接建立的过程和相应的状态会在接下来的部分详细介绍。
要发起通信的一方(客户端)会在发送的TCP报文中设置SYN标志、初始序列号(Initial Sequence Number, ISN)以及它要通信的另一方的端口号,通常连接的另一方是服务器。该报文通常被称为SYN数据包或者SYN报文段,此时该TCP连接处于SYN_SENT状态。
此连接的服务器一方会发送一个同时设置了SYN标志和ACK标志的TCP报文段作为应答。此外,服务器还会将确认序列号设置为客户端所发送的初始序列号加一。该报文通常被称为SYN-ACK数据包或SYN-ACK报文段,此时该TCP连接处于SYN_RCVD状态。
接下来,客户端会应答SYN-ACK数据包:发送一个设置了ACK标志,并且确认序列号为SYN-ACK序列号加一的报文段。至此,三次握手已经结束,该连接已建立,进入了ESTABLISHED状态。
与初始化连接的协议(这里的协议指的是建立连接的过程)相对应,还有一个终止连接的过程。用于终止TCP连接的过程与建立连接的三步相对,共有四个步骤。多出的一个步骤来自于TCP连接的全双工特性,因为任何一边都可能在任何时候发送数据。
通过发送设置了FIN标志的TCP报文段,TCP连接的某一方可以关闭该方向的连接。连接的任何一方都可以发送FIN标志,以表明它已经将数据发送完毕。而连接的另一方则可以继续发送数据。然而,实际上,当FIN被接收时,连接的终止过程通常将开始。在下面的讨论中,我把想要终止连接的一方称为客户端。
终止过程从客户端发送一个设置了FIN标志的报文段开始,此时服务器端的状态为CLOSE_WAIT,而客户端的状态为FIN_WAIT_1。在服务端接收到FIN后,服务端将向客户端回复ACK,同时将序列号加一。此时,客户端进入FIN_WAIT_2状态。服务端同时向它的高层协议指出连接已终止。接下来,服务端将关闭连接,这会导致一个设置了FIN标志的报文段被发送到客户端,然后服务端将进入LAST_ACK状态,而客户端则进入TIME_WAIT状态。最后,客户端发送报文段确认此FIN标志(设置ACK标志,并将序列号加一),然后该连接便进入了CLOSED状态。由于TCP连接可以被任何一方终止,因此,一个TCP连接能够以半关闭的状态存在,此时一端已发起了FIN终止序列,但另一端则并没有这样做。
TCP连接也能够由任何一方发送一个设置了重置(RESET)标志的报文段而终止。这通知连接的另一端使用一种中止的方式来释放连接。它与通常的结束TCP连接的那种常被称为有序释放的方式不同。
TCP连接序列中有一个可选择部分是最大报文段长度(Maximum Segment Size,MSS)。MSS是通信的双方各自所能接收的最大的数据块的大小。由于MSS是连接的两方所能接收到的最大的大小,通常发送比MSS小一些的数据块更合适。一般而言,您应该考虑使用一个大一些的MSS,然而请牢记应避免分片(会在IP层进行),因为分片会增加系统开销(数据包分片需要额外的IP和TCP报头的字节)。