1.TCP/IP 协议族
1.1 TCP/IP协议族及主要协议
TCP/IP 协议族是一个四层协议系统。
自上而下为(如下图所示):
应用层
传输层
网络层
数据链路层
应用层负责处理应用程序逻辑,在用户空间实现。(少数服务器程序在内核中实现。)
数据链路层、网络层、传输层负责处理网络通信细节,这部分必须即稳定又高效,因此他们都在内核空间中实现。
上层协议使用下层协议提供的服务
,这个如何实现的详见1.2 封装。
1.1.1 数据链路层
实现网卡接口的网络驱动程序;
处理数据再物理媒介(以太网、令牌环等)上的传输 。
常用协议:
ARP,Address Resolve Protocol,地址解析协议 (重点)
RARP,Reverse Address Resolve Protocol,逆地址解析协议。
以此实现IP地址和机器物理地址(通常是MAC地址)之间的相互转换。
以太网、令牌环、802.11无线网络 都是用MAC地址。
网络层通过IP地址寻址,而数据链路层通过物理地址(通常是MAC)寻址
。
故网络层必须先将目标IP转化为其物理地址,才能使用数据链路层提供的服务,即ARP的用途。
RARP可不是用来MAC转IP的。RARP协议仅用于网络上的某些无盘工作站。(不重要,百度了解即可)
1.1.2 网络层
实现数据包的选路和转发。
IP,Internet Protocol,因特网协议
ICMP,Internet Control Message Protocol,因特网控制报文协议
WAN,Wide Area Network,广域网
LAN,Local Area Network,局域网
WAN通常使用 众多分级路由器来 连接 分散的 主机或LAN。
网络层就是选择这些中间节点,以确定两台主机之间的通信路径。
同时网络层对上层隐藏网络拓扑细节,使上层看来通信双方是直接相连的。
网络层通信流程:
根据:IP协议根据数据包的目的IP来决定如何投递。
流程(IP协议使用逐跳(hop by hop)的方式确定通信路径):
1.IP协议为数据包寻找合适吓一跳(next hop)路由器,并将数据包交付给该路由器转发。
2.多次重复过程1,直到数据包到达目标主机,或者由于发送失败而被丢弃。
1.1.2.1 ICMP
ICMP报文格式如下图。
ICMP 是IP协议的补充,用于检测网络连接。
ICMP并非严格意义上的网络层协议,因为它使用处于同一层的IP协议提供的服务。
ICMP报文分两类:
1.差错报文
主要用来回应错误,
比如目标不可达(类型值为3)和重定向(类型值为5)
2.查询报文
用来查询网络信息。
比如ping程序,就是使用ICMP报文查看目标是否可达(类型值为8)。
ICMP报文格式详解:
1.8位类型
参照上面报文分类理解
2.8位代码
可以进一步细分不同条件。
如:重定向报文使用代码值0 表示对网络重定向
代码值1 表示对主机重定向。
3.16位校验和
对整个报文(包括头部和内容部分)进行循环冗余校验(CRC,Cyclic Redundancy Check),以校验报文在传输过程中是否损坏。
1.1.2.2 IP 协议详解
IP协议是协议族核心,也是socket编程基础之一。
详解从两个方面进行:
1.IP头部信息
IP头部信息出现在每个IP数据报中,作用如下:
1.用于指定IP通信的源端IP地址、目的端IP地址
2.指导IP分片和重组
3.指定部分通信行为
2.IP数据报的路由和转发
IP数据报的路由和转发发生在除目标机器之外的所有主机和路由器上。
它们决定数据报是否应该转发以及如何转发。
IP协议分类
1.目前常用的IP地址为32位的IPv4协议
2.为应对IPv4地址即将用完的情况,又开发出128位的IPv6协议。
1.1.2.2.1 IP 服务的特点
IP协议为上层提供 无状态
、无连接
、不可靠的服务
。
无状态(stateless):
1.概念:
指IP通信双方不同步传输数据的状态信息。故 所有IP数据报的发送、传输和接收都是相互独立、没有上下文关系的。
这种服务最大的缺点是 无法处理乱序和重复的IP数据报。
如:
发送端 发出第N个IP数据报可能比第N+1个后到达接收端;
而同一个IP数据报也可能经过不同的路径多次到达接收端。
在上面两种情况下,接收端的IP模块无法检测到乱序和重复,因为这些IP数据报之间没有上下文关系。
接收端IP模块只要收到了完整的IP数据报(若是IP分片,IP模块将先重组),就将其数据部分(TCP报文、UDP报文、ICMP报文)
上交给上层协议。
那么上层协议来看,这些数据可能是乱序的、重复的。
2.优点
简单、高效。
因为无需为通信连接分配内核资源,传输时也无需携带状态信息。
网络协议中无状态很常见,如HTTP协议,一个浏览器的连续两次网页请求之间没有任何关联,它们将被web服务器独立处理。
无连接(connectionless) :
1.概念
指IP通信双方都不长久地维持对方的任何信息。
故,上层协议每次发送数据时都必须指定对方的IP地址。
不可靠:
指IP协议不能保证IP数据报准确的到达接收端,它只承诺尽最大努力(best effort)。
很多种情况都能导致IP数据包发送失败,如:
1.某个中转路由器发现IP数据报在网络上存活的时间太长(根据IP数据报头部字段TTL判断),
那么他将丢弃之,并返回一个ICMP错误消息(超时错误)给发送端。
2.接收端发现收到的IP数据报 不正确(通过校验机制),它将丢弃之,
并返回一个ICMP错误消息(IP头部参数错误)给发送端。
总结:
无论怎样,发送端IP模块一旦检测到一个IP数据报发送失败,就通知上层协议发送失败,
而不会试图重传。
故使用IP服务的上层协议(比如TCP协议)需要自己实现数据确认、重传等机制以达到可靠传输的目的。
所以,使用IP服务的上层协议(如TCP)需要自己实现数据确认、超时重传等机制来达到可靠传输的目的。
1.1.2.2.2 IPv4 头部
IPv4头部长度通常是20字节
,除非含有可变长的选项部分。
4位版本号(version): 指定IP协议版本。 如IPv4 值为4。
4位头部长度(header length): 标识该IP头部有多少个32bit字(4字节)。 因为4位最大表示15,故IP头部最长为60字节。
8位服务类型(Type Of Service):
包含3部分:
1. 一个3位的优先权字段(现在已被忽略)
2. 4位 TOS字段
分别表示 最小延时、最大吞吐量、最高可靠性、最小费用,
其中最多有一个能置为1,应用程序根据实际需求设置。
如:
1.ssh和telnet这样的登录程序需要最小延时服务
2.文件传输程序ftp 则需要最大吞吐量。
3. 1位保留字段(必须置0)
16 位总长度(total length):
指整个IP数据报的长度,以字节位单位,故IP数据报的最大长度位65535字节。
由于MTU的限制,长度超过MTU的数据报都将被 分片传输 ,所以实际传输的IP数据报(或分片)的长度都远远没有达到最大值。
接下来三个字段描述如何实现分片:
16 位标识(identification): 唯一的标识主机发送的每一个数据报。
其初始值由系统随机生成;每发送一个数据报,其值就加1。
其值在数据报分片时被复制到每个分片中,因此同一个数据报的所有分片都具有相同标识值。
3 位标志:
第1位:保留。
第2位(Don't Fragment,DF):标识禁止分片。
如果设置了这个位,IP模块将不对数据报进行分片。在这种情况下,
若IP数据报长度超过MTU,IP 模块将丢弃该数据报并返回一个ICMP差错报文。
第3位(More Fragment,MF):表示更多分片。
除了数据报的最后一个分片外,其它分片都要把它置1.
13 位分片偏移(fragmentation offset):
是分片相对原始IP数据报开始处(仅指数据部分)的偏移。
实际的偏移是该值左移 3位(乘8)后得到的。
因此,除最后一个分片外,每个 IP 分片的数据部分的长度必须是8的整数倍(这样才能保证后面IP分片拥有一个合适偏移值)
8 位生存时间(Time To Live): 数据报到达目的地之前允许经过的路由器跳数。防止数据报陷入路由循环。
TTL值被发送端设置(常见值为64)。
数据报每经过一个路由,该值就被路由器减 1。
当TTL值减为 0 时,路由器将丢弃数据报,并向源端发送一个ICMP差错报文。
8位协议(protocol): 用来区分上层协议。
如 ICMP 值为1,TCP 为6, UDP 为17.
Linux可通过 cat /etc/protocols 查看所有上层协议对应的值。
16位头部校验和(header checksum): 由发送端填充,接收端对其使用CRC算法校验IP数据报头部在传输过程中是否损坏。
特别注意:此处仅检验头部数据。
32 位源端IP地址和 32位目的端地址: 用来标识数据报的发送端和接收端。
一般情况下这两个地址在传输过程中保持不变。
选项(Option): 可变长的可选信息。
最多包含40字节(因为IP头部最长为60字节,同时前20字节是固定的)。
可用的IP选项包括:
1.记录路由(record route)
告诉数据报途径的所有路由器都将自己的IP地址填入IP头部的选项部分,这样可以跟踪数据报的传递路径。
2.时间戳(timestamp)
告诉每个路由器都将数据报被转发的时间(或时间与 IP 地址对)填入IP头部选项部分,
这样可以测量途径路由之间数据报传输时间。
3.松散源路由选择(loose source routing)
指定一个路由器 IP地址列表,数据报发送过程中必须经过其中所有路由器。
4.严格路由选择(strict source routing)
和3类似,但数据报只能经过被指定的路由器。
1.1.2.2.3 使用 tcpdump 观察IPv4头部结构
1.1.2.2.4 IP 分片
当IP数据报长度超过帧的MTU时
,它将被分片传输
。
分片流程:
分片可能发生在发送端,也可能发生在中转路由器上,而且可能在传输过程中被多次分片。
组装流程:
但只有在最终的目标机器上,这些分片才会被内核中的IP模块重新组装。
如何实现分片和组装:
通过IP头部如下三个字段,数据报标识、标志、片偏移(详见上一小节)。
一个数据报的每个分片都有自己的IP头部,它们有相同的标识值,但有不同的片偏移。
且除最后一个分片外,其它所有分片都将设置MF标志。
此外,每个分片的IP头部总长度都将被设置为该分片的长度。
MTU长度:
以太网帧的 MTU 是 1500 字节(可以通过命令 ifconfig 或 netstat查看)。
因此它可携带IP数据报数据长度最多为 1480 字节(IP 头部占 20 字节)。
IP层传递给数据链路层的数据可能是一个完整的IP数据报,也可能是一个IP分片,统称为IP 分组(packet)。
例子:
若用 IP数据报封装一个长度为 1481 字节的 ICMP报文(包括8字节头部,故数据长度为1473 字节),则该数据报在以太网帧传输时必须被分片,如下图所示:
看上图2-2 注意点:
1.第一个IP分片 IP 头部设置了MF,且包含ICMP头部
2.第二个分片不包含ICMP头部,因为IP模块重组只需要一份ICMP头部信息。
3.ICMP 报文的头部长度取决于报文的类型,其变化很大。此处只是以8字节为例。
如,ping程序使用的ICMP 请求和应答报文头部长度就是 8 字节。
抓包查看IP分片示例:
1.1.2.2.5 IP 路由
IP协议的一个核心任务就是数据报的路由
,即决定发送数据报到目标机器的路径
。
IP模块工作流程如下图2-3:
简析上图工作流程:
当IP模块接收到来自数据链路层的IP数据报时:
1.首先对该数据报头部进行 CRC 校验,确认无误后就分析其头部具体信息。
2. ①若该IP数据报头部设置了 松散源路由选择或严格源路由选择,则 IP模块调用数据报转发子模块来处理数据报。
②若该数据报头部中目标IP地址是本机的某个IP地址(或者是广播地址),即该数据报是发送给本机的,
则 IP 模块就根据数据报头部中的协议字段来决定将它派发给哪个上层应用(此处就是分用过程)
③若IP模块发现这个数据报不是发送给本机的,则调用数据报转发子模块来处理该数据报。
数据报转发子模块流程(详见小节 1.1.2.2.6 IP转发):
1.首先检测系统是否允许转发。
若不允许,IP 模块就将数据报丢弃。
若允许,数据报转发子模块就对该数据报执行一些操作,然后将它交给IP数据报输出子模块。
图2-3中 计算下一跳路由 子模块即实现:
IP 数据报应该发送至哪一个下一跳路由(或者目标机器),以及经过哪个网卡来发送,就是 IP 路由过程。
图2-3中 IP 输出队列:
存放的是所有等待发送的 IP 数据报,包括:
1.需要转发的 IP 数据报
2.还有封装了本机上层数据(ICMP报文、TCP报文、UDP数据报)的IP数据报。
图2-3中 虚线箭头: 显示了路由表更新过程。
这一过程指通过路由协议或者 route 命令调整路由表,使之更适应最新的网络拓扑结构,成为 IP 路由策略。
图2-3 中 ICMP重定向也能用于更新路由表,详见 小节 1.1.2.2.7.1 ICMP重定向报文
IP 模块实现数据报路由的核心数据结构是路由表
。
1.1.2.2.5.1 路由机制
1.1.2.2.5.2 路由表更新
1.1.2.2.6 IP 转发
由前文(小节 1.1.2.2.5 IP 路由) 可知,不是发送给本机的 IP 数据报将由数据报转发子模块来处理。
路由器都能执行数据报的转发操作。
而主机一般只发送和接收数据报,这是因为主机上 /proc/sys/net/ipv4/ip_forward内核参数默认被设置为 0 。
例子:
下面通过修改它来使能主机的数据报转发功能:
1.1.2.2.7 重定向
1.1.2.2.7.1 ICMP 重定向报文
1.1.2.2.7.2 主机重定向实例
1.1.2.2.8 IPv6 头部结构
IPv6 较于 IPv4:
1.解决地址不够用问题
2.改进:
如:①增加多播和流功能,为网络上多媒体内容质量提供精细控制。
②引入自动配置功能,使得局域网管理更方便。
③增加专门的网络安全功能。
④...等等
IPv6 头部由 40 字节固定头部
和可变长的扩展头部
组成。
1.1.2.2.8.1 IPv6 40 字节固定头部
40 字节(320 位) = 4 + 8 + 20 + 16 + 8 + 8 + 128 + 128
4 位版本号(version):
指定IP协议版本。对 IPv6来说,其值是6.
8 位通信类型(traffic class):
指示数据流通信类型或优先级, 和IPv4中的 TOS 类似。
20 位流标签(flow label):
IPv6新增字段,用于某些对连接的服务质量有特殊要求的通信,如音频或视频等实时传输。
16 位净荷长度(payload length):
指IPv6扩展头部和应用程序数据长度之和,不包括固定头部长度。
8 位下一个包头(next head):
指出紧跟IPv6 固定头部后的包头类型。
如扩展头(如果有的话)或上层协议头(如TCP、UDP、ICMP)。类似IPv4头部中协议字段,且相同的取值有相同的含义。
8 位跳数限制(hop limit): 和 IPv4 的 TTL 含义相同。
128 位源端地址和 128 位目的端地址: IPv6 用 128 位来表示 IP 地址。
1.1.2.2.8.2 IPv6 扩展头部
可变长的扩展头部使得 IPv6 能支持更多的选项。
它的长度可以是 0,表示没有使用任何扩展头部。
一个数据报可以包含多个扩展头部,注意:
每个扩展头部的类型由前一个头部(固定头部或扩展头部)中的下一个报头字段指定。
目前可以使用的扩展头部如下图所示:
注意:IPv6 是完全独立的协议,不是 IPv4 的扩展。如以太网帧 IPv6 (0x86dd)数据报和 IPv4 数据报(0x800)就有不同类型值
1.1.3 传输层
为两台主机上的应用程序提供端到端(end to end)的通信。
负责数据的收发、链路的超时重连等等。
包含协议:
TCP、UDP、SCTP
传输层只关心通信的起始段和目的端,不在乎数据包的中转过程。
如下图:
垂直实线:表示TCP/IP协议族各层之间的实体通信(数据包的传递)
水平虚线:代表逻辑通信线路
由图:
数据链路层 封装了物理网络的细节;
网络层 封装了网络连接的细节;
传输层 则为应用程序封装了一条端到端的逻辑通信链路,它负责数据的收发、链路的超时重连等。
1.1.3.1 TCP
TCP协议(Transmission Control Protocol,传输控制协议)
TCP为应用层提供可靠的
、面向连接的
、基于流(stream)
的服务。
可靠的:
使用超时重传、数据确认等方式来确保数据包被正确发送至目的端。
面向连接:
通信时: 通信双方必须先建立连接,并在内核中为该连接维持一些必要的数据结构,如连接状态、读写缓冲区及诸多定时器。
通信完毕:双方必须关闭连接以释放这些内核数据。
基于流:
基于流的数据没有长度限制,它源源不断的重通信一端流入另一端。
发送端可以逐个字节向数据流写入数据;
接收端也可以逐个字节的读出。
1.1.3.1.1 TCP 协议详解
和传输层的IP相比,网络层的TCP更靠近应用层,故应用程序具有更强的可操作性。
1.1.3.1.1.1 TCP服务特点
1.面向连接、字节流、可靠传输
2.通信双方必须先建立连接。双方必须为该连接分配必要的内核资源。
3.TCP 连接是全双工的,即双方的数据读写可以通过一个连接进行。通信完毕后,通信双方都必须断开连接以释放资源。
4.TCP 的连接是一对一的,所以基于广播和多播(目标是多个主机地址)的应用程序不能使用TCP服务,而无连接的UDP则适合广播和多播。
5.TCP 模块发送的 TCP报文段个数和应用程序执行的写操作次数之间没有固定的数量关系。
TCP模块发送数据流程(详见图3-1):
①当发送端连续执行多次写操作时,TCP模块会先将这些数据放入 TCP发送缓冲区。
②当TCP 模块真正开始发送数据时,发送缓冲区中这些等待发送的数据可能被封装成一个或多个TCP报文段发出。
6.应用程序执行的读操作次数和 TCP模块接收到的 TCP报文段个数之间也没有固定数量关系。
TCP模块接收数据流程(详见图3-1):
①当接收端收到 TCP报文后,TCP模块将它们携带的应用程序数据按照TCP报文段序号依次放入TCP接收缓冲区,并通知应用程序来读取。
②接收端应用程序可以一次性将 TCP接收缓冲区中的数据全部读出,也可分次读,取决于用户指定的应用程序读缓冲区大小。
7. TCP可靠体现在,
①TCP采用发送应答机制,即:
发送端发送的每个TCP报文都必须得到接收方的应答,才认为这个TCP报文传输成功。
②TCP采用超时重传,即:
发送端在发送一个 TCP报文之后启动定时器,如果在定时时间未收到应答,它将重发该报文。
8.TCP报文最终是以 IP数据报发送,而 IP数据报到达接收端可能乱序、重复,但 TCP协议还会对接收到的 TCP报文段重排、整理,再交付给应用层。
1.1.3.1.1.2 TCP头部结构
每个TCP报文都有TCP头部信息。
TCP 的头部结构包括 固定头部结构 和 头部选项。
TCP头部结构如图3-3所示:
其中诸多字段为管理 TCP连接和控制数据流提供足够信息。
TCP头部结构(最长 60字节) = 固定头部结构(20字节) + 选项(最长40字节)。
16位端口号(port number):
告知主机该报文段是来自哪里(源端口),并传给哪个上层协议或应用程序(目的端口)的。
客户端通常使用系统自动选择的临时端口,服务器则使用知名端口(命令查看所有知名端口:cat /etc/services)
32位序号(sequence number):
一次 TCP通信(从TCP连接建立到断开)过程中某一个传输方向上的字节流的每个字节的编号。
例:
假设主机A 和主机 B进行TCP通信:
A 发送给B 的第一个 TCP报文段中,序号值被系统初始化为某个随机值 ISN(Initial Sequence Number,初始序号值)。
那么在A->B这个传输方向上,后续 TCP报文段中序号值将被系统设置为 ISN加上该报文段所携带数据的第一个字节在整个字节流中的偏移。
如:
某个TCP报文段传送数据是字节流中的第 1025~2048字节,那该报文段序号值就是 ISN+1025。
B->A方向的序号含义等同上面概念及例子。
32位确认号(acknowledgement number):
用作对另一方发送来的 TCP报文段的相应。
其值是收到的 TCP报文段的序号值加1。
例:
假设主机A和主机B进行TCP通信,
那么 A发送出的 TCP报文段不仅携带自己的序号,而且包含 B发送来的 TCP报文段的确认号。
反之,B发送出的 TCP报文段也同时携带自己的序号和对 A发送来的报文段的确认号。
4位头部长度(header length):
标识该 TCP头部有多少个 32bit字(4字节)。
因为 4位最大是15,所以 TCP头部最长是 60字节。
6位标志位:
URG: 表示紧急指针(urgent pointer)是否有效
ACK: 表示确认号是否有效。 称携带ACK标志的TCP报文段为确认报文段。
PSH: 提示接收端应用程序应该立即从TCP接收缓冲区中读走数据,为接收后续数据腾出空间
(如果应用程序不将接收到的数据读走,它们就会一直停留在 TCP接收缓冲区中)
RST: 表示要求对方重新建立连接。 称携带RST标志的 TCP报文段为复位报文段。
SYN: 表示请求建立一个连接。称携带 SYN 标志的 TCP报文段为同步报文段。
FIN: 表示通知对方本端要关闭连接了。称携带FIN的 TCP报文段为结束报文段。
16位窗口大小(window size):
TCP 流量控制的一个手段。
这里窗口指 接收通告窗口(Receiver Windows,RWND)。
它告诉对方本端 TCP 接收缓冲区还能容纳多少字节数据,这样对方可以控制发送数据的速度。
16位校验和(TCP checksum):
由发送端填充,接收端对 TCP报文段进行 CRC 算法以检验 TCP报文段在传输中是否损坏。
注意,这个校验不仅包含头部,也包含数据部分。
16位紧急指针(urgent pointer):
是一个正的偏移量。
它和序号字段的值相加表示最后一个紧急数据的下一字节的序号。
故,确切来讲,这个字段是紧急指针相对当前序号的偏移,不妨称为 紧急偏移。
TCP紧急指针是发送端向接收端发送紧急数据的方法。
TCP 头部选项字段(options)是可变长的可选信息(如上图 3-4所示)。此选项最长40字节。
kind(1字节): 说明选项的类型。
有的TCP选项部分没有后面两个字段,仅包含 1字节的 kind字段。
length(1字节): 指定该选项的总长度。
该长度包括 kind字段和 length字段占据的 2字节。
info(n字节):是选项的具体信息。
常见的 TCP 选项有7 种(如图 3-5):
kind = 0: 选项表结束选项。
kind = 1: 是空操作(nop)选项,没特殊含义。
一般用于将 TCP 选项的总长度填充为 4 字节的整数倍。
kind = 2: 是最大报文段长度选项(MSS)。
具体含义:
1.TCP 连接初始化时,通信双方使用该选项来协商最大报文段长度(Max Segment Size,MSS)。
TCP 模块通常将 MSS设置为(MTU-40)字节(减掉的这 40字节包括 20 字节的TCP头部和 20字节的 IP头部);
这样携带 TCP报文段的 IP数据报的长度就不会超过 MTU(假设 TCP头部和 IP头部都不包含头部选项,并且这是一般情况)从而避免本机发生 IP分片。
2.对以太网而言,MSS值是 1460(1500 - 40)字节。
kind = 3: 是窗口扩大因子选项。
作用:
TCP连接初始化时,通信双方使用该选项来协商接收通告窗口的扩大因子。
引申:
在 TCP 固定头部中,接收通告窗口大小是用 16位表示的,故最大为 65535字节,
但实际上TCP模块允许的接收通告窗口大小远不止这个数(为了提高 TCP 通信的吞吐量),窗口扩大因子解决了这个问题。
假设:
TCP头部中的接收通告窗口大小是 N,窗口扩大因子(移位数)是 M,
那么 TCP报文段的实际接收通告窗口大小为 N 乘 2的M次方,或者说 N左移 M位。
注意,M 的取值范围是 0 ~ 14。
启用或关闭此选项:
可以通过修改 /proc/sys/net/ipv4/tcp_window_scaling 内核变量来启用或关闭窗口扩大因子选项。
再引申:
和MSS选项一样,窗口扩大因子选项只能出现在同步报文段中,否则将被忽略。
但同步报文段本身不执行窗口扩大操作,即同步报文段头部的接收通告窗口大小就是该 TCP报文段的实际接收通告窗口大小。
当连接建立好后,每个数据传输方向的窗口扩大因子就固定不变了。
kind = 4 : 选择性确认(Selective Acknowledgment,SACK)选项。
作用:
SACK技术使 TCP模块只重新发送丢失的 TCP报文段,不用发送所有未被确认的 TCP报文段。
此选项用在连接初始化时,表示是否支持 SACK 技术。
启用或关闭:
可以通过修改 /proc/sys/net/ipv4/tcp_sack 内核变量来启用或关闭选择性确认选项。
kind = 5: SACK实际工作的选项。
该选项参数告诉发送方 本端已经收到并缓存的不连续的数据块,从而让发送端可以据此检查并重发丢失的数据块。
含义:
每个块边沿(edge of block)参数包含一个 4字节的序号。
其中块左边沿表示不连续块的第一个数据的序号,而块右边沿则表示不连续块的最后一个数据的序号的下一个序号。
这样一对参数(块左边沿和块右边沿) 之间的数据是没有收到的。
因为一个块信息占用 8字节,所以 TCP头部选项中实际上最多可以包含 4个这样的不连续数据块(考虑选项类型和长度占用的 2字节)。
kind = 8: 时间戳选项。
提供了较为准确的计算通信双方之间的回路时间(Round Trip Time,RTT)的方法,从而为 TCP流量控制提供重要信息。
启用或关闭:
我们可以通过修改 /proc/sys/net/ipv4/tcp_timestamps 内核变量来启用或关闭时间戳选项。
1.1.3.1.1.3 使用tcmpdump 观察TCP 头部信息
1.1.3.1.1.3 详解TCP 连接的建立和关闭状态
-
tcpdump 观察 TCP 连接的建立和关闭状态
2.TCP 连接的半关闭状态
TCP连接是全双工的,故允许两个方向的数据传输被 独立关闭。
即
通信连接一端关闭,发送结束报文段给对方。
告知对端本端已完成数据发送,此时还允许继续接收来自对端的数据。
直到对端也发送结束报文段以关闭连接。
这种状态就是 半关闭(half close)状态。(如图3-7所示)
实际使用半关闭的应用程序很少。
3.连接超时
1.1.3.1.1.4 详解TCP 连接两端状态转移
TCP 连接的任意一端
在任何时刻
都处于某种状态
。
下图指的是socket编程的状态转换,需要 socket 的知识。
socket TCP状态转移过程文字描述:
1.服务器端的状态转移过程
LISTEN状态:
1.被动打开: 等待客户端连接,客户端连接上即被动打开。
2.虚线 收SYN,发SYN,ACK:
服务器监听到某个连接请求(收到同步报文段(带SYN)):
1.将该链接放入内核等待队列中
2.向客户端发送带 SYN标志的确认报文段。
3.此时连接状态转为 SYN_RCVD状态。
SYN_RCVD:
1.虚线 收ACK:
若服务器成功接收到客户端发送回的确认报字段,则该连接状态转为 ESTABLISHED
(当服务器向客户端发送 带SYN标志的确认报文段后,客户端需返回 ACK确认报文段)。
2.发 FIN:
ESTABLISHED:
此种状态下连接双方能够进行双向数据传输的状态。
1.收 FIN:
当客户端主动关闭连接时(close或shutdown),发送给服务端 结束报文段(携带FIN)。
2.发 ACK
服务端收到客户端关闭连接(FIN)发送ACK 进行确认,此时状态转为CLOSE_WAIT.
CLOSE_WAIT:
1.发 FIN
服务端监测到 客户端关闭连接请求后,也会立即给客户端发送一个结束报文段(FIN)来关闭连接。
此时状态转为 LAST_ACK,与此同时等待 客户端的最后一次确认(ACK)。
LAST_ACK:
1.收 ACK
此时连接状态转为 CLOSED。
CLOSED:
2.客户端的状态转移过程
客户端主动与服务器建立连接(客户端connect调用,这一部分在图3-8 上没有):
1.发送 SYN
发送一个同步报文段,使连接转为 SYN_SENT。
2.连接建立后,可能因为两个原因失败返回, connect 调用失败会立即返回到初始 CLOSED 状态:
①connect 连接的端口不存在(未被监听) 或该端口扔处于 TIME_WAIT 状态的连接所占用。
则服务器将给客户端发送一个复位报文段,connect调用失败。
②目标端口存在,但connect 在超时时间内未收到服务器的确认字段,则connect 调用失败。
3.若客户端成功收到服务端的同步报文段和确认,则connect 调用成功返回。
状态转移为 ESTABLISHED 状态。
3.主动关闭连接(客户端和服务端都可能会发生):
1.发送 FIN
发送 FIN 同时此连接进入 FIN_WAIT_1 状态,
FIN_WAIT_1:
1.收 ACK
若 在1.发送FIN 后,同时客户端收到收到服务器专门用于确认目的的确认报文段,则连接进入 FIN_WAIT_2 状态。
2.直接收到 带确认信息的结束报文段(而不是先收到确认报文段,再接收结束报文段),此时客户端连接状态会直接转为 TIME_WAIT。
FIN_WAIT_2:
此时服务器端处于 CLOSE_WAIT状态(详见图3-8被动关闭),这一对状态时可能发生 半关闭状态。
1.收 FIN、发ACK
若此时服务器也关闭连接(发送结束报文段),则客户端将发送 给予确认 并进入 TIME_WAIT状态。
注意:连接停留在此状态的客户端连接要等待服务器发送结束报文段,才能转移至 TIME_WAIT,否则将一直停留在这个状态,此时即为半关闭状态,唯一益处是可以继续接收数据,除此无其它益处。
连接停留在此状态可能发生在:
1.客户端执行半关闭后,未等服务器关闭连接就强行退出了。
此时客户端连接由内核来管,可称为 孤儿连接(和孤儿进程类似)。
Linux 为了防止孤儿连接长时间存留在内核中,定义了两个内核变量
①指定内核能接管的孤儿连接数目
/proc/sys/net/ipv4/tcp_max_orphans
②孤儿连接在内核中生存时间
/proc/sys/net/ipv4/tcp_fin_timeout
2.
TIME_WAIT:
在这个状态,客户端连接要等待一段长为 2MSL(Maximum Segment Life,报文段最大生存时间),才能完全关闭。
MSL 是 TCP 报文段在网络中的最大生存时间,Linux文档建议值是 2min。
TIME_WAIT状态存在的两点原因:
1.可靠的终止TCP连接
原因:
假设服务器接收客户端结束报文段后发送服务器的 TCP结束报文段时丢失,那么服务端将重发结束报文段,
此时客户端需要停留在某个状态(即此TIME_WAIT)以处理重复收到的结束报文段,
否则,客户端将以复位报文段来回应服务器,服务器则认为这是一个错误,因为它期望的是确认报文段。
2.保证让迟来的 TCP 报文段有足够的时间被识别并丢弃(这种情况发生在服务端)
原因:
在Linux 系统上,一个 TCP 端口不能同时打开多次(两次及以上)。
当一个TCP连接处于 TIME_WAIT 状态时,不能立即使用该连接占用着的端口立即建立一个和刚关闭的连接相似的连接(同IP地址和端口)。
这个新的,和原来的相似的连接被称为原来连接的化身(incarnation)。
新的化身可能接收到了原来连接的 应用程序的TCP报文段(迟到的报文段),这显然是不应该发生的。
引申:
TIME_WAIT状态等待 2MSL 的原因:
TCP 报文段 的最大生存时间是 MSL,
故 坚持 2MSL 时间的 TIME_WAIT 状态能够保证网络上两个传输方向上尚未被接收、迟到的 TCP报文段都已经消失(被中转路由器丢弃)。
因此,一个连接的新化身可以在 2MSL 时间后安全建立。
实际开发时,
1.服务端 当一个连接处于 TIME_WAIT 状态还占着端口,程序将无法启动(直到 2MSL 超时时间结束)。
2.而客户端不会无法启动(除非指定端口和IP和处于TIME_WAIT状态的服务端一致),
因为客户端使用系统自动分配的临时端口号来建立连接,由于随机性,临时端口号一般不会和 处于 TIME_WAIT 状态的端口一致,一般可以立即重启。
可通过 socket 选项 SO_REUSEADDR 来强制进程立即使用处于 TIME_WAIT状态的连接占用的端口。
TCP 连接的建立和断开过程中客户端和服务器的状态变化如下图所示:
1.1.3.1.1.4 详解TCP 固定头部复位报文段
在某些特殊条件下,TCP 连接的一端会向另一端发送携带 RST 标志的报文段,即复位段,以通知对方关闭连接或重新建立连接。
下面是产生复位报文段的 3 种情况:
-
访问不存在的端口
-
异常终止连接
TCP 提供了异常终止一个连接的方法,即给对方发送一个复位报文段。 一旦发送了复位报文段,发送所有排队等待发送的数据都将被丢弃。 实际开发中,应用程序可以使用 socket 选项 SO_LINGER 来发送复位报文段,以异常终止一个连接。
-
处理半打开连接
1.1.3.1.1.5 详解 TCP 数据流
TCP 报文段所携带的应用程序数据按照长度分为两种:
1.交互数据
交互数据仅包含很少的字节。
使用交互数据的应用程序(或协议)对实时性要求高,比如 telnet、ssh等。
2.成块数据
成块数据的长度则通常为 TCP 报文段允许的最大程度。
使用成块数据的应用程序(或协议)对传输效率要求高,比如 ftp。
-
交互数据流
-
成块数据流
1.1.3.1.1.6 详解 带外数据
概念:
有些传输层协议具有带外(Out Of Band,OOB)数据的概念,用于迅速告诉对端本端发生的重要事件。
发送时机:
带外数据比普通数据(也称为带内数据)具有更高优先级,应该总是立即被发送
(不论发送缓冲区是否有排队等待发送的普通数据)。
发送方式:
带外数据的传输,
1.可以使用一条独立的传输层连接(真正的带外数据传输)
2.也可以映射到传输普通数据的连接中(类似带外数据传输,形似,只是称为带外数据传输,不是真正的带外数据传输)
如 TCP的带外数据实现。
实际应用中,带外数据的使用很少见,已知的仅有 telnet、 ftp等。
UDP 没有实现带外数据传输
,TCP也没有真正的带外数据
。
TCP 带外数据实现:
利用头部中的紧急指针标志和紧急指针两个字段,给应用程序提供一种紧急方式。
此方式就是利用传输普通数据的连接来传输紧急数据。
TCP 这种紧急数据含义和带外数据类似,故本文将 TCP紧急数据称为带外数据。
1.1.3.1.1.7 详解 TCP 超时重传
1.1.3.1.1.8 详解 TCP 拥塞控制
概念:
1.提高网络利用率
2.降低丢包率
3.保证网络资源对每条数据流的公平性。
TCP拥塞控制 4 部分:
1.慢启动(slow start)
2.拥塞避免(congestion avoidance)
3.快速重传(fast retransmit)
4.快速回复(fast recovery)
Linux拥塞控制算法:
reno、vegas、cubic等。
上诉算法部分或全部实现了上面拥塞控制 4 部分。
/proc/sys/net/ipv4/tcp_congestion_control 文件指示当前机器使用的拥塞控制算法。
拥塞控制控制的是什么?:
拥塞控制的最终受控变量是 发送端向网络一次连续写入(收到其中第一个数据的确认之前)的数据量,称为 SWND(send window,发送窗口)。
发送端最终以 TCP 报文段来发送数据,所以 SWND 限定了发送端能连续发送的 TCP 报文段数量。
这些 TCP报文段的最大长度(仅指数据部分)称为 SMSS(Sender Maximum Segment Size,发送者最大段大小),其值一般等于 MSS(Max Segment Size,MSS,tcp 最大报文段长度)。
-
慢启动 和 拥塞避免
-
快速重传 和 快速恢复
1.1.3.2 UDP
UDP(User Datagram Protocol,用户数据报协议)
与TCP相反,UDP为应用层提供不可靠
、无连接
、基于数据报
的服务。
不可靠:
如果数据在传输中途丢失,或者目的端通过数据校验发现数据错误而丢弃,则UDP只是简单的通知应用程序发送失败。
故,UDP应用程序通常要自己处理数据确认、超时重传等。
无连接:
通信双方不保持一个长久联系,故应用程序每次发送数据都要明确指定接收端的地址(IP地址等)。
基于数据报:
是相对基于流的服务而言的。
每个UDP报都有一个长度,接收端必须以该长度为最小单位将其所有内容 一次 读出,否则数据将被截断。
1.1.3.2.1 UDP 详解
UDP发送接收流程(详见图3-2):
①发送端应用程序每执行一次写操作,UDP 模块就将其封装成一个UDP数据报并发送之。
②接收端必须及时针对每一个 UDP数据报执行读操作,否则就会丢包。
③如果用户没有指定足够的应用程序缓冲区来读取 UDP数据,则 UDP数据将被截断。
UDP 协议和 IP协议一样,提供不可靠服务。它们都需要上层协议来处理数据确认和超时重传。
1.1.3.3 SCTP
SCTP(Stream Control Transmission Protocol,流控制传输协议)
相对较新的传输层协议,
为在因特网上传输电话信号而设计。
自行百度了解。
1.1.4 应用层
应用层负责处理应用程序逻辑,在用户空间实现(少数服务器程序在内核中实现。)。
数据链路层、网络层、传输层负责处理网络通信细节,这部分必须即稳定又高效,因此他们都在内核空间中实现。
应用层协议很多:
如:
telent,远程登录协议,能在本地完成远程任务。
OSPF(Open Shortest Path First,开放最短路径优先)是一种动态路由更新协议,用于路由器之间的通信,以告知对方各自的路由信息。
DNS(Domain Name Service,域名服务)提供机器域名到IP地址的转换。
注意:
1.ping是应用程序,不是协议。利用ICMP报文检测网络连接。
2.应用层协议(或程序)可能跳过传输层直接使用网络层提供的服务:
如:
ping程序和OSPF。
3.应用层协议(或程序)通常即可以使用TCP服务,又可以使用UDP服务,
如;
DNS协议。
4.命令查看所有知名应用层协议,以及都能使用哪些传输层服务,如下
cat /etc/services
1.2 封装
对于顶层目标服务来看,封装和分用似乎没发生过。
TCP/IP协议族间通过 封装
实现上层协议使用下层协议提供的服务。
封装流程(如下图所示):
应用层数据发送到物理网络之前,将沿着协议栈 从上往下 依次传递。
每层协议都在上层数据基础上增加自己的 头部信息(有的还包括尾部信息),以实现该层功能。
1.2.1 传输层封装
1.2.1.1 TCP封装
经过TCP协议封装后的数据称为TCP报文段(TCP message segment),封装过程如下图所示。
如图:
TCP报文段组成位置在内核空间。
TCP报文段 = IP数据报的数据部分 = TCP头部信息 + TCP内核发送缓冲区数据(即用户空间应用程序发送据)
应用程序利用TCP发送数据流程:
发送端应用程序使用send(或write)向TCP连接写入数据
-> 内核TCP模块将这些数据复制到与该连接对应的TCP内核缓冲区
-> 然后TCP模块调用IP模块服务,传递给IP服务 TCP报文段。
1.2.1.2 UDP封装
UDP封装后的数据称为UDP数据报(UDP datagram)。
与TCP封装类似。不同之处如下:
1.由于UDP的服务不可靠,故UDP不为应用层数据保存副本,当一个数据报发送成功,那该数据报就会被丢弃。
2.由于1.的特性,若应用程序检测到发送数据未被接收端正确接收,并打算重发,只能重新从用户空间再拷贝到UDP内核发送缓冲区。
1.2.2 网络层封装
1.2.2.1 IP封装
IP封装后的数据称为IP数据报(IP datagram)。
IP数据报 = 头部信息 + 数据部分 (数据部分就是一个TCP报文 或 UDP报文 或ICMP报文)
1.2.3 数据链路层封装
1.2.3.1 帧
数据链路层封装的数据称为帧(frame)。
帧的最大传输单元(Max Transmit Unit,MTU):
即帧最多能携带多少上层协议数据(如IP数据报),通常受网络类型限制。
如下图1-6,MTU=1500字节。
因此根据数据报长度可能被分片(fragment)传输。
根据传输媒介区分帧分类:
1. 以太网传输的是 以太网帧(ethernet frame),如下图示例。
2. 令牌环网络上传输的是 令牌环帧(token ring frame)。
以太网帧 组成:
通信双方: 6字节目的物理地址 + 6字节源(本地)物理地址
类型:标识上层协议的类型字段,每个上层协议对应一个数值。
CRC :对帧的其他部分提供循环冗余校验。
1.3 分用
对于顶层目标服务来看,封装和分用似乎没发生过。
分用(demultiplexing)是一个过程(如下图所示):
当 数据链路层的帧 到达目的主机,将沿着协议栈 自底向上 依次传递;
-> 各层协议依次处理帧中本层负责的 头部数据,以获取所需信息;
-> 并最终将处理后的帧 交给目标应用程序。
分用 依靠帧头部信息中的类型字段实现。
例:
IP、ARP、RARP都用帧传输。
1.若主机收到的以太网帧中类型字段值为 0x800 ,则帧数据部分为IP数据报,以太网驱动程序就将帧交付给IP模块。
2.若主机收到的以太网帧中类型字段值为 0x806,则帧数据部分为RARP请求或应答报文,以太网帧就将帧交付给RARP模块。
引申:
1.ICMP、TCP、UDP都使用IP协议,故IP数据报头部采用 16位协议(protocol)字段来区分。
2.TCP、UDP则通过其 头部中 16位端口号(port number)字段区分上层应用程序。
3.因2.,所有应用程协议都有自己的端口号。如HTTP协议(Hyper-Text Transfer Protocol,超文本传送协议)端口号为80.DNS协议端口号为53等.
1.4 网络测试
此处简写,详见书中内容。
通过抓取ARP和DNS在网络上传输的以太网帧,并查看其中的以太网帧头部、IP数据报头部等信息,深入理解TCP/IP协议族的工作机制。
1.4.1 网络准备
1.4.2 ARP协议工作原理
ARP(Address Resolution Protocol,地址解析协议)
地址解析:
就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。
ARP基本功能:
就是实现任意网络层地址 到 任意物理地址的转换,以保证通信的顺利进行。
此处只讨论 IP地址到以太网地址(此处只是MAC地址)的转换。
ARP协议工作原理:
主机向自己所在的网络广播一个ARP请求(包含目标机器的网络地址)。
-> 网络上其它机器都将收到这个请求
-> 但只有被请求的目标机器回应一个ARP应答,其中包含自己的物理地址。
1.4.2.1 以太网ARP请求/应答报文详解
如下图:
硬件类型: 物理地址类型。 如1 表示MAC地址。
协议类型: 要映射的协议地址类型。 如0x800 表示IP地址。
硬件地址长度: 对应硬件类型,如 硬件类型为1,此处就是MAC地址长度为6.
协议地址长度: 对应协议类型,如 协议类型值为ox800,此处就是IP(若为v4)地址长度4.
操作 (4种): ARP请求 值为1,ARP应答 值为2,RARP请求 值为3,RARP应答 值为4。
剩余四个字段:
发送请求时,发送端除 目的端以太网地址 其它三个都填,以构建ARP请求。
收到请求时,目的端填入 目的端以太网地址,并交换目的端地址和发送端地址,以构建ARP应答。
引申:
一般情况:
ARP请求/应答报文长度 46字节 = 28字节(ARP请求应答报文长度,图1-9) + 18字节(以太网帧头部及尾部长度)
特殊情况(有的实现要求以太网帧数据部分长度至少为46字节,此时ARP请求/应答报文要填充一些字节):
ARP请求/应答报文长度 至少64字节 = 28字节(ARP请求/应答报文长度,图1-9)+ 至少18字节(ARP请求/应答报文填充字节) + 18字节(以太网帧头部和尾部)
1.4.2.2 以太网ARP高速缓存查看和修改
通常,ARP维护一个高速缓存,其中包含经常访问的IP地址到物理地址的映射。
以此避免重复ARP请求,提高发送数据包速度。
Linux查看和修改ARP高速缓存命令如下:
1.4.2.3 使用tcpdump观察ARP通信过程
想了解的可以仔细看看。
1.4.3 DNS工作原理
通常,我们使用机器的域名访问这台服务器,而不直接用IP地址,如访问互联网上的网站。
机器域名转换为IP 使用 域名查询服务。
域名查询服务实现方式有很多,如:
1.NIS(Network Information Service,网络信息服务)
2.DNS
3.本地静态文件
4...等等
DNS简介:
1.DNS是一套分布式的域名服务系统。
2.每个DNS服务器上都存放大量的 机器名和IP地址 的映射,且是动态更新的。
3.众多网络客户端程序都是用DNS来向DNS服务器查询目标主机的IP地址。
4.DNS查询和应答报文详见下一小节。
1.4.3.1 DNS查询和应答报文详解
注:此处 标识 和 标志 严格区分,含义不同。
16位标示识: 标记一对DNS查询和应答,以此区分一个DNS应答是哪个DNS查询的回应。
16位标志: 用于协商具体的通信方式和反馈通信状态。 详见下面小节 1.4.3.1.1 DNS头部16位标志字段详解
接下来四个字段(16位问题个数、16位应答资源记录个数、16位授权资源记录数目、16位额外资源记录数目):
分别指出DNS报文的最后4个字段的资源记录数目。
对查询报文来说:
一般包含1个查询问题,而 应答资源记录个数、授权资源记录数目、额外资源记录数目则为0.
对应答报文来说:
应答资源记录个数至少为1,而授权资源记录数目和额外资源记录数目可为0或非0.
查寻问题:使用一定格式, 详见 下面小结 1.4.3.1.2 DNS头部 查询问题格式。
应答、授权几额外信息: 都使用资源记录(Resource Record,RR)格式。详见下面小结 1.4.3.1.3 DNS头部 资源记录格式。
1.4.3.1.1 DNS头部16位标志字段详解
1.4.3.1.2 DNS头部 查询问题格式
1.4.3.1.3 DNS头部 资源记录格式
1.4.3.2 Linux下访问DNS服务
1.4.3.3 使用tcpdump观察DNS通信过程
1.5 socket 和TCP/IP协议族的关系
如以上章节可知,数据链路层、网络层、传输层协议实在内核中实现的。
故操作系统需要实现一组系统调用,使应用程序能够使用这些协议提供的服务。
实现这组系统调用的API(Application Programming Interface,应用程序接口)主要有两套:
1.socket,现在的主流。
2.XTI,已不再使用。
socket实现的API提供两点功能:
1.将应用程序数据
从用户缓冲区中复制到TCP/UDP内核发送缓冲区,以交付内核来发送数据;
或者是从内核TCP/UDP接收缓冲区中复制数据到用户缓冲区,以读取数据。
2.应用程序可以通过它们来修改内核中各层协议的某些头部信息或其它数据结构,
从而精细的控制底层通信行为。
如:
setsockopt函数来设置IP数据报在网络上的存活时间。
socket
是一套通用网络编程接口
,不但可以访问内核中的TCP/IP协议栈
,还可以访问其它网络协议栈
(如X.25协议栈、UNIX本地域协议栈等)