UNIX网络编程读书笔记(二)第二章 传输层:TCP、UDP和SCTP

  • IPv4 网际协议版本4(Internet Protocol version 4)
  • IPv6 网际协议版本6(Internet Protocol version 6)
  • UDP 用户数据报协议(User Datagram Protocol)是一个简单的、不可靠的数据协议
  • TCP 传输控制协议(Transmission Control Protocol)是一个复杂的、可靠的字节流协议
  • SCTP (stream control transmission protocol,流控制传输协议)与TCP类似之处为它也是一个可靠的传输协议,但它还提供消息边界、传输级别多宿(multihoming)支持以及将头端阻塞(head-of-line blocking)减少到最小的一种方法。
  • ICMP 网际控制消息协议(Internet Control Message Protocol)
  • IGMP 网际组管理协议(Internet Group Management Protocol)
  • ARP 地址解析协议(Address Resolution Protocol)
  • RARP 反向地址解析协议(Reverse Address Resolution Protocol)
  • ICMPv6 网际控制消息协议版本6(Internet Control Message Protocol version 6)
  • BPF BSD分组过滤器(BSD packet filter)
  • DLPI 数据链路提供者接口(datalink provider interface)

2.1用户数据报协议(UDP)

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

每个UDP数据报都有一个长度。如果一个数据报正确地到达目的地,那么该数据报的长度将随数据一道传递给接收端应用进程。

UDP提供无连接的(connectionless)服务,因为UDP客户与服务器之间不必存在任何长期的关系。例如,一个UDP客户可以创建一个套接字并发送一个数据报给一个给定的服务器,然后立即用同一个套接字发送另一个数据报给另一个服务器。同样地,一个UDP服务器可以用同一个UDP套接字从若干个不同的客户接收数据报,每个客户一个数据报。

2.2 传输控制协议(TCP)

RFC 793详细说明,RFC 1323、RFC 2581、RFC2988、RFC 3390 更新

TCP提供客户与服务器之间的连接(connection),TCP客户先与某个给定服务器建立一个连接,再跨该连接与那个服务器交换数据,然后终止这个连接。

TCP还提供了可靠性(reliability)。当TCP向另一端发送数据时,它要求对端返回一个确认。如果没有收到确认,TCP就自动重传数据并等待更长时间。在数次重传失败后,TCP才放弃,如此在尝试发送数据上所花的总时间一般为4~10分钟。

TCP含有用于动态估算客户和服务器之间的往返时间(round-trip time,RTT)的算法.通过给其中每个字节关联一个序列号对所发送的数据进行排序(sequencing)。

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

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

2.3 流控制传输协议(SCTP)

RFC 2960详细说明,RFC 3309更新,RFC 3286简要介绍。

SCTP在客户和服务器之间提供关联(association),并像TCP那样给应用提供可靠性、排序、流量控制以及全双工的数据传送。 SCTP使用“关联”一词取代“连续”是为了避免这样的内涵:一个连接只涉及两个IP地址之间的通信。一个关联指代两个系统之间的一次通信,它可能因为SCTP支持多宿而涉及不止两个地址。 SCTP面向消息(message-oriented),提供各个记录的按序递送服务,与UDP一样,由发送端写入的每条记录的长度随数据一道传递给接口端应用。 SCTP能够在所连接的端点之间提供多个流,每个流各自可靠地按序递送消息,一个流上某个消息的丢失不会阻塞同一关联其他流上消息的投递。 SCTP还提供多宿特性,使得单个SCTP端点能够支持多个IP地址。

2.4 TCP的连接和终止

2.4.1 三路握手

建立一个TCP连接时会发生下述情形:

  1. 服务器必须准备好接受外来的连接。通常通过调用socket、bind和listen这3个函数来完成,我们称之为被动打开(passive open)。
  2. 客户通过调用connect发起主动打开(active open)。这导致客户TCP发送一个SYN(同步)分节,它告诉服务器客户将在(待建立的)连接中发送的数据的初始序列号。通常SYN分节不携带数据,其所在IP数据报只含有一个IP首部、一个TCP首部及可能有的TCP选项。
  3. 服务器必须确认(ACK)客户的SYN,同时自己也得发送一个SYN分节,它含有服务器将在同一连接中发送的数据的初始序列号。服务器在单个分节中发送SYN和对客户SYN的ACK(确认)。在这里插入图片描述
  4. 客户必须确认服务器的SYN。
     

    客户的初始序列号为J,服务器的初始序列号为K。ACK中的确认号是发送这个ACK的一端所期待的下一个序列号。因为SYN占据一个字节的序列号空间,所以每一个SYN的ACK中的确认号就是该SYN的初始序列号加1。每一个FIN(表示结束)的ACK中的确认号为该FIN的序列号加1。

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

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

2.4.2 TCP终止

终止一个连接则需要4个分节:

(1)某个应用进程首先调用close,我们称该端执行主动关闭(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。

(2)接收到这个FIN的对端执行被动关闭(passive close)。这个FIN由TCP确认,它的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程(放在已排队等候该应用进程接收的任何其他数据之后),因为FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。

(3)一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。

(4)接收这个最终FIN的原发送段TCP(即执行主动关闭的那一端)确认这个FIN。

在步骤2和步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为办关闭(half-close)。

2.4.3 TCP状态转换图

TCP为一个连接定义了11种状态,并且TCP规则规定如何基于当前状态及在该状态下所接收的分节从一个状态转换到另一个状态。

粗实线表示客户状态转换,粗虚线表示服务器状态转换
应用:表示状态转换在应用进程发起操作时发生
接收:表示状态转换在接收到分节时发生
发生:表示这个转换发送什么

一个完整的TCP连接所发生的实际分组交换情况:

2.5 TIME_WAIT状态

停留在TIME_WAIT状态的持续时间是最长分节生命期(maximum segment lifetime,MSL)的两倍,即2MSL。

MSL是任何IP数据报能够在因特网中存活的最长时间。

迷途的重复分组(lost duplicate)或漫游的重复分组(wandering duplicate):是指早先迷失在循环中的被送到目的地的分组。

TIME_WAIT状态有两个存在的理由:

  1. 可靠地实现TCP全双工连接的终止;防止ACK丢失的重传
  2. 允许老的重复分节在网络中消逝。保证每成功建立一个TCP连接时,来自该连接先前化身的老的重复分组都已在网络中消逝了。

2.6 SCTP关联的建立和终止

SCTP的四路握手:

INIT(随众多参数一道)承载一个验证标记Ta和一个初始序列号J。在关联的有效期内,验证标记Ta必须在对端发送的每个分组中出现。初始序列号J用作承载用户数据的DATA块的起始序列号。对端也在INIT ACK中承载一个验证标记Tz,在关联的有效期内,验证标记Tz也必须在其发送的每个分组中出现。除了验证标记Tz和初始序列号K外,INIT的接收端还在作为响应的INIT ACK中提供一个cookie C。该cookie包含设置本SCTP关联所需的所有状态,这样服务器的SCTP栈就不必保存所关联客户的有关信息。

2.7 端口号

TCP、UDP、SCTP都使用16位整数的端口号(Port number)来区分进程。服务器端,TCP、UDP、SCTP定义了一组众所周知的端口(well-known port),用于标识众所周知的服务。客户端使用短期存活的临时端口(ephemeral port)。

IANA(the Internet Assigned Numbers Authority , 因特网已分配数值权威机构)维护者一个端口号分配状况的清单。端口号被划分为以下3段:

(1)众所周知的端口0~1023。这些端口由IANA分配和控制。

(2)已登记的端口(registered port)为1024~49151。这些端口不受IANA控制,不过由IANA登记并提供它们的使用情况清单,以方便整个群体。

(3)49152~65535是动态的(dynamic)或私用的(Private)端口。IANA不管这些端口。它们就是我们所称的临时端口。

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

SCTP一个关联由一组本地IP地址、一个本地端口、一组外地IP地址、一个外地端口标识。

标识每个端点的两个值(IP地址和端口号)通常称为一个套接字。

2.8 TCP端口号与并发服务器

并发服务器中主服务器循环通过派生一个子进程来处理每个新的连接。如果一个子进程继续使用服务器众所周知的端口来服务一个长时间的请求,那将发生什么?我们来看一个典型的序列。首先,在主机freebsd上启动服务器,该主机是多宿的,其IP地址为12.106.32.254和192.168.42.1。服务器在它的众所周知的端口(本例为21)上执行被动打开,从而开始等待客户的请求,如图所示:

我们使用记号{*:21,*:*}指出服务器的套接字对。服务器在任意本地端口(第一个星号)的端口21上等待连接请求。外地IP地址和外地端口都没有指定,我们用“:”来表示。

我们称它为监听套接字(listening socket)。

这里指定本地IP地址的星号为通配符(wildcard)。如果运行服务器的主机是多宿的,服务器可以指定它只接受到达某个特定本地接口的外来连接。这里要么选一个接口要么选任意接口。服务器不能指定一个包含多个地址的清单。通配的本地地址表示“任意”这个选择。

通配的地址通过在调用bind之前把套接字的地址结构中的IP地址字段设置成INADDR_ANY来指定。

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

稍后在IP地址为206.168.112.219的主机上启动第一个客户,它对服务器的IP地址之一12.106.32.254执行主动打开。我们假设本例中客户机主机的TCP为此选择的临时端口为1500,如图所示。图中在该客户的下发标出了它的套接字对。

当服务器接收并接受这个客户的连接时,它fork一个自身的副本,让子进程来处理该客户的请求。

至此,我们必须在服务器主机上区分监听套接字和已连接套接字(connected socket)。注意已连接套接字使用与监听套接字相同的本地端口(21)。还要注意在多宿服务器主机上,连接一旦建立,已连接套接字的本地地址(12.106.32.254)随即填入。

下一步假设在客户主机上另有一个客户请求连接到同一个服务器。客户主机的TCP为这个新客户的套接字分配一个未使用的临时端口,譬如1501。服务器上这两个连接时有区别的:第一个连接的套接字对和第二个连接的套接字对不一样,因为客户的TCP给第二个连接选择了一个未使用的端口(1501)。

通过本例应注意,TCP无法仅仅通过查看目的端口号来分离外来的分节到不同的端点。它必须查看套接字对的所有4个元素才能确定由哪个端点接收某个到达的分节。图2-14中对于同一个本地端口(21)存在3个套接字。如果一个分节来自206.168.112.219端口1500,目的地位12.106.32.254端口21,它就被递送给第一个子进程。如果一个分节来自206.168.112.219端口1501,目的地为12.106.32.254端口21,它就被递送给第二个子进程。所有目的端口为21的其他TCP分节都被递送给拥有监听套接字的最初那个服务器(父进程)。

2.9 缓冲区大小及限制

  1. IPv4数据报最大大小是65535字节
  2. IPv6数据报最大大小是65575字节
  3. 硬件规定的MTU
  4. 路径MTU
  5. 数据报分片
  6. 最小缓冲区大小
  7. MSS最大分节大小

2.10 TCP输出

应用程序写数据到一个TCP套接字中时发生的步骤

6ebb664f6bbbe275b0704e7dfec3bf0c113.jpg

每个套接字有一个发送缓冲区,可以使用SO_SNDBUF套接字选项来更改缓冲区大小。
应用程序调用write,内核从改应用程序的缓冲区中复制所有数据到所写套接字的发送缓冲区。如果发送缓冲区比较大,内核将不从write系统调用返回,直到应用程序缓冲区所有数据都复制到发送缓冲区。
TCP必须为已发送的数据保留一个副本,直到它被对端确认为止。
TCP以MSS大小或更小的块把数据传递给IP,同时加上TCP首部。MSS或是由对端通告的值,或是536(IPV4最小重组缓冲区字节数576减去IPV4首部字节数20和TCP首部字节20)
 

2.11 UDP输出

应用进程写数据到一个UDP套接字步骤:

57c3253e8370bb0cdaa0da3cf6050cf67d9.jpg

虚线表示套接字发送缓冲区实际上并不存在,但是有发送缓冲区大小,可用SO_SNDBUF修改,它限制写到该套接字的UDP数据报上限。如果大于它,内核返回EMSFSIZE错误
因为UDP是不可靠的,因此它不必保存应用进程数据的一个副本,无需一个真正的发送缓冲区。
应用进程的数据在沿协议栈向下传递时,通常被复制到某种格式的一个内核缓冲区中,然而当该数据被发送之后,这个副本就被数据链路层丢弃了。
 

转载于:https://my.oschina.net/u/3227348/blog/1933175

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值