总图
注:
- tcpdump网络应用,或者使用BSD分组过滤器(BPF packet filter),或者使用数据链路提供者接口(datalink provider interface, DLPI)直接与数据链路进行通信
- 右边的9个带有虚线标记的API,它们通常为套接字或XTI
- 访问BPF或DLPI的接口不需要使用套接字或XTI
- 但在Linux中,Linux使用SOCK_PACKET的特殊套接字,提供对数据链路的访问
- traceroute使用两种套接字:IP套接字用于IP访问,ICMP套接字用于ICMP访问
- 使用“IPv4/IPv6主机”或“双栈主机”。表示同时支持IPv4和IPv6的主机
IPv4
- 网际协议版本4。使用32位服务。
- 为TCP、UDP、SCTP、ICMP和IGMP提供分组递送服务
IPv6
- 网际协议版本6。作为IPv4的一个替代品而设计。
- 使用128位的地址。为TCP、UDP、SCTP、ICMPv6提供分组递送服务
TCP
- 传输控制协议。面向连接的协议,为用户进程提供,全双工字节流。是一种流套接字
- TCP关心确认、超时、重传之类的细节
UDP
- 用户数据报协议。一个无连接的协议
- UDP套接字是一种数据报套接字
- UDP数据报不能保证最终到达他们的目的地
SCTP
- 流控制传输协议。可靠的全双工关联的面向连接的协议(使用“关联”来表示SCTP中的连接)
- SCTP是多宿的,每个关联的两端均涉及一组记录IP地址和一个端口号
- SCTP提供消息服务,维护来自应用层的记录边界
- SCTP可以在同一个关联中即使用IPv4又使用IPv6
IGMP
- 网际组管理协议,用于多播,在IPv4中是可选的
ARP
- 地址解析协议
- ARP把一个IPV4地址映射一个硬件地址
- ARP通常用于以太网、令牌环网、FDDI等广播地址,在点到点的网络中不需要
RARP
- 反向地址解析协议
- 将一个硬件地址,映射成一个IPv4地址;有时用于无盘节点的情况
ICMPv6
- 网际控制消息协议版本6
- 综合了ICMPv4、IGMP和ARP的功能
BPF
- BSD分组过滤器
- 提供对数据链路层的访问功能,通常可以在源自Berkeley的内核中找到
DLPI
- 数据链路提供者接口
- 提供对数据链路层的访问功能,通常随SVR4内核提供
用户数据报协议UDP
- 传输层协议
- 应用进程往一个UDP套接字中写入一个消息,该消息随后封装到一个UDP数据报;该UDP数据报随后又被封装到一个IP数据报中,然后发送到目的地
- UDP不保证UDP数据报会到达其最终目的,不保证各个数据报到达后会保持顺序不变,也不保证每个数据报只到达一次
- 如果想要确保一个数据报到达其目的,可以在程序中添加一些特性:来自对端的确认,本地的超时,重传等
- 每个UDP数据报都有一个长度。当其到达目的地后,将它的数据和长度一并交给应用进程,UDP在发送时,标明自身的大小
- UDP提供无连接的服务。一个UDP服务器可以用同一个UDP套接字从若干个不同的客户接收数据报,每个客户一个数据报
传输控制协议TCP
- TCP提供客户与服务器之间的连接
- TCP客户先与某个服务器建立一个连接,再跨该连接与那个服务器交换数据
- TCP还提供了可靠性,当TCP向另一端发送数据时,要求对端返回一个确认。如果没有确认,TCP就自动重传数据,并等待更长时间。在数次重传失败后,TCP才放弃,如此在尝试发送数据上所话的总时间一般为4~10分钟(一般看具体实现)
- TCP并不保证数据一定会被对方端点接收,它提供的是数据的可靠递送或故障的可靠通知
- TCP含有动态估算客户和服务器之间的往返时间(round-trip time,RTT);另外,因为RTT受网络流通各种变化因素影响,TCP还需要估算一个给定连接的RTT
- TCP通过给其中每个字节关联一个序列号对所发送的数据进行排序。如果这些分节(分节是TCP传递给IP的数据单元)非顺序到达,则接收端TCP会先根据它们的序列号进行排序,再将结果传递给应用。并且可以根据这个序列号进行判断数据是否是重复的
- TCP提供流量控制(flow control)。TCP总是能够告知对端在任何时刻它一次能够从对端接收多少个字节的数据,这称为通告窗口(advertised window)。在任何时刻,通告窗口指出接收缓冲区中当前可用的空间量,从而确保发送端发送数据不会使接收缓冲区溢出
- TCP连接是全双工的(full-duplex)。意味着,在一个给定连接上,应用可以在任何时刻在进出两个方向上即发送数据又接收数据
流控制传输协议SCTP
- SCTP在客户和服务器之间提供关联(association),并向TCP那样给应用提供可靠性、排序、流量控制、全双工的数据传送
- SCTP使用“关联”而不是“连接”是因为:一个连接只涉及两个IP地址之间的通信;一个关联指代两个系统之间的一次通信,它可能因为SCTP支持多宿而涉及不止两个地址
- SCTP是面向消息的(message-oriented)。它提供各个记录的按序递送服务,并在发送端写入的每条记录的长度随数据一起发送
- SCTP能够在所连接的端点之间提供多个流,每个流各自可靠地按序递送消息,一个流上某个消息的丢失,不会阻塞同一关联上的其他流的消息的递送
TCP连接的建立和终止
三路握手
- 建立一个TCP连接会发生一下情形:
- 服务器必须准备好接受外来的连接。这通常通过调用socket、bind、listen,来完成,称为被动打开(passive open)
- 客户通过connect发起主动打开(active open)。这导致客户TCP发送一个SYN(同步)分节,它告诉服务器客户将在(待建立的)连接中发送的数据的初始序列号。通常SYN分节不携带数据,其所在的IP数据报中只含一个PIP首部、一个TCP首部、可能含有的TCP选项
- 服务器必须确认(ACK)客户的SYN,同时字节也得发送一个SYN分节,它含有服务器将在同一连接中发送的数据的初始序列号。服务器在单个分节中发送SYN对客户SYN的ACK
- 客户必须确认服务器的SYN
- 这种交换,至少需要3个分组,因此称为TCP的三路握手(three_way handshake)
- ACK中的确认号,是发送这个ACK的一端所期待的下一个序列号
- 因为SYN占据一个字节的序列号空间,所以每一个SYN的ACK中的确认号就是该SYN的初始序列号加1
- 类似的,每一个FIN(表示结束)的ACK中的确认号为FIN的序列号加1
TCP选项
- 每一个SYN可以含有多个TCP选项,以下为常用TCP选项:
- MSS选项
- 发送SYN的TCP一端使用本选项,通告对端它的最大分节大小(maximum segment size)即MSS,也就是它在本链接的每个TCP分节中愿意接受的最大数据量
- 发送端TCP使用接收端的MSS值作为所发送分节的最大大小
- 可以使用TCP_MAXSEG套接字选项来提取和设置这个TCP选项
- 窗口规模选项
- TCP连接任何一端,能够通告对端的最大值是65535,因为在TCP首部中相应的字段占16位
- 然而现在需要更大的窗口提供以吞吐量。这个新选项指定TCP首部中的通告窗口必须扩大(即左移)的位数(0~14),因此所提供的最大窗口接近1G(65535*2^14)
- 可以通过使用SO_RCVBUF套接字选项影响这个TCP选项
- TCP可以作为主动打开的部分内容随它的SYN发送该选项,但是只在对端也随它的SYN发送该选项的前提下,才可以扩大自己窗口的规模
- 时间戳选项
- 这个选项对于高速网络连接是必须的。它可以防止由失而复现的分组可能造成的数据破坏
- 它是一个新的选项,也类似于窗口的规模选项的方式协商处理
TCP连接终止
- 某个应用进程先调用close,称该端主动关闭(active close)。该端的TCP发送一个FIN分节,表示数据发送完毕
- 接收到FIN的对端执行被动关闭(passive close)。这个FIN由TCP确认。它的接收作为一个文件结束符(end-of-file)传递给接收端应用程序,FIN意味着接收应用在相应连接上,再无任何数据可接收
- 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN
- 接收这个最终FIN的原发送端(执行主动关闭的那一端)确认这个FIN
- 既然每个方向都需要一个FIN和ACK,因此通常需要4个字节
- 在某些情况下,步骤1的FIN随数据一起发送
- 另外,步骤2和步骤3发送的分节都出自执行被动关闭的那一端,用可能被合并成一个分节
- 一个FIN占据1个字节的序列号空间,每个FIN的ACK确认号就是这个FIN序列号加1
- 在步骤2和步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为半关闭(helf-close)
- 当套接字关闭时,其所在段TCP发送一个FIN。这是应为一个Unix进程,无论是自愿、还是被动终止,其所有打开的描述符都被关闭,这也导致仍是打开的任何TCP连接也发出一个FIN
TCP状态转换图
- TCP为一个连接定义了11种状态
- 粗实线表示通常的客户端转换,粗虚实现表示通常的服务器转换
- 图中还有两种状态未表示:
- 同时打开(simultaneons open)。发生在两端几乎同时发送SYN并且这俩个SYN在网络中交错的情形
- 同时关闭(simultabeous close)。发生在两端几乎同时发送FIN的情形下
观察分组
- 本例中客户通告一个值为536的MSS(表明客户端只实现了最小重组缓冲区大小),服务器通告了一个值为1460的MSS(以太网上IPv4典型值)
- 服务器对客户请求的确认是伴随其应答发送的,这种做法称为捎带(piggybacking),它通常在服务器处理请求并产生应答的时间小于200ms时发生;如果服务器耗费更长的时间,例如是1s,那么我们会看到是先确认后应答
- 如果连接的目的仅仅只是发生一个单分节的请求和接收一个单分节的应答,那么使用TCP将会有8个分节的开销;如果改用UDP,那么只需要交换两个分组,一个承载请求,一个承载应答
TINE_WAIT状态
- 在TCP中,执行主动关闭的那端会经历该状态。该端点停留在这个状态的持续时间是最长分节生命期的两倍,简称:2MSL
- 任何TCP实现,都必须为MSL选择一个值,时间从30s到4m不等(常见)
- MSL是人格IP数据报在网络中存在的最长时间
- TCP需要正确处理迷途的重复分组(lost duplicate)或漫游的重复分组(wandering duplicate)
- TIME_WITE有两个存在理由
- 可靠的实现TCP全双工连接的终止
- 允许老的重复分节在网络中消逝
- 第一个原因可以通过假设最终ACK丢失来解释。服务器将会重新发送最终那个FIN,因此客户端必须维护自己的信息,以允许它再次发送最终的ACK。如果客户端不维护状态信息,则它将响应一个RST,该分节会被服务器解释成一个错误
- 如果TCP打算执行所有必要的工作以彻底终结某个连接两个方向上的数据流,则必须正确处理连接终止序列4个字节中的任何一个分节丢失的情况
- 第二个原因,是因为,TCP必须放置来自某个连接的老的重复分组在该连接以终止后再现,从而被误解成属于同一连接的某个新的化身。为了做到这一点,TCP不对处于TIME_WITE的连接发起新的化身
- 并且TIME_WAIT状态的持续时间是MSL的2倍,这就足以让某个方向上的分组最多存活MSL后被丢弃
SCTP关联的建立与终止
四路握手
- 服务器必须准备好接受外来的连接。这通常通过调用socket、bind、listen,来完成,称为被动打开(passive open)
- 客户通过connect或者发送一个隐式打开该关联的消息=进行主动打开==(active open)。这导致客户SCTP发送一个INIT消息(初始化),它告诉服务器客户的IP地址清单、初始序列号、用于标识本关联的所有分组的起始标识、客户请求的外出流的数目、客户能够支持的外来流的数目
- 服务器以一个INIT ACK确认客户的INIT消息,其中含有服务器的IP地址清单、初始序列号、起始标记、服务器请求的外出流数目、服务器能够支持的外来流数目、一个状态cookie。状态cookie包含服务器用于确认本关联有效所需的所有状态,它是数字化签名过的,以确保其有效性
- 客户以一个COOKIE ECHO消息回射服务器的状态cookie。除COOKIE ECHO外,该消息可能在同一个分组中还捆绑了用户数据
- 服务器以一个COOKIE ACK消息确认客户回射的cookie是正确的,与本关联是建立的,该消息可能在同一个分组中还捆绑了用户数据
- INIT承载一个验证标记Ta和一个初始序列号J
- 在关联的有效期内,验证标记Ta必须在对端发送的每一个分组中出现
- 初始系列号J作为承载用户数据的DATA块的起始序列号。
- 对端在INIT ACK中承载一个验证标识Tz
- 在关联的有效期内,验证标记Tz必须在对端发送的每一个分组中出现
- 除了验证标记Tz和初始序列号K外,INIT的接收端还为响应的INIT ACK中提供一个cookie C。该cookie包含设置本SCTP的所有状态
- 四路握手过程结束,两端各自选择一个主目的地址。当不存在网络故障时,主目的地址作为数据要发送的默认地址
关联终止
- SCTP不像TCP那样,允许半关闭存在
- 当一端关闭某个关联时,另一端必须停止发送新的数据
- 关闭关联请求的接收端发送完已经排队的数据后,完成关联的关闭
- SCTP没有类似于TCP的TIME_WAIT状态,因为SCTP使用验证标记
- 所有后续块,都在捆绑它们的SCTP分组的公共首部标记了初始的INIT块和INIT ACK块中作为起始标记交换的验证标记
- 来自旧连接的块,通过所在的SCTP分组的公共首部,间接携带的验证标记,对于新连接来说是不正确的
SCTP状态转换图
- 注意:ESTABLISHED,这个状态是绝大多数数据传送发生点的状态,尽管DATA块也可以由COOKIE ECHO块或COOKIE ACK块所在消息捆绑捎带
观察分组
- 本例中,客户在COOKIE ECHO块所在的分组中稍带了它的第一个DATA块,服务器则早作为应答的COOKIE ACK块所在分组中稍带了数据
- 一般而言,当网络应用采用一到多接口式样时,COOKIE ECHO通常捎带一个或多个DATA块
- SCTP分组中,信息的单位称为块(chunk)。块是自描述的,包含一个块类型、若干个块标记和一个块长度
SCTP选项
- SCTP使用参数和块方便增设可选特性。新的特性通过添加这两个条目之一加以定义,并允许通常的SCTP处理规则汇报未知的参数和未知的块。
- 参数类型字段和块类型字段的高两位都指明SCTP接收端该如何处理未知的参数或未知的块
- 如下有两个正在开发中的选项
- 动态地址扩展,允许协作的SCTP端点从已有的某个关联中动态删除IP地址
- 不完全可靠性扩展,允许协作的SCTP端点在应用进程的指导下限制数据的重传==。当一个消息变得过于陈旧而无须发送时,该消息将被跳过而不再发送到对端
端口号
- 任何时候,当多个进程可能同时使用TCP、UDP和SCTP这3种传输层协议中的任何一种,并且这3种协议都使用16位整数的端口号考区分这些进程
- 客户通常使用短期存货的临时端口(ephemeral port)这些端口通常由传输层协议自动赋予客户。客户不需关心其值,只需要确信该端口唯一就行。并且传输协议的代码需要保证这种唯一性
- Unix系统有保留端口(reserves port)的概念,指的是小于1024的任何窗口。这些端口只能赋予特权用户进程的套接字
- 有少数客户(而不是服务器)需要一个保留端口用于客户/服务器的认识:rlogin和rsh客户;这些客户调用库函数rresvport创建一个TCP套接字,并赋予一个513~1023范围的端口
- 该函数通常先尝试绑定端口1023,若失败尝试1022,依次类推,知道端口在513上成功或失败
套接字对
- 一个TCP连接套接字对(socket pair)是一个定义改连接的两个端点的四元组:本地IP地址、本地TCP端口、外地IP地址、外地TCP端口
- 一个SCTP关联有:一组本地IP地址、一个本地端口、一组外地IP地址、一个外地端口标识
缓冲区大下及限制
- IPv4数据报的最大大小是65535,包括IPv4首部。因为其总长度占据16位
- IPv6数据报的最大大小是65575,包括40字节的IPv6首部。因为其净荷长度是16位,注意IPv6净荷长度不包含IPV6首部
- IPv6有一个特大净荷长度选项,它将净荷长度阻断扩展到32位,不过这个选项需要MTU超过65535的数据链路提供支持
- 许多网络有一个可由硬件规定的MTU。例如,以太网MTU的最大字节是1500字节;较老的SLIP链路通常使用1006字节或296字节的MTU
- IPv4要求最小链路MTU是68字节;IPv6要求的最小链路为1280字节。注:要想IPv6运行在MTU小于此值的链路上,需要特定于链路的分片和重组功能,以使这些链路看起来至少为1280字节MTU
- 当一个IP数据报,从某个接口送出时,如果它的大小超过相应链路的MTU,IPv4和IPv6都将执行分片。这些分片,在没有到达最终目的地之前不会重组
- IPv4主机,对其产生的数据报执行分片,IPv4路由器,则对其转发的数据报执行分片
- IPv6只有主机对其产生的数据报执行分片
- IPv4首部的“不分片”位(DF位),则不管是主机还是路由器,都不执行分片。若IPv4路由器接触收到的一个超过它的外出链路MTU大小且设置了DF位的IPv4数据报时,它将产生一个ICMPv4的错误
- IPv6路由器不执行分片,即每个IPv6数据报隐含一个DF位。若IPv6路由器收到一个超过其外出链路MTU大小的IPv6数据报时,它将产生一个ICMPv6的错误
- IPv4的DF位和IPv6的隐含DF位可用于路径MTU发现,
- IPv4和IPv6都定义了最小重组缓冲区大小(minimum reassembly buffer size),它是IPv4或IPv6都必须保证的支持的最小数据报大小。其值对于IPv4是576字节,对于IPv6是1500字节
- TCP有一个MSS(maximum segment sizw,最大分节大下)选项,用于向对端TCP通告在每个分节中能发送的最大TCP数量。MSS的目的是告诉对端其重组缓冲区大小的实际值,从而试图避免分片
- MSS经常设置成MTU值为MTU减去IP和TCP的首部的固定长度
- 在TCP的MSS选项中,MSS是一个16位的字段,限定其最大值为65535。这对IPv4是适合的,因为IPv4数据报中的最大TCP数据量为65495;然而对于IPv6,如果TCP使用特大净荷选项,并且接收到的对端通告的MSS为65535,那么它所发送数据报数据报的大小限制就是MTU。如果这个值太大,那么路径MTU发现功能将确定这个较小值
- SCTP基于到对端所有地址发现的最小路径MTU保持一个分片点。这个最小MTU大小用于把较大的用户消息分割成较小的能够以单个IP数据报发送的若干片段。SCTP_MAXSEG套接字选项可以影响该选项,使用户能够请求一个更小的分片点
TCP输出
- 每一个TCP套接字有一个发送缓冲区,可以使用SO_SNDBUF套接字选项来更改该缓冲区的大小
- 当某个应用进程调用write时,内核把进程的缓冲区中复制所有数据到所写的套接字发送缓冲区。如果数据过大或发送缓冲区中已有其他数据,该进程将被投入睡眠
- 这里假设该套接字时阻塞的,它是通常的默认设置
- 内核将不从wtite系统调用返回,直到应用进程缓冲区中的所有数据都复制到套接字发送缓冲区
- 因此,从写一个TCP套接字的write调用成功返回仅仅表示,可以重新使用原来的应用进程缓冲区,并不表明对端TCP或应用进程已接收到数据
- TCP必须为发送的数据保留一个副本,直到它被对端确认为止
UDP输出
- 以虚线框展示套接字发送缓冲区,实际上并不存在
- 任何UDP套接字有发送缓冲区大小,可以使用SO_SNDBUF套接字选项更改,不过它仅仅是可写到该套接字的的UDP数据报的大小上限
- 如果一个应用进程写一个大于套接字发送缓冲区大小的数据报,内核将返回一个EMSGSIZE错误
- UDP是不可靠的,所以不必吧奥村应用进程数据的一个副本,因此无需一个真正的发送缓冲区
- TCP会把应用数据划分成MSS大小的块,而UDP没有对应的手段
SCTP输出
- SCTP是与TCP类似的可靠协议,它的套接字有一个发送缓冲区,和TCP一样,可以由SO_SNDBUG套接字选项来更改这个缓冲区大小
- 写一个SCTP套接字的write调用成功仅仅表示可以重新使用原来的应用进程缓冲区,并不表明对端的SCTP或应用进程已接收到数据
- 本段SCTP必须等待SACK,在累积确认点超过已发送的数据后,才可以从套接字缓冲区中删除该数据