以太网帧、IP 帧、UDP/TCP帧、http 报文结构解析
我们从 OSI/RM 参考模型入手,来看 OSI/RM 七层模型中的每一层数据帧结构。
一 OSI/RM 结构
OSI 是不同制造商的设备和应用软件在网络中进行通信的标准,此模型已经成为计算机间和网络间进行通信的主要结构模型, 目前使用的大多数网络通信协议的结构都是基于 OSI 模型的。OSI 包括体系结构、服务定义和协议规范三级抽象。OSI 体系结构定义了一个七层模型用于进行进程间的通信,并作为一个概念性框架来协调各层标准的制定;OSI 的服务定义描述了各层所提供的服务,以及层与层之间的抽象接口和交互用的服务原语;OSI 各层的协议规范精确的定义了应当发送何种控制信息及何种过程来解释该控制信息。OSI/RM 七层结构以及各层的基本功能如下。
1. 物理层:物理层是 OSI 参考模型的最低层,它的任务是为它的上一层(数据链路层)提供一个物理连接,以便透明地传送比特流,即经过实际电路传送后的比特流没有发生变化,物理层好像是透明的,对其中的传送内容不会有任何影响,任意的比特流都可以在这个电路上传送。
2. 数据链路层:链路是指两个相邻节点间的传输线路,是物理连接,数据链路表示传输数据的链路,是逻辑连接。数据链路层把物理层提供的可能出错的链路(物理连接)改造成为逻辑上无差错的数据链路,使之对网络层表现为一条无差错的链路。数据链路层将传送的数据组织成的数据链路协议数据单元称为数据帧。在 OSI 参考模型中,数据链路层位于第二层,局域网的标准将数据链路层分为两个子层:逻辑链路控制子层(LLC)与介质访问控制子层(MAC)。数据链路层的主要功能是将网络层接收到的数据分割成特定的可被物理层传输的帧。
3. 网络层:网络层是 OSI 参考模型中的第三层,它传送的数据单元是数据“分组”。它的任务是在数据链路层提供的两个相邻节点之间的数据帧的传送基础上,选择合适的路由与交换节点,使从源节点传输层得到的数据能准确无误、高效地到达目的节点,并交付给目的节点的传输层,主要功能是路由选择、阻塞控制、网络互联。网络层提供了虚电路服务(网络层向传输层提供的一种使所有分组按顺序到达目的端系统的可靠的数据传送方式,是一种面向连接的服务)和数据报服务(数据报服务一般仅由数据报交换网来提供,端系统的网络层同网络节点的网络层之间均按照数据报方式交换数据,是一种面向无连接的服务)。
4. 传输层:在 OSI 参考模型中的第四层,第一、二、三层是面向通信的通常称为通信子网,第五、六、七层面向应用的通常称为资源子网,负责端到端的可靠传输,实现数据通信中通信子网向资源子网的过渡,传输层传输信息的单位称为报文,负责总体数据的传输和控制。这一层主要定义了两个传输协议,TCP 以及 UDP 协议。
5. 会话层:在 OSI 参考模型的第五层,负责在网络中的两个节点之间建立和维持通信。会话层的功能包括:建立通信链接和保持会话过程通信链接的畅通,同步两个节点之间的对话,决定通信是否被中断以及通信中断时决定从何处重新发送。
6. 表示层:在 OSI 参考模型的第六层,表示层中数据将按照网络能理解的方案进行格式转化,管理数据的解密与加密,还对图片和文件格式信息进行解码和编码。表示层提供两类服务:相互通信的应用进程间交换信息的表示方法与表示连接服务。
7. 应用层:在 OSI 参考模型的最高层:负责对软件提供接口以使程序能享用网络服务。应用层提供的服务包括文件传输、文件管理以及电子邮件的信息处理。
二 TCP | IP 协议
Internet 协议集是对 ISO/OSI 的简化,其主要功能集中在 OSI 的第三、四层,通过增加软件模块来保证和已有系统的最大兼容性。现在 TCP | IP 协议集已成为 Internet 的标准,应用最为广泛,TCP | IP 协议集包括多种协议,如下图所示:
三 以太网帧
在数据链路层中,使用最多的就是以太网,而以太网帧因为历史原因存在多个版本,这里采用 IEEE 802.3 以太帧格式。
前导码:7 个字节,用于数据传输过程中的双方发送与接收的速率的同步。
SFD:帧开始符,1 个字节,用于标识一个以太网帧的开始。
目的 MAC 地址:6 个字节,指明帧的接收者。
源 MAC 地址:6 个字节,指明帧的发送者。
长度:2 个字节,指明该帧数据字段的长度,但不代表数据字段长度能够达到 2^16 字节。
类型:2 个字节,指明帧中数据的协议类型,比如常见的 IPv4 中的 ip 协议采用 0x0800。
数据与填充:46~1500 个字节,包含了上层协议传递下来的数据,如果加入数据字段后帧长度不够 64 字节,会在数据字段加入填充字段达到 64 字节。
校验和:4 个字节,对接收网卡(主要是检测数据与填充字段)提供判断是否传输错误的一种方法,如果发现错误,则丢弃此帧。目前最为流行的用于校验和(FCS)的算法是循环冗余校验(cyclic redundancy check -- CRC)。
四 IP帧
IP 协议是 TCP/IP 协议族中最为核心的协议,它提供不可靠的、无连接的服务,也即依赖其他层的协议进行差错控制。在局域网中,IP 协议往往被封装在以太网帧中传送,而所有的 TCP、UDP、ICMP、IGMP 数据都被封装在 IP 数据报中传送。
/*IP 头定义,共 20 个字节*/
typedef struct _IP_HEADER
{
char m_cVersionAndHeaderLen; //前4位:版本信息;后4位:头长度
char m_cTypeOfService; //服务类型 8 位
short m_sTotalLenOfPacket; //数据包长度
short m_sPacketID; //数据包标识
short m_sSliceinfo; //分片使用
char m_cTTL; //存活时间
char m_cTypeOfProtocol; //协议类型
short m_sCheckSum; //校验和
unsigned int m_uiSourIp; //源IP
unsigned int m_uiDestIp; //目的IP
}__attribute__((packed))IP_HEADER,*PIP_HEADER;
版本:4 位,用来表明 IP 协议实现的版本号,当前一般为 IPv4,即 0100,IPv6 的为 0110,这个字段确保可能运行不同 IP 版本的设备之间的兼容性。
首部长度:即报头长度,4 位,以 32 bit 的字来定义 IP 首部的长度,包括可选项。若该字段的最小值是 5 (标准头部长度),即 5*32=160 比特 =20 字节,此字段最大值为15 (有扩展部分),即15*32 =480 比特 = 60 字节。
服务类型:8位,用于携带提供服务质量特征信息的字段,服务类型字段声明了数据报被网络系统传输时可以被怎样处理。其中前 3 比特位优先权子字段(Precedence,现已被忽略,各种终端都不采用)。第 8 比特保留未用。第 4 至第 7 比特分别代表延迟、吞吐量、可靠性和花费,当它们取值为 1 时分别代表要求最小时延、最大吞吐量、最高可靠性和最小花费,这 4 比特的服务类型中只能置其中 1 比特为 1,可以全为 0 ,若全为 0 则表示一般服务,大多数情况下该服务类型会被忽略。
总长度:16 位,指明整个数据报的长度,按字节计算,最大长度为 2^16 字节。
标识:16 位,用来唯一标识主机发送的每一份数据报,IP 软件会在存储器中维持一个计数器,每产生一个数据段,计数器就加 1,并将此值赋给标志字段。但这个“标识”并不是序号,因为 IP 是无连接服务,数据报不存在按序接收问题,如数据报由于超过网络的 MTU (最大传送单元) 而必须分片时,这个标志字段的值就会被复制到所有的数据报的标识字段中,相同的标识字段的值使分片后各数据报片能正确的重装成为原来的数据报。
标志:3 位,分别是 RF、DF、 MF,目前只有 DF,MF 有效,DF(don't fragment),置为 0 时表示可以分段,置为 1 时是不能被分段,MF(more fragment),置为 0 时表示该数据段为最后一个数据段,置为 1 时表示后面还有被分割分段。
段偏移量:13 位,指出较长的分组在分段后,某段在原分组的相对位置。也就是说相对用户字段的起点,该片从何处开始。段偏移以 8 个字节(有 3 位被 flags 占据)为偏移单位,这就是每个分片的长度一定是 8 字节 (64位) 的整数倍。
生存期:8 位,用来设置数据报最多可以经过的路由器数,由发送数据的源主机设置,通常为 32、64、128等,每经过一个路由器,其值减 1 ,直到 0 该数据报被丢弃。
协议:8 位,指明 ip 数据字段中的数据采用上层什么协议封装的,常见的有 ICMP(1)、IGMP(2)、TCP(6)、UDP(17)
首部校验和:16位,填充根据 ip 头部计算得到的校验和码。计算方法是:对头部中每个 16 比特进行二进制反码求和,但不含涉及头部后的数据字段。
源 IP 地址:源 ip 地址,32 位。
目的 IP 地址:目标 ip 地址,32 位。
选项:n*32 位,用来定义一些可选项:如记录路径、时间戳等,但这些选项很少被使用,同时并不是所有主机和路由器都支持这些选项。可选字段的长度必须是 32 比特的整数倍,如果不足,必须填充 0 以达到此长度要求,根据 IHL(首部长度) 可以得到选项的长度。
数据:不定长度,但受限于数据报的最大长度 2^16 ,这是在数据报中要传输的数据,它是一个完整的较高层报文或报文的一个分片。
五 UDP 帧
UDP(User Datagram Protocol) 即用户数据报协议,在网络中它与 TCP 协议一样用于处理数据包,是一种不可靠(服务不用确认、不对报文排序、不进行流量控制,可能会出现丢失、重复、失序现象)、无连接(在主机间不建立会话)的协议,在 OSI 模型中的第四层--传输层,处于 IP 协议的上一层。由于 UDP 传输不是可靠性服务,所以帧结构较为简单,而且处理与发送速率高,开销要求低,支持点对点和一点对多点的通信,经常用作音频、视频和普通数据的传输协议,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
/*UDP 头定义,共 8 个字节*/
typedef struct_UDP_HEADER
{
unsigned short m_usSourPort; //源端口号 16 bit
unsigned short m_usDestPort; //目的端口号 16 bit
unsigned short m_usLength; //数据包长度 16 bit
unsigned short m_usCheckSum; //校验和 16 bit
}__attribute__((packed))UDP_HEADER,*PUDP_HEADER;
源端口:16 bit (2 个字节),是一个大于 1023 的 16 位数字,由基于 UDP 应用程序的用户进程随机选择。
目的端口:16 bit (2 个字节),指明接收者所用的端口号,一般由应用程序来指定。
数据长度:16 bit (2 个字节),标明 UDP 头部和 UDP 数据的总字节长度。
校验和字段:16 bit (2 个字节),用来对 UDP 头部和 UDP 数据进行校验。与 TCP 不同的是,此字段是可选项,而 TCP 数据段中必须包含校验和字段。
数据:不定长度,为上层协议封装好的数据。
六 TCP 帧
TCP(Transmission Control Protocol) 即传输控制协议,是一种面向连接的(需通过三次握手来建立 TCP 连接,在主机间建立会话)、可靠的(TCP 通过确认和按顺序传递来确保数据的传递)、基于字节流的传输层通信协议,但 TCP 传输比较慢,开销略高,并且只支持点对点通信。当应用层向 TCP 层发送用于网间传输的 8 字节表示的数据流,TCP 则把数据流分割成适当长度的报文段,最大传输段大小(MSS)通常受该计算机连接的网络的数据链路层的最大传送单元(MTU)限制,之后 TCP 把数据包传给 IP 层,由它来通过网络将包传送给接收端实体的 TCP 层。
/*TCP 头定义,共 20 个字节*/
typedef struct_TCP_HEADER
{
short m_sSourPort; //源端口号 16 bit
short m_sDestPort; //目的端口号 16 bit
unsigned int m_uiSeqNum; //序列号 32 bit
unsigned int m_uiAcknowledgeNum; //确认号 32 bit
short m_sHeaderLenAndFlag; //前4位:TCP 头长度;中6位:保留;后6位:标志位
short m_sWindowSize; //窗口大小 16 bit
short m_sCheckSum; //校验和 16 bit
short m_surgentPointer; //紧急数据偏移量 16 bit
}__attribute__((packed))TCP_HEADER,*PTCP_HEADER;
源端口:2 个字节,是一个大于 1023 的 16 位数字,由基于 TCP 应用程序的用户进程随机选择。
目的端口:2 个字节,指明接收者所用的端口号,一般由应用程序来指定。
顺序号:4 个字节,用来标识从 TCP 源端向 TCP 目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号。如果将字节流看作在两个应用程序间的单向流动,则 TCP 用顺序号对每个字节进行计数,序号是 32 bit 的无符号数,序号达到 2^32-1 后又从 0 开始。比如我们收到一个数据报中 sq(顺序号) =0,数据报内容为 20 字节,那么下一个数据报的 sq 就应该是 21。当建立一个新的连接时,SYN 标志变为 1,顺序号字段包含由这个主机选择的该连接的初始顺序号 ISN。
确认序号:4 个字节,包含发送确认的一端所期待收到的下一个顺序号。因此,确认序号应该是上次已经成功收到数据字节顺序号加 1 。比如我们收到的一个数据报的 sq = 0 ,数据报内容为 20 字节,那么我们的 ack(确认序号) 应该是 21 ,用来表明 sq=0 ,内容为 20 字节的数据报已经收到,接下来期望收到的是 sq=21 的数据报。只有 ACK 标志为 1 时确认序号字段才有效。
报文长度:4 位,给出报头中 32 bit 字的数目,需要这个值是因为任选字段的长度是可变的,这个字段占 4 bit,即 TCP 最多有 60 (15*4) 字节的首部。
保留区:6 位,保留给将来使用,目前必须置为 0 。
控制位:6位,控制位包括
URG:为 1 表示紧急指针有效,为 0 则忽略紧急指针值。
ACK:为 1 表示确认号有效,为 0 表示报文中不包含确认信息,忽略确认号字段。
PSH:为 1 表示是带有 PUSH 标志的数据,表示发送端缓存中已经没有待发送的数据,指示接收方应该尽快将这个报文段交给应用层而不用等待缓冲区装满。
RST:用于复位由于主机崩溃或其他原因而出现错误的连接。它还可以用于拒绝非法的报文段和拒绝连接请求。一般情况下,如果收到一个 RST 为 1 的报文,那么一定发生了某些问题。
SYN:同步序号,为 1 表示连接请求,用于建立连接和使顺序号同步。
FIN:用于释放连接,为 1 表示发送方已经没有数据发送了,即关闭本方数据流。
窗口大小:2 个字节,表示从确认号开始,本报文的源方可以接收的字节数,即源方接收窗口的大小。窗口大小是一个 16 bit 字段,因而窗口大小最大为 2^16-1 。
校验和:2 个字节,对整个的 TCP 报文段(包括 TCP 头部和 TCP 数据以及伪报文头)进行校验和计算。这是一个强制性的字段,要求由发送方计算和存储,并由接收端进行验证(接收端要与发送端数值结果完全一样,才能证明数据是有效的)。
紧急指针:2 个字节,是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式,只有当 URG 标志置为 1 时紧急指针才有效。
选项:n*4 字节,常见的可选字段是最长报文大小 MSS(Maximum Segment Size)。每个连接方通常都在通信的第一个报文段 (为建立连接而设置 SYN 标志的那个段) 中指明这个选项,它指明本端所能接收的最大长度的报文段。选项长度不一定是 32 位字的整数倍,所以需要添加填充位,使得报文长度为 32 位字的整数倍。
数据:不定长度,为上层协议封装好的数据。
七 HTTP 报文
HTTP 是建立在 TCP | IP 上层的应用层协议,,可以保证客户端(特别是Web浏览器)正确和快速地传输超文本文件信息,而且是一个基于 请求/响应 模式的无状态协议,自由度更高,是面向文本的,报文中的每一个字段都是一些 ASCALL 码串,各个字段的长度是不确定的。报文类型可以分为两种:请求报文和响应报文(都是由一个请求起始行、一个或多个头域、一个标识头域结束的空行和可选的消息体组成)。
请求报文
1. 请求行:
请求行包括请求方法、URL、协议版本,它们之间用空格分隔(所以我们输入的 URL 是不允许带有空格),且都不定长度。
请求方法:常用的 POST (将表单数据存入请求体里面,多数用于上传数据),GET(将请求参数都放置在 URL+? 后,参数之间用 & 连接,用于获取数据),HEAD (服务端只返回响应头,所以处理响应速度快,用于检测请求是否可用)。
2. 请求头
每一行以键值对的形式写入,键值间用 :分割,多个值之间以 ;分割,每行以回车符换行符结束。常见的键有:User-Agent:产生请求的浏览器类型;Accept:客户端可识别的内容类型列表;Host:请求的主机名,允许多个域名同处一个 IP 地址;Range:指定请求实体的一个或者多个子范围,采用多线程下载时可以设置该键。要注意请求头与请求体间有一个空行,它表示通知服务端没有更多请求头了,下面的都是请求体。
3. 请求体
请求数据不在 GET 方法中使用,而是在 POST 方法中使用,POST 方法适用于需要客户填写表单的场合。
响应报文
1. 状态行:
状态行包括协议版本,状态码,状态码描述,常见的一个状态行的例子是(HTTP/1.1 200 OK),其中 200 表示请求正常状态码中的第一个数字定义了相应的类别,第一个数字可以取如下所示的 5 个不同的值之一,含义如下。
1XX:信息响应类,表示接收到请求并且继续处理。
2XX:请求处理成功的响应类,表示请求被成功接收和处理,如 HTTP 200 (表示一切正常)。
3XX:重定向响应类,为了完成指定的请求动作,必须接受进一步的请求处理。
4XX:客户端错误,客户请求包含语法错误或者不能正确执行,如 HTTP 400 (表示请求无效)。
5XX:服务端错误,服务器不能正常执行一个正确的 HTTP 请求,如 HTTP 500 (服务器内部出现错误)。
2. 响应头:
与请求头类似,通过键值对的形式向客户端传递关键消息,常见的有:Content-Type:用于向接收方指示实体的介质类型;Content-Range:用于向接收方指示实体的传送范围;Content-Length:用于向接收方指示实体的传送长度;Date:传送系统的日期与时间。
3. 响应体:
服务端真正返回的文本数据,如果客户端请求一个网页的话,该数据段将填充请求的 html 文本。