[关闭]
@FunC 2018-05-15T11:15:03.000000Z 字数 4210 阅读 1734

UNP CH01, CH02

unix


CH01 简介

OSI 模型

OSI 模型与网际协议族的近似映射:

其中 TCP 和 UDP 之间的空隙表示网络应用绕过传输层直接使用 IPv4/IPv6 是有可能的,这就是所谓的原始套接字(raw socket)

所谓的套接字编程接口,指的是顶上三层(网际协议的应用层)进入传输层的接口。
之所以套接字提供的是从 OSI 模型顶上三层进入传输层的接口,原因有二:
1. 顶上三层处理具体网络应用的所有细节,却对通信细节了解很少;同时地下四层对具体网络应用了解不多,却处理所有的通信细节(包括发送数据,等待确认,计算并验证校验和等)
2. 顶上三层通常构成所谓的用户进程(user process),地下四层却通常作为操作系统内核的一部分提供。Unix 与其他现代操作系统都提供分隔用户进程与内核的机制。

Unix errno

只要一个 Unix 函数(如某个套接字函数)中有错误发生,全局变量 errno 就被置为一个知名该错误类型的正值,同时函数本身则通常返回 -1.

使用全局变量 errno 的方式,不适用于共享所有全局变量的多个线程


CH02 传输层:TCP、UDP 和 SCTP

因为本书主题是:套接字联网API,所以协议本身仅作简单介绍。

总图

小细节:最左边名为 tcpdump 的网络应用或者使用BSD分组过滤器,或者使用数据链路提供者接口直接与数据链路进行通信。访问BPF或者DLPI的接口不使用套接字或XTI。

用户数据报协议(UDP)

应用进程往一个UDP套接字写入一个消息,该消息随后被封装到一个UDP数据报,再封装进一个IP数据报,然后发送到目的地。
UDP的主要问题是它缺乏可靠性。例如它不保证数据报的先后顺序跨网络后保持不变,不保证每个数据报只到达一次,没有本端的超时与重传机制等。这些都需要交给应用程序中实现。
不同于没有任何记录边界的TCP,每个UDP数据包都有一个长度。
UDP提供无连接的服务,UDP客户与服务器之间不必存在任何的长期关系。

传输控制协议(TCP)

TCP提供了:
* 客户与服务器之间的连接。客户跨该连接与那个服务器交换数据,然后终止连接。
* 可靠性。发送数据后,要求对端返回一个确认。否则自动重传数据并等待更长时间。
* 用于动态估算客户和服务器之间往返时间(RTT)的算法。
* 通过给其中每个字节关联一个序列号,来对所发送的数据进行排序。如果收到重复数据则丢弃
* 流量控制。通过用通告窗口告诉对端它能一次能接受的数据
* 全双工连接。在任何时刻既发送数据又接受数据(UDP*可以是*全双工的)

流控制传输协议(SCTP)

SCTP提供的服务与UDP和TCP类似。它在客户和服务器之间提供关联(association),并像TCP一样提供可靠性。(提供关联而不是连接的原因是SCTP支持多宿,不止两个地址)
SCTP是面向信息的,提供各个记录的按序递送服务,包含信息边界。
SCTP能够在所连接的端点之间提供多个流,每个流各自可靠地传递信息。一个流上某个消息的丢失不回阻塞同一关联其他流上消息的投递(区别于TCP)
SCTP提供多宿特性,使单个SCTP端点能够支持多个IP地址。这样如果某个同路发生故障,SCTP可以通过切换到使用已与该关联相关的另一个地址来规避所发生的故障。

TCP连接的建立和终止

TCP连接建立:三路握手🤝


类比电话系统:
* socket: 有电话可用
* bind: 告诉别人你的电话号码
* listen: 打开电话振铃
* connect: 要求我们知道对方电话,并拨打
* accept: 发生在被呼叫的人应答电话之时,返回客户的标识

TCP 选项

每个SYN可以含有多个TCP选项,下面是常用的TCP选项:
* MSS选项:通告对端它的最大分节大小(MSS)。
* 窗口规模选项:指定TCP首部中的通告窗口必须扩大的位数。使用该选项的前提是它的两个端系统必须都支持这个选项。
* 时间戳选项:对于高速连接是必要的,防止失而复现的分组可能造成的数据损坏。

TCP连接终止:四路挥手👋

其中步骤2,3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为半关闭

当一个Unix进程无论是自愿(调用exit或从main函数返回)还是非自愿(收到终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN。

TCP状态转移图

TCP涉及连接建立和连接终止的操作可以用状态转移图来说明。TCP为一个连接定义了11种状态。

其中有两个状态未提及过:
* 同时打开
* 同时关闭
这都是能发生的(较罕见)
这些状态都可使用 netstat 显示

观察分组

下面给出一个完整的TCP连接所发生的实际分组交换情况(包括连接建立、数据传送和连接终止三个阶段):
[image:C66967D4-38A0-456E-A48A-52889E8BC7FD-720-00005282E484F1A3/A215340D-C34A-4F1A-BC6A-155F9EA11A8C.png]

注意到不同方向上的MSS值不相同,不过这不成问题。此处客户端接到服务端MSS为1460,同时为了避免分片(自己的MSS是536),所以不会发送大于536的数据

粗剪头表示两个数据分节。看到服务器对客户请求的确认是伴随其应答发送的,这称为捎带。通常在服务器处理请求并产生应答的时间少于200ms时发生。

值得注意的是,如果该连接的整个目的仅仅是发送一个单分节请求和接受一个单分节应答,那个使用TCP有8个分节的开销。而如果改用UDP,那么只需要交换两个分组。

UDP避免了TCP连接建立和终止所需的开销。

TIME_WAIT 状态

执行主动关闭的那端经历了TIME_WAIT状态,并持续两倍的MSL(最长分节生命期)
该状态有两个存在的理由:
1. 可靠地实现TCP全双工连接的终止
即最终的ACK如果丢失了,服务器将重发FIN,客户必须维持状态信息,以允许重发最终的ACK
2. 允许老的重复分解在网络中消逝
这是因为一个TCP连接由四元组确定。TCP必须防止来自某个连接的老的重复分组在该连接已经终止后再现,从而被误解成属于新连接的数据。而两个方向的分组均最多存活MSL秒,所以TIME_WAIT持续2MSL

SCTP关联的建立和终止

四路握手🤝

类似于TCP:

* 客户主动打开时,发送一个INIT消息,告诉服务器客户的IP地址清单、初始序列号、起始标记、客户请求的外出流的数目以及客户能够支持的外来流的数目。
* 服务器响应的INIT ACK 还带上了状态cookie(包含服务器用于确信本关联有效的所有状态,经数字化签名)

使用四路握手是为了避免拒绝服务攻击(第四章谈及)

关联终止👋

SCTP不像TCP那样允许“半关闭”的关联。当一端关闭某个关联时,另一端必须停止发送新的数据。

因为有了验证标记(上面的Ta, Tz),所以不再需要类似于TCP的TIME_WAIT状态。

SCTP状态转移图

注意服务器收到INIT后仍停留在CLOSED状态。

观察分组


可以注意到,SCTP在COOKIE ECHO块中就捎带了它的第一个DATA块,而服务器在COOKIE ACK块中稍带了分组数据

SCTP选项

端口号

套接字对

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

TCP端口号与并发服务器

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

可以看到,TCP无法仅仅通过查看目的端口来分离外来的分节到不同的端点。它必须查看套接字对的所有四个元素才能确定由那个端点接受某个到达的分节。如果没匹配已连接的套接字,则地送给拥有监听套接字的服务器(父进程)。

缓冲区大小及限制

TCP输出


如果套接字的发送缓冲区容不下应用进程的所有数据,应用进程将被投入睡眠,知道应用进程缓冲区的所有数据都复制到套接字发送缓冲区。

所以TCP套接字的write调用成功返回仅仅表示我们可以重新使用原来的应用进程缓冲区。

UDP输出


其中虚线框展示套接字发送缓冲区,是因为它实际上并不存在。它仅仅代表可以写到该套接字的UDP数据报的大小上限。
因为UDP是不可靠的,它不比保存应用进程数据的副本,因此无需真正的发送缓冲区。

从写一个UDP套接字的 write 调用成功返回,只代表缩写的数据报已被假如数据链路层的输出队列。如果队列没有足够的空间存放该数据报,内核通常会返回一个ENOBUFS错误。

SCTP输出

与TCP输出基本类似。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注