《UNIX网络编程卷1》笔记 简介部分

UNIX网络编程必读书籍——《UNIX网络编程卷1》 简介部分 笔记

传输层:TCP、UDP和SCTP

概述

UDP是一个简单的、不可靠的数据报协议,而TCP是一个复杂、可靠的字节流协议。SCTP与TCP类似之处在于它也是一个可靠的传输协议,但它还提供消息边界、传输级别多宿(multihoming)支持以及将头端阻塞( head-of-line blockin)减少到最小的一种方法。我们必须了解由这些传输层协议提供给应用进程的服务,这样才能弄清这些协议处理什么,应用进程中又需要处理什么。

总图

TCP/IP协议族除了TCP和IP这两个协议以外,还有许多其他的成员:

用户数据报协议(UDP)

应用进程往一个UDP套接字写入一个消息,该消息随后被封装( encapsulating)到一个UDP数据报,该UDP数据报进而又被封装到一个IP数据报,然后发送到目的地。UDP不保证UDP数据报会到达其最终目的地,不保证各个数据报的先后顺序跨网络后保持不变,也不保证每个数据报只到达一次

使用UDP进行网络编程所遇到的问题是它缺乏可靠性。如果一个数据报到达了其最终目的地,但是校验和检测发现有错误,或者该数据报在网络传输途中被丢弃了,它就无法被投递给UDP套接字,也不会被源端自动重传。

每个UDP数据报都有一个长度。如果一个数据报正确地到达其目的地,那么该数据报的长度将随数据一道传递给接收端应用进程。同时TCP是一个字节流(byte-stream)协议,没有任何记录边界,这一点不同于UDP

传输控制协议(TCP)

首先,TCP客户先与某个给定服务器建立一个连接,再跨该连接与那个服务器交换数据,然后终止这个连接。

其次,TCP还提供了可靠性( reliability)。当TCP向另一端发送数据时,它要求对端返回个确认。如果没有收到确认,TCP就自动重传数据并等待更长时间。在数次重传失败后,TCP才放弃。TCP通过给其中每个字节关联一个序列号对所发送的数据进行排序,从而达到确认重传的目的。

再次,TCP提供流量控制( flow control)。TCP总是告知对端在任何时刻它一次能够从对端接收多少字节的数据,这称为通告窗口( advertised window)。在任何时刻,该窗口指出接收缓冲区中当前可用的空间量,从而确保发送端发送的数据不会使接收缓冲区溢出。该窗口时刻动态变化。

最后,TCP连接是全双工的( full-duplex)。这意味着在一个给定的连接上应用可以在任何时刻在进出两个方向上既发送数据又接收数据。因此,TCP必须为每个数据流方向跟踪诸如序列号和通告窗口大小等状态信息。当然UDP也是全双工的。

流控制传输协议(SCTP)

SCTP提供的服务与UDP和TCP提供的类似。SCTP在客户和服务器之间提供关联( association),并像TCP那样给应用提供可靠性、排序、流量控制以及全双工的数据传送。SCTP中使用“关联”一词取代“连接”是为了避免这样的内涵:一个连接只涉及两个IP地址之间的通信。一个关联指代两个系统之间的一次通信,它可能因为SCTP支持多宿而涉及不止两个地址

与TCP不同的是,SCTP是面向消息的(message-oriented)。它提供各个记录的按序递送服务。与UDP一样,由发送端写入的每条记录的长度随数据一道传递给接收端应用SCTP能够在所连接的端点之间提供多个流,每个流各自可靠地按序递送消息。

TCP连接的建立和终止

三次握手

TCP选项

每一个SYN可以含有多个TCP选项,下面是一些常用的:

  • MSS选项。发送SYN的TCP一端使用本选项通告对端它的最大分节大小( maximunsegment size)即MSS,也就是它在本连接的每个TCP分节中愿意接受的最大数据量。
  • 窗口规模选项。TCP连接任何一端能够通告对端的最大窗口大小是65535,因为在TCP首部中相应的字段占16位。
  • 时间戳选项。这个选项对于高速网络连接是必要的,它可以防止由失而复现的分组°可能造成的数据损坏。

TCP 连接终止

TCP 状态转换图

TCP建立连接和连接终止的操作可以用状态转换图来进行表示,其中一共有11种状态

观察分组

下图展示了一个完整的TCP连接所发生的实际分组交换情况,包括建立连接、数据传送和连接终止3个阶段

TIME_WAIT状态

TIME WAIT状态有两个存在的理由:

  1. 可靠地实现TCP全双工连接的终止,当最终的ACK丢失的时候,客户端必须重新发出这个ACK,在这之前客户端必须维护之前的状态,以允许重新发送那个ACK
  2. 允许老的重复分节在网络中消逝,防止影响后面的TCP连接

端口号

任何时候,多个进程可能同时使用TCP、UDP和SCTP这3种传输层协议中的任何一种。这3种协议都使用16位整数的端口号(port number)来区分这些进程

当一个客户想要跟一个服务器联系时,它必须标识想要与之通信的这个服务器。TCP、UDP和SCTP定义了一组众所周知的端口(well- known port),用于标识众所周知的服务。

另一方面,客户通常使用短期存活的临时端口( ephemeral port)。这些端口号通常由传输层协议自动赋予客户。客户通常不关心其临时端口的具体值,而只需确信该端口在所在主机中是唯一的就行。传输协议的代码确保这种唯一性

端口号被划分成以下3段:

  1. 众所周知的端口为0~1023。这些端口由IANA分配和控制。可能的话,相同端口号就分配给TCP、UDP和SCTP的同一给定服务。
  2. 已登记的端口( registered port)为1024~49151。这些端口不受IANA控制,不过由LANA登记并提供它们的使用情况清单,以方便整个群体。
  3. 49152~65535是动态的( dynamic)或私用的( private)端口。IANA不管这些端口。它们就是我们所称的临时端口。(49152这个魔数是65536的四分之三。)

套接字对

一个TCP连接的套接字对( socket pair)是一个定义该连接的两个端点的四元组:本地P地址、本地TCP端口号、外地IP地址、外地TCP端口号。套接字对唯一标识一个网络上的每个TCP连接。

TCP端口号与并发服务器

并发服务器中主服务器循环通过派生一个子进程来处理每个新的连接

我们必须在服务器主机上区分监听套接字和已连接套接字( connected socket)。注意已连接套接字使用与监听套接字相同的本地端口(21)。

从上面我们可以看到:TCP无法仅仅通过查看目的端口号来分离外来的分节到不同的端点。它必须查看套接字对的所有4个元素才能确定由哪个端点接收某个到达的分节。

缓冲区大小和限制

TCP输出

每一个TCP套接字有一个发送缓冲区,我们可以使用 SO_SNDBUF套接字选项来更改该缓冲区的大小。当某个应用进程调用 write时,内核从该应用进程的缓冲区中复制所有数据到所写套接字的发送缓冲区。如果该套接字的发送缓冲区容不下该应用进程的所有数据(或是应用进程的缓冲区大于套接字的发送缓冲区,或是套接字的发送缓冲区中已有其他数据),该应用进程将被投入睡眠。这里假设该套接字是阻塞的,它是通常的默认设置。内核将不从wite系统调用返回,直到应用进程缓冲区中的所有数据都复制到套接字发送缓冲区。因此,从写一个TCP套接字的 write调用成功返回仅仅表示我们可以重新使用原来的应用进程缓冲区,并不表明对端的TCP或应用进程已接收到数据

UDP输出

任何UDP套接字都有发送缓冲区大小(我们可以使用 SO SNDBUE套接字选项更改它,见75节),不过它仅仅是可写到该套接字的UDP数据报的大小上限。如果一个应用进程写一个大于套接字发送缓冲区大小的数据报,内核将返回该进程一个 EMSGSIZE错误。既然UDP是不可靠的,它不必保存应用进程数据的一个副本,因此无需一个真正的发送缓冲区。

常见因特网应用的协议使用