# tcp/ip详解-协议
## 概述
### 四层协议系统
TCP/IP协议族通常被认作是四层协议系统,每层分别负责不同功能,由下到上分别为
1. 链路层(数据链路层)
2. 网络层:负责分组在网络中的活动,例如分组的选路等。网络层协议通常包含ip协议,icmp协议,igmp协议
3. 传输层:负责在两台主机之间提供端到端的通信,tcp/ip协议族中,传输层协议有TCP和UDP
4. 应用层:负责处理特定的协议细节
> #### IGMP
> IGMP为internet组管理协议,其用于将udp数据报广播到多个主机
> #### ARP & RARP
> ARP和RARP是某些网络接口使用的特殊协议,用于转换ip层和链路层所使用的地址。
### ipv4地址
ipv4地址大小为4字节,其分类如下所示:
- A类: 0~127.xxx.xxx.xxx (第一bit为0)
- A类地址组成为`0` + 7bit `net id` + 24bit `host id`
- B类: 128~191.xxx.xxx.xxx (前两位bit为10)
- B类地址组成为`10` + 14bit `net id` + 24bit `host id`
- C类: 192~223.xxx.xxx.xxx (前三位bit为110)
- `110` + 21bit `net id` + 8bit `host id`
- D类:224~239.xxx.xxx.xxx (前四位bit为1110)
- `1110` + 28bit `multicast group id`
- E类:240~255.xxx.xxx.xxx (前五位bit为11110)
- `1111` + 28bit `reserved for future use`
### 协议层封装
当应用向网络中发送数据时,数据被送入协议栈中,从上到下,从应用层一直到数据链路层,每层协议都会为上一层提交的数据添加首部或尾部信息,并且将修改后的数据传递给下一层。
其中,个数据层叫法如下:
- tcp传递给网络层的数据被称为tcp段(tcp segment)
- 网络层传递给数据链路层的数据被称为ip数据报(ip datagram)
- 通过以太网传输的数据流被称为帧(frame)
> #### 以太网数据帧
> 以太网数据帧,其以太网首部长度为`14字节`,尾部为`4`字节。
>
> 且数据帧的长度必须位于46字节~1500字节之间
> #### packet
> 更确切说,ip传递给数据链路层的,是分组(packet),分组可能是ip数据报(ip datagram),也可能是ip数据报的一个片(fragment)
> #### UDP datagram
> UDP数据和TCP数据类似,UDP传递给IP的数据被称为UDP数据报(UDP datagram)。
>
> UDP首部长度为8字节。
> #### IP首部
> 除了TCP, UDP向IP传递数据外,`ICMP`和`IGMP`也会向IP传输数据,即使ICMP和IGMP和IP都位于网络层。
>
> 故而,IP首部中存在长度为8bit的范围,用于标识传递给ip的协议。该字段中,1表示ICMP,2表示IGMP,6表示TCP,17表示UDP。
> #### TCP/UDP首部中的端口号
> 有很多程序都使用UDP/TCP来传递数据,故而,需要通过端口号来标识程序。
>
> 端口号长度为16bit,范围为`0 ~ 65535`,tcp和udp把源端口号和目标端口号存放在报文首部。
> #### 数据链路层标识传递数据的协议
> 类似于ip,网络接口不仅会被ip传递数据,ARP和RARP即使和网络接口一样位于数据链路层,但是仍然会向网络接口传递数据。故而,以太网帧的首部同样存在一个16bit的区域用于标识传递数据的协议。
### 分用
当目标主机接收到网络上传输到的数据时,会沿着协议栈至底向上传递。
在每层协议从下层协议接收到数据时,会去掉本层的首部/尾部。每层协议都会检查首部中的标识区域,用于判断上层协议,并将去掉首部/尾部后的数据传递给上层协议。这个过程被称之为`分用`。
各层协议其对应上层协议可能为:
- 以太网网络接口:上层协议可能为`ARP, RARP, IP`
- IP协议:上层协议可能为`ICMP, IGMP, TCP, UDP`
- TCP, UDP: 上层可能为各种应用层序,通过来源ip、来源端口号、目标端口号进行判断
## 数据链路层
TCP/IP中以太网数据报的封装格式在`RFC894`中,IEEE 802网络的ip数据报则是封装在`RFC 1042`中。
对于Internet主机,必须要求:
- 必须能接受和发送RFC 894封装格式的分组
- 应该能接受`RFC 894`和`RFC 1042`封装格式的分组
- 也许能发送`RFC 1042`封装格式的分组
RFC 894是最常被使用的封装格式,如下为两种格式的定义。
### RFC 1042
`目的物理地址(6字节)` + `源物理地址(6字节)` + `长度(2字节)` + `802.2 LLC(3字节)` + `802.2 SNAP(5字节)` + `数据(38 ~ 1492字节)` + `CRC(4字节)`
其中,字段含义如下:
- `目的/源地址`:目的/源地址均代表物理地址,长度均为6字节48bit
- `长度`: RFC 1042中长度代表`长度`字段后续`不包含CRC`的长度
- `CRC`: CRC字段用于帧内字节差错的循环冗余校验,
### RFC 894
`目标物理地址(6字节)` + `源物理地址(6字节)` + `类型(2字节)` + `数据(46 ~ 1500字节)` + `CRC(4字节)`
其中,字段定义如下:
- `类型`:以太网帧中`类型`字段定义了被封装数据的协议类型:
- `0800`: 如果类型字段的值为`0800`,那么代表后续数据为IP数据报
- `0806`: 如果类型字段值为`0806`,那么代表后续数据为ARP请求/应答
- `0835`: 如果字段类型为`0835`,那么代表后续数据为RARP请求/应答
RFC 894和RFC 1042定义的帧必须有最小长度要求,RFC894要求数据长度至少为46字节,而RFC1042要求数据长度至少为38字节,为了保证被封装数据长度达到最小长度要求,必须`在长度不足时向剩余空间填充pad字节`。
### 环回接口
大多数产品都支持环回接口(loopback interface),允许运行在同一台机器上的程序之间相互进行通信。在A类地址中,`127`开头的地址即是为环回端口预留。根据惯例,大多数系统把`127.0.0.1`分配给这个接口,并命名为localhost。
> 一个传给loopback interface的ip数据包不能在任何网络上出现。
### MTU
RFC 894和RFC 1042对数据帧长度有限制,数据部分长度最多不能超过1500和1492字节,该限制被称为最大传输单元(MTU)。
如果ip层需要传输一个大于MTU的数据报,那么该数据报需要被分片为若干片,令每一片都小于MTU限制的大小。
## ip:网际协议
ip协议为tcp/ip中最为核心的协议,TPC/UDP/ICMP/IGMP数据都以ip数据报的形式进行发送。
### IP协议特性
#### 不可靠
ip协议是不可靠的,其并不保证传输的数据被成功送达目标端。如果在ip数据包发送途中,某个路由器暂时用完缓冲区,那么ip协议存在简单的错误处理算法:丢弃该ip数据报,并且向信源段发送ICMP消息报。
#### 无连接(connectionless)
`无连接`代表ip数据报并不维护任何状态信息,每个数据报的处理是相互独立的。故而,`ip数据报可以不按发送的顺序进行接收`。
> 例如,发送端向接收端发送了两个连续的数据报(先发送A再发送B),每个数据报都会独立的进行路由选择,`数据报B可能和数据报A选择不同的路线,故而数据报B可能在数据报A之前到达`。
### Ip首部
ip首部的格式如下所示:
#### 首部各字段含义
##### 版本
版本长度为4bit,代表ip协议版本号,ipv4场景下该值为4
##### 首部长度
首部长度指首部占32bit字的数目,包括`选项`。首部长度占4bit,故而ip协议首部最长为`(2^4-1) * (32/8) bytes = 15 * 4 bytes = 60bytes`。
对于普通ip数据报(没有任何选项),其首部长度为20字节,故而首部长度的值为`20 / 4 = 5`。
##### 服务类型(TOS)
TOS字段长度为8位,其组成如下:
- `优先权`子字段:3bit,现在会被忽略
- `TOS`子字段:4bit
- 未用位:1bit,该位目前没有被使用,但是必须置为0
TOS子字段其4bit分别代表如下含义:最小时延、最大吞吐量、最高可靠性、最小费用。其4bit中最多只能有一bit被置为1,如果4bit均位0,则代表是一般服务。
> 目前,`绝大多数tcp/ip实现都不支持TOS特性`.
##### 总长度
总长度指整个ip数据报的长度,单位为字节。例如用首部长度和总长度字节,就能得知ip数据报中内容起始位置和长度。
总长度字段,其长度为16位,故而ip数据报最大长度为`65535`字节。但是,受数据链路层协议MTU影响,当ip数据报被分片时,总长度的值也会随着分片而变化。
> 而且,由于数据链路层协议拥有传输数据的最小长度限制(例如以太网协议其数据最小长度为46字节)。而ip数据报其长度可能小于46字节。
>
> 当ip数据报大小小于46字节时,以太网协议会对不足46字节的部分进行填充,故而,总长度字段能够实标识该数据报的实际大小,去除被以太网协议填充的字节。
##### 标识
标识字段为16bit,其会唯一标识主机发送的每一份数据报,通常每发送一份报文,其值会加一。
##### 标志字段和位偏移字段
标志字段和位偏移字段和分片操作有关,分别为3bit和13bit。
##### TTL
TTL字段设置了数据报可以经过的最多路由器个数,其指定了数据报的生存时间。`TTL字段的长度为8bit`。
TTL字段的初始值由源主机及逆行设置,通常为32或64。每当数据报经过一个路由器的处理,那么ttl字段的值就会减1。`当ttl字段的值为1时,该数据报将会被丢弃,并发送ICMP`报文来通知源主机。
##### 协议
协议字段长度为8 bit,可以用于标识IP协议的上层协议。通过协议字段,可以实现对ip协议的分用。
##### 首部校验和
首部校验和字段长度为16bit,其根据ip首部计算校验和码,并不对首部后的数据进行计算。
关于数据的校验和,其在上层TCP,UDP,ICMP,IGMP协议,协议首部中均含有`同时覆盖首部和数据内容的校验和码`。
> 在计算首部校验和时,首先将校验和字段设置为0,之后对首部中每个16bit字进行二进制反码求和,并将结果保存到校验和字段中。
>
> 接收方在接收到数据报后,会对首部每个16bit进行二进制反码求和,并且要求计算结果全为1。如果校验错误,那么ip协议会要求丢弃接收到的数据报,但是不生成差错报文,由上层协议发现丢失的报文并进行重传。
> 在路由器在针对ip报文进行转发时,通常需要将ip数据报的ttl减一,故而其需要对ip首部校验和的值进行增加(并不需要重算整个ip首部的校验和).
##### 源ip地址和目标ip地址
每个ip数据报都包含源ip地址和目标ip地址,均为4字节32bit。
##### 选项
最后,选项字段是一个可变长度的可选信息。这些选项很少被使用,也并非所有的主机和路由器都支持这些选项。
选项字段通常以32bit作为界限,在必要时候会填充0,保证ip首部长度始终为32bit(4字节)的整数倍。
### ip路由选择
通常来说,当目标主机和源主机位于同一个子网内时,ip数据报可以直接发送到目标主机。但是,当目标主机和源主机跨网络通信时,源主机则是将ip数据报发送给默认路由器,并由路由器来转发该数据报。
IP发送数据报场景如下:
- 从上层TCP, UDP, ICMP, IGMP中接收数据报并进行发送
- 从一个网络接口接收数据报并进行发送(转发数据报)
ip层在内存中拥有一个路由表,当接收到数据报(从上层协议或从网络接口)时,会搜索该路由表,
- 如果数据报来源为某个接口时,首先检查目标ip地址是否为本机ip地址之一或广播ip地址
- 如果是,数据报则被送到ip首部中协议字段所定义的协议模块进行处理;
- 如果不是上述地址之一,则处理逻辑如下
- 如果ip层被设置为路由器功能,则对数据报进行转发
- 如果ip层未被设置为路由器功能,则丢弃该数据报
> 故而未被设置为路由器功能的主机,在网络接口接收到ip数据报时,首先会判断该ip数据报是否目标地址为本机的ip或广播(ip数据报是否是发送给自己的)。如果是则传递给上层协议模块;如果不是则丢弃该ip数据报
路由表每一条记录都包含如下信息:
- 目标ip地址:可以是主机地址,也可以是网络地址
- 下一跳路由器的ip地址,或是位于直接相连网络中的ip地址
- 标志:标识目的ip地址是主机还是网络
- 指定数据报应该被传输给哪个网络接口(不同的网络接口连接不同的子网,路由表即使将指定ip地址的请求路由到指定的网络接口)
ip路由选择是逐跳(hop-by-hop)进行的。
ip路由选择主要执行如下逻辑:
- 搜索ip路由表,找到能够和目的ip完全匹配的条目,如果能找到,发送
- 搜索路由表,找到能与目的网络号匹配的条目,如果找到,发送
- 搜索路由表,找到default条目,发送
如果上述尝试都失败,那么该数据报将无法被传达,会向生成数据报的应用程序发送`主机不可达`或`网络不可达`的错误。
### 子网
对于A类或B类地址,其分别能容纳`2^24-2`或`2^16-2`数量的主机,同一网络中并不需要这么多主机,故而通常会建立子网,将host id分割为子网号和主机号,示例如下。
#### 组建子网示例
B类地址组建子网示例如下:
- 网络号: 16位 (`01` + 14bit)
- 子网号:8位
- 主机号:8位
上述示例中,将16bit host id中的8位划分给子网,8位划分给主机号,故而允许有254个子网和254台主机(全0或全1地址无效)。
> 在根据host id划分子网和主机位数时,并不要求以字节划分,故而,16位host id可以划分4bit给子网,12bit给主机,故而能拥有14个子网,每个子网最多拥有16382台主机
### 子网掩码
子网掩码用于标识host id中有多少bit用于子网号,有多少bit用于主机号。和ip地址类似,子网掩码也是32bit大小,含义如下:
- 分配给net id和子网号的bit值为1
- 分配给主机号的bit值为0
示例如下:
假设一个B类地址,`172.19.23.153`,其子网掩码为`255,255.255.0`,代表16bit的host id中,有8位被分配给子网,8位被分配给主机。
### endianness
endianness代表字节在内存中的排列顺序,在不同计算机操作系统中,endianness可能有所不同。故而,在不同主机通过网络进行数据交换时,必须通过相同的endianness进行数据的发送和接收。
endianness通过存在两种格式:`big endian`和`little endian`:
- big endian:首先存储MSB,即MSB位于内存地址较低的字节
- little endian:首先存储LSB,即LSB位于内存地址较低的字节
#### big endian
在big endian system中, `most significant byte`(MSB)存储在内存的低位地址上。例如,对于32 bit的integer `0x12345678`,其在big endian system中的存储顺序如下:
```
Address: 00 01 02 03
Data: 12 34 56 78
```
其中,`0x12`为MSB,存储在内存最低位地址`00`上,而`0x78`则是存储在最高位地址`03`上。
#### little endian
在little-endian system中,`least significant byte`(LSB)存储在内存的较低位地址上。例如,对于32 bit的integer`0x12345678`,其在little endian system中存储顺序如下:
```
Address: 00 01 02 03
Data: 78 56 34 12
```
其中,`0x12`位MSB,其存储在最高位地址上`03`上
#### MSB & LSB
MSB & LSB的概念可以参照如下十进制示例。
> 对于10进制数`2984`,将其个位修改为`5`会导致整体数值加1,而将千位`2`改为`3`则将导致整体数值增加`1000`,故而,可看作千位相较于个位`more significant`。
MSB和LSB的定义如下:
- MSB: The byte that holds the highest position value
- LSB: The byte that holds the lowest position value
#### 网络字节传输顺序
网络字节传输中,通常都按照`big endian`字节序来进行传输。
操作系统中,`unbuntu`和`windows`都采用`little endian`。
## tcp
### tcp服务
尽管tcp和udp都同样使用ip层,但是tcp却提供和udp完全不同的服务。tcp提供了一种面向连接的、可靠的字节流服务。
#### 面向连接
面向连接代表两个tcp应用在通信时,必须要先建立一个tcp连接。在一个tcp连接中,仅有两方进行通信,广播和多播无法应用于tcp连接。
#### 可靠性
tcp通过如下方式来提供可靠性:
- 数据应该被分割成tcp认为最适合发送的数据块,由tcp传递给ip的信息单位被称为报文段或段(segment)。
- 当tcp发出一个segment后,其将启动一个定时器,并等待目的端收到该报文的确认。如果在指定时间内未接收到目的端的确认(ack),将针对该报文段进行重传
- 当tcp收到来源于tcp另一端的数据,其将回复一个确认,该确认并非立即发送,而是通常会等待一段时间(几分之一秒)
- tcp将会维护其首部与数据的校验和,其是一个端到端的校验和,目的是检测数据在传输过程中是否发生变化。
- 如果校验和不符,那么tcp将会丢弃接收到的报文段,并且不针对该报文段作确认(超时重传)
- tcp数据段会通过ip数据报来进行传输,而ip数据报并不会保证到达的顺序,故而tcp数据段也可能会乱序到达
- tcp会针对接收到的数据段集合重新进行排序,确保接收到的数据能被成功交付给应用层
- 因为ip数据报可能会发生重复,故而tcp需要对重复数据进行丢弃
- tcp还能提供流量控制,tcp的每一方都有大小固定的缓冲空间,tcp接收方`只允许tcp发送方发送接收方缓冲区所能容纳的数据`。
- 这将避免发送方发送过快导致接收方缓冲区溢出,用于协调发送方发送速率
#### 字节流服务
两个应用之间可以通过tcp连接交换8bit字节构成的字节流。`tcp不在字节流中插入任何记录标识符`。
故而,当发送方先发送10字节,再发送20字节,最后发送50字节时,`tcp的接收方无法获知tcp发送方发送了几次以及每次发送多少字节`。
> 在发送方分3次发送了80字节的场景下,接收方接收数据的次数以及每次接收的字节数并不一定和发送方相同。
>
> 例如,发送方分三次,每次发送10,20,50字节的数据;但接收方可能分四次,每次接收20字节来接收发送方发送的数据。
>
> tcp并不保证发送方的发送数据次数以及每次发送数据大小与接收方完全相同,tcp可靠性只保证发送方分多次发送的数据都能被接收方正确接收到,对于接收次数和每次接收字节大小没有限制。
此外,tcp对于字节流内容并不做任何解释,tcp不知道传输的数据是二进制数据或文本数据,对于数据内容的解释由tcp连接双方的应用层来进行。
### tcp首部
tcp数据被封装在一个ip数据报中,其格式如下:
```
ip首部 + tcp首部 + tcp数据
20字节 20字节
```
tcp首部的数据格式如下,如果不考虑选项字段,那么tcp首部的大小为20字节
tcp首部各字段含义如下:
#### 源端口号,目的端口号
源端口号和目标端口号长度均为16位,范围为`0~65535`。
源端口号和目的端口号用于查找发送方和接收方的应用程序进程,`源端口号 + 目的端口号 + 源ip地址 + 目的ip地址`这个四元组能够唯一确定一个tcp连接。
一个ip地址和一个端口号也被成为一个socket,socket pair(源ip+源端口号+目标ip+目标端口号)则也是可以唯一确定一个tcp连接双方。
#### 序列号
序列号长度为32bit,用于标识tcp发送端向tcp接收端发送的数据字节流,其表示在当前报文段中的第一个数据字节。
> 如果将字节流是两个应用程序间的单向流动,则tcp则通过序列号对每个字节进行计数。序号是无符号的32bit整数,并且会在达到最大值(2^32-1)后重新从0开始计数。
##### tcp连接建立
当创建一个新的tcp连接时,控制位中的`SYN`标志位为1,序列号字段则包含主机选择的初始序号ISN(initial sequence number)。
建立连接后,主机发送数据的第一个字节,其序列号为ISN+1,因为SYN标志会消耗一个序号。
#### 确认序列号
确认序列号长度为32bit。
每个传输的字节都会被计数,序列序列号的值则是ack的发送方希望接收到的下一个序列号,`即确认序列号应该为已经接收到的数据字节序列号+1`。
发送ack无需任何代价,32bit的确认序列号和ack标志位都被包含在tcp首部中,故而当连接建立后,发送的tcp报文段中总是包含确认序列号字段,ack标志位也总是为1.
##### 全双工
tcp为应用层提供了全双工的服务,其意味着数据能够在两个方向上独立的进行传输,连接的双方必须维护两个方向上的传输序列号。
##### 选择性确认
tcp协议并不能进行选择性确认。tcp中的ack只能针对`位于xxx位置之前的数据已经全部被接收`这种场景进行确认,但是对如下场景,tcp协议并不支持:
- `1-1024`字节已经被接收,并且接收到另一个报文段`2049~3072`,此时接收方无法针对新的报文`2049~3072`进行ack,因为`1025~2048`尚未被接收,故而接收方只能发送确认序列号为1025的ack
- tcp也无法执行nack操作,例如,`1025~2048`报文段被接收,但是校验和校验失败,那么接收方也只能发送确认序列号为1025的ack,无法对校验失败的报文段执行nack操作。
#### 首部长度
首部长度字段代表tcp首部32bit字的长度,即单位为4字节。首部长度字段占4bit,即代表tcp首部长度最多只能为60字节。
当没有选项时,正常tcp首部长度为20字节。
#### 标志位
tcp首部中存在6个标志位,其中多个标志位可以同时被设置为1,标志位的简介如下。
- URG: 紧急指针有效
- ACK: 确认序列号有效
- PSH: 接收方应该尽快将该报文段交给应用层
- RST: 重新建立连接
- SYN: 同步序列号,用于发起一个连接
- FIN: 发送端完成发送任务
#### 窗口大小
窗口大小用于进行tcp的流量控制,窗口大小字段的长度为16bit,故而窗口大小最大为65535字节。
tcp由每一端通过声明窗口的大小来提供流量控制。窗口大小为字节数,开始位置为确认序列号字段指明的值。
#### 校验和
校验和字段为16bit,覆盖了整个tcp报文段,包含tcp首部和tcp数据。
校验和字段由发送方进行计算和存储,并且由接收方进行校验。
#### 紧急指针
紧急指针长度为16bit,仅当URG标志位置位时才有效。紧急指针代表一个正的偏移量,和序列号字段相加后的值表示紧急数据最后一个字节的序列号。
tcp的紧急方式是发送方向另一方发送紧急数据的方式。
#### 可选字段
最常见的可选字段为最长报文大小(MMS, Max Segement Size),每个连接方通常都在通信的第一个报文段(SYN置位的那个报文段)中指明了MMS选项。
#### 数据部分
在tcp报文段中,数据部分是可选的,如果一方没有数据需要发送,那么tcp报文段中并不包含数据部分,只包含tcp的首部。