写点什么

Android C++ 系列:Linux 网络(三)协议格式

作者:轻口味
  • 2021 年 12 月 10 日
  • 本文字数:5580 字

    阅读完需:约 18 分钟

Android C++系列:Linux网络(三)协议格式

1. 数据包封装

传输层及其以下的机制由内核提供,应用层由用户进程提供(后面将介绍如何使用 socket API 编写应用程序),应用程序对通讯数据的含义进行解释,而传输层及其以下 处理通讯的细节,将数据从一台计算机通过一定的路径发送到另一台计算机。应用层 数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装 (Encapsulation),如下图所示



不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数 据报(datagram),在链路层叫做帧(frame)。数据封装成帧后发到传输介质上,到达目 的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理。

2. 以太网帧格式

其中的源地址和目的地址是指网卡的硬件地址(也叫 MAC 地址),长度是 48 位,是在网 卡出厂时固化的。用 ifconfig 命令看一下,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地 址。协议字段有三种值,分别对应 IP、ARP、RARP。帧末尾是 CRC 校验码。



以太网帧中的数据长度规定最小 46 字节,最大 1500 字节,ARP 和 RARP 数据包的长度不够 46 字节,要在后面补填充位。最大值 1500 称为以太网的最大传输单元(MTU),不同的网络 类型有不同的 MTU,如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路 的 MTU 了,则需要对数据包进行分片(fragmentation)。ifconfig 命令的输出中也有“MTU: 1500”。注意,MTU 这个概念指数据帧中有效载荷的最大长度,不包括帧首部的长度。

3. ARP 数据包格式

在网络通讯时,源主机的应用程序知道目的主机的 IP 地址和端口号,却不知道目的主机 的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的 硬件地址与本机不符,则直接丢弃。因此在通讯前必须获得目的主机的硬件地址。ARP 协议 就起到这个作用。源主机发出 ARP 请求,询问“IP 地址是 192.168.0.1 的主机的硬件地址是多 少”,并将这个请求广播到本地网段(以太网帧首部的硬件地址填 FF:FF:FF:FF:FF:FF 表示 广播),目的主机接收到广播的 ARP 请求,发现其中的 IP 地址与本机相符,则发送一个 ARP 应 答数据包给源主机,将自己的硬件地址填写在应答包中。


每台主机都维护一个 ARP 缓存表,可以用 arp -a 命令查看。缓存表中的表项有过期时间 (一般为 20 分钟),如果 20 分钟内没有再次使用某个表项,则该表项失效,下次还要发 ARP 请求来获得目的主机的硬件地址。想一想,为什么表项要有过期时间而不是一直有效?


ARP 数据报的格式如下所示



注意到源 MAC 地址、目的 MAC 地址在以太网首部和 ARP 请求中各出现一次,对于链路层为 以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链 路层网络类型,1 为以太网,协议类型指要转换的地址类型,0x0800 为 IP 地址,后面两个地 址长度对于以太网地址和 IP 地址分别为 6 和 4(字节),op 字段为 1 表示 ARP 请求,op 字段为 2 表示 ARP 应答。


下面举一个具体的例子。 请求帧如下(为了清晰在每行的前面加了字节计数,每行 16 个字节): 以太网首部(14 字节)


0000: ffffffffffff00055d6158a80806


ARP 帧(28 字节)


0000: 00 01


0010: 08000604000100055d6158a8c0a80037


0020: 000000000000c0a80002


填充位(18 字节)


0020: 00 77 31 d2 50 10


0030: fd7841d30000000000000000


以太网首部:目的主机采用广播地址,源主机的 MAC 地址是 00:05:5d:61:58:a8,上层协议类型 0x0806 表示 ARP。


ARP 帧:硬件类型 0x0001 表示以太网,协议类型 0x0800 表示 IP 协议,硬件地址(MAC 地址)长度为 6,协议地址(IP 地址)长度为 4,op 为 0x0001 表示请求目的主机的 MAC 地址,源 主机 MAC 地址为 00:05:5d:61:58:a8,源主机 IP 地址为 c0 a8 00 37(192.168.0.55),目的 主机 MAC 地址全 0 待填写,目的主机 IP 地址为 c0 a8 00 02(192.168.0.2)。


由于以太网规定最小数据长度为 46 字节,ARP 帧长度只有 28 字节,因此有 18 字节填充 位,填充位的内容没有定义,与具体实现相关。


应答帧如下:


以太网首部


0000: 00055d6158a800055da1b8400806


ARP 帧


0000: 00 01


0010: 08000604000200055da1b840c0a80002


0020: 00055d6158a8c0a80037


填充位


0020: 00 77 31 d2 50 10


0030: fd7841d30000000000000000


以太网首部:目的主机的 MAC 地址是 00:05:5d:61:58:a8,源主机的 MAC 地址是 00:05:5d:a1:b8:40,上层协议类型 0x0806 表示 ARP。


ARP 帧:硬件类型 0x0001 表示以太网,协议类型 0x0800 表示 IP 协议,硬件地址(MAC 地址)长度为 6,协议地址(IP 地址)长度为 4,op 为 0x0002 表示应答,源主机 MAC 地址为 00:05:5d:a1:b8:40,源主机 IP 地址为 c0 a8 00 02(192.168.0.2),目的主机 MAC 地址为 00:05:5d:61:58:a8,目的主机 IP 地址为 c0 a8 00 37(192.168.0.55)。


如果源主机和目的主机不在同一网段,ARP 请求的广播帧无法穿过路由器,源 主机如何与目的主机通信?

4. IP 段格式


IP 数据报的首部长度和数据长度都是可变长的,但总是 4 字节的整数倍。对于 IPv4,4 位版本字段是 4。4 位首部长度的数值是以 4 字节为单位的,最小值为 5,也就是说首部长度 最小是 4x5=20 字节,也就是不带任何选项的 IP 首部,4 位能表示的最大值是 15,也就是说首 部长度最大是 60 字节。8 位 TOS 字段有 3 个位用来指定 IP 数据报的优先级(目前已经废弃不 用),还有 4 个位表示可选的服务类型(最小延迟、最大吐量、最大可靠性、最小成本), 还有一个位总是 0。总长度是整个数据报(包括 IP 首部和 IP 层 payload)的字节数。每传一 个 IP 数据报,16 位的标识加 1,可用于分片和重新组装数据报。3 位标志和 13 位片偏移用于 分片。TTL(Time to live)是这样用的:源主机为数据包设定一个生存时间,比如 64,每过 一个路由器就把该值减 1,如果减到 0 就表示路由已经太长了仍然找不到目的主机的网络, 就丢弃该包,因此这个生存时间的单位不是秒,而是跳(hop)。协议字段指示上层协议 是 TCP、UDP、ICMP 还是 IGMP。然后是校验和,只校验 IP 首部,数据的校验由更高层协议负 责。IPv4 的 IP 地址长度为 32 位。选项字段的解释从略。


想一想,前面讲了以太网帧中的最小数据长度为 46 字节,不足 46 字节的要用填充字节补 上,那么如何界定这 46 字节里前多少个字节是 IP、ARP 或 RARP 数据报而后面是填充字节?

5. UDP 数据抱格式

下面分析一帧基于 UDP 的 TFTP 协议帧。


以太网首部


0000: 00055d67d0b100055d6158a80800


IP 首部


0000: 45 00


0010: 005393250000801125ecc0a80037c0a8


0020: 00 01


UDP 首部


0020: 05 d4 00 45 00 3f ac 40


TFTP 协议


0020: 00 01 ‘c”:‘”’q’


0030: ‘w’ ‘e’ ‘r’ ‘q”.‘’q’‘ w’ ‘e’ 00 ’n’ ‘e‘ ’t’ ‘a‘ ’s’ ‘c’ ‘i’


0040: ‘i’ 00 ’b’ ‘l’ ‘k‘ ’s’ ‘i’ ‘z’ ‘e’ 00 ’5’ ‘1’ ‘2’ 00 ’t’ ‘i’


0050: ’m’ ‘e’ ‘o’ ‘u‘ ’t’ 00 ‘1’ ‘0’ 00 ’t‘ ’s’ ‘i’ ‘z’ ‘e’ 00 ’0’


0060: 00


以太网首部:源 MAC 地址是 00:05:5d:61:58:a8,目的 MAC 地址是 00:05:5d:67:d0:b1,上层协议类型 0x0800 表示 IP。


IP 首部:每一个字节 0x45 包含 4 位版本号和 4 位首部长度,版本号为 4,即 IPv4,首部长度为 5,说明 IP 首部不带有选项字段。服务类型为 0,没有使用服务。16 位总长度字段(包 括 IP 首部和 IP 层 payload 的长度)为 0x0053,即 83 字节,加上以太网首部 14 字节可知整个帧长度是 97 字节。IP 报标识是 0x9325,标志字段和片偏移字段设置为 0x0000,就是 DF=0 允许分 片,MF=0 此数据报没有更多分片,没有分片偏移。TTL 是 0x80,也就是 128。上层协议 0x11 表 示 UDP 协议。IP 首部校验和为 0x25ec,源主机 IP 是 c0 a8 00 37(192.168.0.55),目的主机 IP 是 c0 a8 00 01(192.168.0.1)。


UDP 首部:源端口号 0x05d4(1492)是客户端的端口号,目的端口号 0x0045(69)是 TFTP 服务的 well-known 端口号。UDP 报长度为 0x003f,即 63 字节,包括 UDP 首部和 UDP 层 pay- load 的长度。UDP 首部和 UDP 层 payload 的校验和为 0xac40。


TFTP 是基于文本的协议,各字段之间用字节 0 分隔,开头的 00 01 表示请求读取一个文 件,接下来的各字段是:


c:\qwerq.qwe netascii blksize 512 timeout 10 tsize 0
复制代码


一般的网络通信都是像 TFTP 协议这样,通信的双方分别是客户端和服务器,客户端主动 发起请求(上面的例子就是客户端发起的请求帧),而服务器被动地等待、接收和应答请 求。客户端的 IP 地址和端口号唯一标识了该主机上的 TFTP 客户端进程,服务器的 IP 地址和端 口号唯一标识了该主机上的 TFTP 服务进程,由于客户端是主动发起请求的一方,它必须知道 服务器的 IP 地址和 TFTP 服务进程的端口号,所以,一些常见的网络协议有默认的服务器端 口,例如 HTTP 服务默认 TCP 协议的 80 端口,FTP 服务默认 TCP 协议的 21 端口,TFTP 服务默认 UDP 协议的 69 端口(如上例所示)。在使用客户端程序时,必须指定服务器的主机名或 IP 地址, 如果不明确指定端口号则采用默认端口,请读者查阅 ftp、tftp 等程序的 man page 了解如何 指定端口号。/etc/services 中列出了所有 well-known 的服务端口和对应的传输层协议,这 是由 IANA(Internet Assigned Numbers Authority)规定的,其中有些服务既可以用 TCP 也 可以用 UDP,为了清晰,IANA 规定这样的服务采用相同的 TCP 或 UDP 默认端口号,而另外一些 TCP 和 UDP 的相同端口号却对应不同的服务。


很多服务有 well-known 的端口号,然而客户端程序的端口号却不必是 well-known 的, 往往是每次运行客户端程序时由系统自动分配一个空闲的端口号,用完就释放掉,称为 ephemeral 的端口号,想想这是为什么。


前面提过,UDP 协议不面向连接,也不保证传输的可靠性,例如:


发送端的 UDP 协议层只管把应用层传来的数据封装成段交给 IP 协议层就算完成任务了, 如果因为网络故障该段无法发到对方,UDP 协议层也不会给应用层返回任何错误信息。


接收端的 UDP 协议层只管把收到的数据根据端口号交给相应的应用程序就算完成任务 了,如果发送端发来多个数据包并且在网络上经过不同的路由,到达接收端时顺序已经错乱 了,UDP 协议层也不保证按发送时的顺序交给应用层。


通常接收端的 UDP 协议层将收到的数据放在一个固定大小的缓冲区中等待应用程序来提 取和处理,如果应用程序提取和处理的速度很慢,而发送端发送的速度很快,就会丢失数据 包,UDP 协议层并不报告这种错误。


因此,使用 UDP 协议的应用程序必须考虑到这些可能的问题并实现适当的解决方案,例 如等待应答、超时重发、为数据包编号、流量控制等。一般使用 UDP 协议的应用程序实现都 比较简单,只是发送一些对可靠性要求不高的消息,而不发送大量的数据。例如,基于 UDP 的 TFTP 协议一般只用于传送小文件(所以才叫 trivial 的 ftp),而基于 TCP 的 FTP 协议适用于 各种文件的传输。下面看 TCP 协议如何用面向连接的服务来代替应用程序解决传输的可靠性问题。

6. TCP 数据报格式


和 UDP 协议一样也有源端口号和目的端口号,通讯的双方由 IP 地址和端口号标识。32 位 序号、32 位确认序号、窗口大小稍后详细解释。4 位首部长度和 IP 协议头类似,表示 TCP 协 议头的长度,以 4 字节为单位,因此 TCP 协议头最长可以是 4x15=60 字节,如果没有选项字 段,TCP 协议头最短 20 字节。URG、ACK、PSH、RST、SYN、FIN 是六个控制位,本节稍后将解 释 SYN、ACK、FIN、RST 四个位,其它位的解释从略。16 位检验和将 TCP 协议头和数据都计算 在内。紧急指针和各种选项的解释从略。


通信时序


下图是一次 TCP 通讯的时序图。



在这个例子中,首先客户端主动发起连接、发送请求,然后服务器端响应请求,然后客户端主动关闭连接。两条竖线表示通讯的两端,从上到下表示时间的先后顺序,注意,数据从一端传到网络的另一端也需要时间,所以图中的箭头都是斜的。双方发送的段按时间顺 序编号为 1-10,各段中的主要信息在箭头上标出,例如段 2 的箭头上标着 SYN, 8000(0), ACK 1001, ,表示该段中的 SYN 位置 1,32 位序号是 8000,该段不携带有效载荷(数据字节数为 0),ACK 位置 1,32 位确认序号是 1001,带有一个 mss 选项值为 1024。


建立连接的过程:


  1. 客户端发出段 1,SYN 位表示连接请求。序号是 1000,这个序号在网络通讯中用作临时 的地址,每发一个数据字节,这个序号要加 1,这样在接收端可以根据序号排出数据包的正 确顺序,也可以发现丢包的情况,另外,规定 SYN 位和 FIN 位也要占一个序号,这次虽然没发 数据,但是由于发了 SYN 位,因此下次再发送应该用序号 1001。mss 表示最大段尺寸,如果一 个段太大,封装成帧后超过了链路层的最大帧长度,就必须在 IP 层分片,为了避免这种情 况,客户端声明自己的最大段尺寸,建议服务器端发来的段不要超过这个长度。

  2. 服务器发出段 2,也带有 SYN 位,同时置 ACK 位表示确认,确认序号是 1001,表示“我 接收到序号 1000 及其以前所有的段,请你下次发送序号为 1001 的段”,也就是应答了客户端 的连接请求,同时也给客户端发出一个连接请求,同时声明最大尺寸为 1024。

  3. 客户端发出段 3,对服务器的连接请求进行应答,确认序号是 8001。


在这个过程中,客户端和服务器分别给对方发了连接请求,也应答了对方的连接请求, 其中服务器的请求和应答在一个段中发出,因此一共有三个段用于建立连接,称为’‘’三 方握手(three-way-handshake)”’。在建立连接的同时,双方协商了一些信息,例如双方发送序号的初始值、最大段尺寸等。


在 TCP 通讯中,如果一方收到另一方发来的段,读出其中的目的端口号,发现本机并没 有任何进程使用这个端口,就会应答一个包含 RST 位的段给另一方。


数据传输的过程:


  1. 客户端发出段 4,包含从序号 1001 开始的 20 个字节数据。

  2. 服务器发出段 5,确认序号为 1021,对序号为 1001-1020 的数据表示确认收到,同时请求发送序号 1021 开始的数据,服务器在应答的同时也向客户端发送从序号 8001 开始的 10 个字 节数据,这称为 piggyback。

  3. 客户端发出段 6,对服务器发来的序号为 8001-8010 的数据表示确认收到,请求发送序 号 8011 开始的数据。


关闭连接的过程:


  1. 客户端发出段 7,FIN 位表示关闭连接的请求。

  2. 服务器发出段 8,应答客户端的关闭连接请求。

  3. 服务器发出段 9,其中也包含 FIN 位,向客户端发送关闭连接请求。

  4. 客户端发出段 10,应答服务器的关闭连接请求。

7. 总结

本文介绍了网络协议格式:数据包封装、以太网帧格式、ARP 数据包格式、IP 段格式、UDP 数据包格式、TCP 数据包格式等。

发布于: 2 小时前阅读数: 5
用户头像

轻口味

关注

🏆2021年InfoQ写作平台-签约作者 🏆 2017.10.17 加入

Android、音视频、AI相关领域从业者。 邮箱:qingkouwei@gmail.com

评论

发布
暂无评论
Android C++系列:Linux网络(三)协议格式