传输TCP/IP数据
问题引入
- 现在使用的以太网中存在不符合国际标准(IEEE802.3/802.2)的部分。
一般情况下,以太网的头部(网络包开头的控制信息)格式并非遵循国际标准(IEEE802.3/802.2), 而是遵循一个更古老的规格(以太网第 2 版,又称 DIX 规格),相对地,国际标准(IEEE802.3/802.2)的 头部格式由于长度太长、效率降低而没有普及。
- 最早的 TCP/IP 协议原型设计相当于现在的 TCP 和 IP 合在一起的样子,后来才拆分成为 TCP 和 IP 两 个协议。
- 设计网络包通信技术的目的是用计算机进行数据通信
在网络包出现之前,通信都是像电话一样把线路连接起来进行的。但是,连接线路的通信方式只能和 固定的对象进行通信,无法发挥计算机可以处理多种工作的特点。为了解决这个问题,人们设计出了使用网 络包来进行通信的方式。
创建套接字
协议栈的结构
注:
- 上下关系:
上面的部分会向下面的部分委派工作,下面的部分接受委派的工作并实际执行。 - 图解细析:
(1)Socket库中包含解析器——负责向DNS服务器发出查询。
(2)协议栈的上半部分分成两块
负责用 TCP 协议收发数据的部分;(像浏览器、邮件等一般的应用程序 都是使用 TCP )
负责用 UDP 协议收发数据的部分;(像 DNS 查询等收发较短的控制数据的时候则使用 UDP)
(3)IP负责将网络包发送给通信对象的操作,IP中还包括 ICMP协议和 ARP协 议。
ICMP 用于告知网络包传送过程中产生的错误以及各种控制消息,
ARP 用于根据 IP 地址查询相应的以太网 MAC 地址。
(4)网卡驱动程序负责控制网卡硬件。
(5)网卡则责完成实际的收发操作,也就是对网线中的信号执行发送和接收的操作.
套接字实体
通信控制信息或者说存放控制信息的内存空间就是套接字的实体。
因此:套接字中记录了用于控制通信操作的各种控制信息,协议栈则需要根据这些信息判断下一步的行动。
可以通过输入 netstat -ano
命令来显示所有套接字:
例如:
- 第 1 行,这一行表示 PID 为 984 的程序正在135 端口等待另一方的连接,(本地 IP 地址和远程 IP 地址都是 0.0.0.0,这表示通信还没开始,IP 地址不确定) 。
- 第8行,表示 PID为 4 的程序正在使用 IP 地 址为 10.10.1.16 的网卡与 IP 地址为 10.10.1.80 的对象进行通信,且本机使用 1031 端 口,对方使用 139 端口(139 端口是 Windows 文件服务器使用的端口)可以得出这个套接字是连接到一台文件服务器的。
调用stocket
- 创建套接字时,首先分配一个套接字所需的内存空间,然后向其中写入初始状态。
- 将表示这个套接字的描述符告知应用程序。
- 接收描述符后,应用程序向协议栈进行收发数据委托时就提供这个描述符。
连接服务器
连接
连接:表示通信双方交换控制信息,在套接字中记录这些必要信息并准备数据收发的一连串操作。
- 连接操作的目的:
(1)把服务器的 IP 地址和端口号等信息告知协 议栈。
(2)客户端向服务器传达开始 通信的请求。 - 连接操作中所交换的控制信息是根据通信规则来确定的,只要根据规则执行连接操作,双方就可以得到必要的信息从而完成数据收发的准备。
- 执行数据收发操作时,需要一块用来临时存放要收发的数据的内存空间,这块内存空间称为缓冲区——在连接操作的过程中分配的。
头部
头部是负责保存控制信息的
控制信息分为两类:
- 客户端和服务器相互联络时交换的控制信息——保存在头部。(整个通信过程中都需要,且在 TCP 协议的规格中进行了定义,这些控制信息位于网络包的开头,因此被称为头部。为了区分,一般会记作 TCP 头部、以太网头部(又称头部) 、IP 头部。)
- 套接字(协议栈中的内存空间)中记录的信息(其中应用程序传递来的信息 、从通信对象接收到的信息、收发数据操作的执行状态等信息也会保存在这里,协议栈会根据这些信息来执行每一步的操作。)
实际操作
调用Socket 库的 connect ——connect(< 描述符 >, < 服务器 IP 地址和端口号 >, …)包括以下步骤:
- 在 TCP 模块处创建表示连接控制信息的头部。
- 通过 TCP 头部中的发送方和接收方端口号可以找到要连接的套接字。
- 将头部中的控制位的 SYN 比特 设置为 1——表示连接(如果不接受连接则不设置 SYN,将 RST 比特设置为 1。)。
- 双方在通信时必须相互确认网络包是否已经送达。
- 服务器 TCP 模块会将 TCP 头部传递给 IP 模块,并委托 IP 模块向客户端返回响应。
- 网络包返回到客户端,通过 IP 模块到达 TCP 模块,并通过 TCP 头部的信息确认连接服务器的操作是否成功。
- 此时如果 SYN 为 1 则表示连接成功。
- 向套接字中写入服务器的 IP 地址、端口号等信息, 同时还会将状态改为连接完毕。
- 服务器返回响应时将 ACK 比特设置为 1,相应地,客户端也需要将 ACK 比特设置为 1 并发回服务器,表示刚才的响应包已经收到。
收发数据
HTTP请求消息传递协议栈
即从应用程序中调用write将发送数据交给协议栈,协议栈执行发送操作:
- 协议栈不处理数据,只是统一当作一定长度的二进制字节序列。
- 协议栈先将数据存放在内部的发送缓冲区,并等待应用程序的下一段数据。(应用程序自行决定交给协议栈的数据量,协议栈并不能控制,因此为了保证效率会先积累至阈值再发送)
- 阈值是由(1)MTU(最大传输单元——一个网络包的最大长度,以太网中一般为 1500 字节。),MSS(最大分段大小——MTU 是包含头部的总长度,MTU 减去头部的长度后得到的长度就是一个网络包中所能容纳的最大数据长度)。
- 阈值是由(2)时间,协议栈内有一个计时器,每经过一定时间,就会把网络包发送出去。以上两个判断要素决定的。
- 图解
拆分大数据
当请求消息是提交表单消息时,长度会超过一个网络包的大小,
- 这时存储在缓冲区的数据需要以MSS长度为单位拆分。
- 当判断需要发送这些数据时,在每一块数据前面加上 TCP 头部, 并根据套接字中记录的控制信息标记发送方和接收方的端口号,然后交给 IP 模块来执行发送数据的操作。
- 图解:
确认网络包收到
通过返回ACK号(两部分,一设置在TCP头部的ACK号中写入数据长度,二是将控制位的ACK比特设为1)
- 为了防御攻击,将序号的初始值随机(序号初始值会在SYN设为1的同时设置序号字段的值告诉通信对方)。
- 在TCP双向收发时,客户端和服务器双方都需要计算各自序号,双方需要在连接过程中互相告知自己计算的序号初始值。
- TCP 采用该方式确认对方是否收到了数据,在得到确认之前,发送过的包都会保存在发送缓冲区中。如果对方没有返回某些包对应的 ACK 号,那么就重新发送这些包。
- 通过这一机制,可以确认接收方有没有收到某个包,如果没有收到则重新发送,这样无论网络中发生任何错误,都可以发现并采取补救措施(重传网络包)。而且避免了在其他结构进行补救。
- 通过“序号”和“ACK 号”可以确认接收方是否收到了网络包。
调整ACK号等待时间
即网络的错误检测和补偿机制
- 返回超时时间——ACK号的等待时间
TCP动态调整等待时间——由ACK号返回所需时间来判断。(TCP 会在发送数据的过程中持续测量 ACK 号的返回时间,如果 ACK 号返回变慢,则相应延长等待时间;相对地,如果 ACK 号 马上就能返回,则相应缩短等待时间)。
滑动窗口
即发送一个包之后,不等待 ACK 号返回,而是直接发送后续的一系列包。(等待 ACK 号的这段时间就被有效利用)。
为了避免出现缓冲区由于包过多而溢出的现象:
接收方需要告诉发送方自己最多能接收多少数据——(叫做窗口大小),然后发送方根据这个值对数据发送操作进行控制。
下面是从右往左发送数据的图解:
注:发送也是双向的
ACK与窗口合并
为了提高收发数据效率,需要考虑返回 ACK 号和更新窗口的时机。
- 更新窗口大小的时机是接收方从缓冲区中取出数据传递给应用程序时。
- 收到数据之后马上就进行向发送方返回 ACK 号。
中和两个方向:
接收方在发送 ACK 号和窗口更新时会等待一段时间(这个过程中很有可能会出现其他的通知操作)把两种通知合并在一个包里面发送。
接收HTTP响应消息
- 调用read程序获取响应消息。
- 控制流程转移到协议栈
- 协议栈将应用程序的委托(从接收缓冲区中取出数据并传递给应用程序)暂时挂起 ,待服务器返回的响应消息到达再继续执行接收操作。
第三步的具体操作: - 协议栈判断收到的数据块和 TCP 头部的内容是否有数据丢失,正常则返回 ACK 号。
- 协议栈将数据块暂存到接收缓冲区中,将数据块按顺序连接起来还原出原始的数据.
- 将数据交给应用程序。
从服务器断开
断开连接
收发数据结束的时间点是应用程序判断所有数据都已经发送完毕。
数据发送完毕的一方会发起断开过程,但不同的应用程序会选择不同的断开时机。
无论哪种情况,完成数据发送的一方会发起断开过程。
- 服务器一方的应用程序会调用 Socket 库的 close 程序。
- 服务器的协议栈会生成包含断开信息的 TCP 头部(即控制位中的 FIN 比特设为 1)
- 协议栈委托 IP 模块向客户端发送数据
- 服务器的套接字中记录断开操作的相关信息。
- 收到服务器发来的 FIN 为 1 的 TCP 头部时,客户端的协议栈将套接字标记为进入断开操作状态。
- 客户端会向服务器返回一个 ACK 号(目的告知服务器已收到 FIN 为 1 的包)
- 应用程序就会调用 read 来读取数据。
- 协议栈告知应用程序(浏览器)来自服务器的数据全部收到
- 客户端应用 程序会调用 close 来结束数据收发操作
- 客户端的协议栈也会和服务器一样,生成一个 FIN 比特为 1 的 TCP 包,然后委托 IP 模块发送给服务器。
- 服务器就会返回 ACK 号,全部结束。
删除套接字
一般等待一段时间(几分钟)后再删除套接字
小结
IP 与以太网的包收发操作
包
- 包由头部和数据构成。头部包含目的地址等控制信息;头部后面就是委托方要发送给对方的数据。
- 包的发送:
发送方负责创建包——包被发往最近的网络转发设备——(由头部信息)判断发送目标——直到到达接收方的网络设备。 - 发送方和接收方统称为终端节点,转发设备被称为转发节点或者中间节点。
- 对于TCP/IP包,其中(集线器是按照以太网规则传输包的设备,而路由器是按照 IP 规则传输包的设备)因此TCP/IP 包包含两个头部:MAC 头部(用于以太网协议)和IP 头部(用于 IP 协议)。
IP 头部被写入要访问的服务器的 IP 地址(包的目的地)——IP 协议根据这一地址查找包的传输方向,从而找到下一个路由器的位置。
MAC 头部被写入下一个路由器的以太网地址——以太网协议就知道要将这个包发到哪一个路由器上。
注:当使用除以太网之外的其他网络进行传输时,MAC 头部也会被替换为适合所选通信规格的其他头部。——替代以太网的角色帮助 IP 协议来传输网络包。 - 图解:
包的收发
- TCP 模块委托 IP 模块发送包——(负责在数据块的前面加上 TCP 头部,然后整个传递给 IP 模块)
- 包的封装——IP 模块添加 IP 头部和 MAC 头部。
IP 头部中包含 IP 协议规定的、根据 IP 地址将包发往目的地所需的控制信息;MAC 头部包含通过以太网的局域网将包传输至最近的路由器所需的控制信息。 - 包被交给网络硬件(网卡)——再到达集线器、路由器等转发设备,再由转发设备一步一步地送达接收方。
- 接收过程和发送过程相反,
信息以电信号的形式从网线传输——由网卡将其转换为数字信息——传递给 IP 模块,IP 模块将 TCP 头部加上数据块,传递给 TCP 模块
注:
无论要收发的包是控制包还是数据包,IP 对各种类型的包的收发操作都是相同的。
关于 IP 的工作方式,可适用于任何 TCP 委派的收发操作
IP头部
IP头部有标准格式,IP 头部的“接收方 IP 地址”填写通信对象的 IP 地址。
若客户端有多张网卡:
发送方 IP 地址需要判断发送所使用的网卡,并填写该网卡的 IP 地址。
MAC头部
以太网必须采用相匹配的方式将包发往目的地
MAC头部包含了接收方和发送方的 MAC 地址等信息。
IP 模块根据路由表 Gateway 栏的内容判断把包发送给谁。
ARP
ARP:Address Resolution Protocol,地址解析协议——查询目标路由器的 MAC 地址。
- ARP查询MAC地址(当对方处于同一个子网中,通过操作得到对方的 MAC 地址 。将MAC 地址写入 MAC 头部,MAC 头部完成)
- 查询结构存储入ARP缓存
优化后步骤为:
发送包时,先查询 ARP 缓存有无保存,若不存在则发送 ARP 查询。 - 当MAC头部加在IP头部前,包就完成了。——整个打包工作由IP模块负责——网卡就只负责接收。一块网卡能支持各种类型的包。
以太网
- 以太网是一种为多台计算机能够彼此*和廉价地相互通信而设计的通信技术。
- 以太网的三个基本性质:将包发送到 MAC 头部的接收方 MAC 地址代表的目的地;用发送方 MAC 地址识别发送方;用以太类型识别包的内容。
IP包转化信号
以太网的包收发操作:
光/电信号可以在网线上传输,因此需要将IP生成的网络包(数字信息)转化
以上操作需要用到网卡与网卡驱动程序
- 网卡的 ROM 中保存着全世界唯一的 MAC 地址,这是在生产网卡时写入的。
- 网卡中保存的 MAC 地址会由网卡驱动程序读取并分配给MAC模块。
MAC模块
操作之前:
网卡驱动从 IP 模块获取包,复制到网卡内的缓冲区,向 MAC 模块发送发送包的命令
MAC 模块操作:
- 将包从缓冲区中取出,在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。
FCS(帧校验序列)用来检查包传输过程中因噪声导致的波形紊乱、数据错误,它是一串 32
比特的序列,是通过一个公式对包中从头到尾的所有内容进行计算而得出来的。
在测量电压和电流时为了判断每个比特的界限,引入时钟信号:
向集线器发送网络包
发送信号有两种方式:
- 是使用集线器的半双工模式
当之前的信号传输完毕,或者本来就没有信号在传输时,开始发送信号。首先, MAC 模块从报头开始将数字信息按每个比特转换成电信号,然后由 PHY,或者叫 MAU 的信号收发模块发送出去 。将数字信息转换为电信号的速率就是网络的传输速率,例如每秒将 10 Mbit 的数字信息转换为电信号发送出去, 则速率就是 10 Mbit/s。
PHY(MAU)模块会将信号转换为可在网线上传输的格式,并通过网线发送出去。
注:PHY(MAU)的职责是将 MAC 模块传递过来的信号通过网线发送出去,和监控接收线 路中有没有信号进来。
- 是使用交换机的全双工模式
接收返回包
以在使用集线器的半双工模式以太网为例
- 把接收到的信号全都收进来
- PHY(MAU)模块将信号转换成通用格式并发送给 MAC 模块
- MAC 模块从头开始将信号转换为数字信息,并存放到缓冲区中。当到达信号的末尾时,需要检查 FCS。(信号的开头是报头,通过报头的波形同步时钟,然后遇到起始帧分界符时开始将后面的信号转换成数字信息。)
注:计算FCS的作用
将从包开头到结尾的所有比特套用到公式中计算出 FCS,然后和包末尾的 FCS 进行对比,正常情况下两者应该是一致的,如果中 途受到噪声干扰而导致波形发生紊乱,则两者的值会产生差异,这时这个包就会被当作错误包而被丢弃。- 网卡通知计算机收到了一个包。
- 进行中断机制:
网卡向扩展总线中的中断信号线发送信号,该信号线通过计算 机中的中断控制器连接到 CPU。当产生中断信号时,CPU 会暂时挂起正在处理的任务,切换到操作系统中 的中断处理程序 。然后,中断处理程序会调用网卡驱动,控制网卡执行相应的接收操作。- 网卡驱动被中断处理程序调用后,会从网卡的缓冲区中取出收到的包,并通过 MAC 头部中的以太类型字段 判断协议的类型。
IP----TCP(ICMP查错)
服务器返回的包的以太类型是 0800——网卡驱动会将其交给 TCP/IP 协议栈进行处理——(IP 模块开始工作)——检查 IP 头部格式是否正确——查看接收方 IP 地址——接收这个包。
注:
如果接收失败则会通过ICMP消息告知错误:
UDP协议的收发操作
不需要重发的数据用 UDP 发送更高效
像DNS 查询等交换控制信息的操作基本上都可以在一个包的大小范围内解决的数据,就适合使用UDP。
发送音频和视频数据时适合使用UDP
操作:
(发送)从应用程序获取的数据前面加上 UDP 头部——交给 IP 进行发送——(接收)根据 IP 头部中的接收方和发送方 IP 地址以及UDP 头部中的接收方和发送方端口号——找到相应的套接字并将数据交给相应的应用程序。
因此UDP 只负责单纯地发送包。
小结
- 表示网络包收件人的接收方 IP 地址位于 IP 头部。
- 端口号用来指定服务器程序的种类位于 TCP 头部。
- TCP会对包是否正确送达进行确认。
- ARP——根据 IP 地址查询 MAC 地址的机制