网络模型
为使不同计算机厂家的计算机能够互相通信,国际标准化组织 ISO 1981 年正式推荐了一个网络系统结构一一七层参考模型,也叫作开放系统互连模型。
ISO 七层网络模型及其功能展示:
这个七层网络模型在数据的传输过程中还会对数据进行封装,封装过程如图所示:
ISO 七层网络模型中,当一台主机需要传送用户的数据( data 时,数据首先通过应用层的接口进入应用层。在应用层,用户的数据被加上应用层的报头( Ppplication Header, AH ),形成应用层协议数据单元( Protocol Data Unit, PDU ),然后被递交到下层表示层。表示层并不‘关心’上层应用层的数据格式而是把整个应用层递交的数据包看成是一个整体(应用层数据)进行封装,即加上表示层的报头( Presentation Header, PH 然后,递交到下层会话层 同样,会话层、传输层、网络层(假设用 TCP 传输,则是 TCP 数据+ IP 包头)、数据链路层(把上层的 TCP 数据+ IP 头统一称为帧数据,即帧 +帧数据+帧尾( CRC)) 也都要分别给上层递交下来的数据加上自己的报头。它们是:会话层报头( Session Header, SH )、传输层报头( Transport Header, TH )、 网络层报头( Network Header, NH )和数据链路层报头( Data link Header, DH 其中,数据链路层还要给网络层递交的数据加上数据链路层报尾(Data link Termination, DT )形成最终的一帧数据。
当一帧数据通过物理层传送到目标主机的物理层时,该主机的物理层把它递交到上层一一数据链路层。数据链路层负责去掉数据帧的帧头部 DH 和尾部 DT (同时还进行数据校验)。如果数据没有出错,则递交到上层网络层。同样,网络层、传输层、会话层、表示层、
应用层也要做类似的工作。最终,原始数据被递交到目标主机的具体应用程序中。
五层网络模型:
使用最广泛的为四层模型--TCP/IP 分层模型(TCP/IP Layering Model)。它有因特网分层模型( Internet Layering Model )和因特网参考模型( nternet Reference Model )之称。
四层网络模型表示:
TCP/IP 分层模型的4个协议层分别完成以下的功能:
网络接口层包括用于协作 IP 数据在已有网络介质上传输的协议 实际上 TCP/IP 标准并不定义与 ISO 数据链路层和物理层相对应的功能。相反,它定义了像 APP ( Address Resolution Protocol ,地址解析协议)这样的协议,提供 TCP/IP 协议的数据结构和实际物理硬件之间的接口。
网间层对应于 OSI 七层参考模型的网络层。本层包含 IP 协议、RIP 协议( Routing Information Protocol ,路由信息协议),负责数据的包装、寻址和路由。同时还包含 ICMP (Internet Control Message Protocol ,网间控制报文协议)用来提供网络诊断信息
传输层对应于 OSI 七层参考模型的传输层,它提供两种端到端的通信服务。其中 TCP 协议( Transmission Control Protocol )提供可靠的数据流运输服务, UDP 协议( Use Datagram Protocol )提供不可靠的用户数据报服务
应用层对应于 OSI 七层参考模型的应用层和表示层。因特网的应用层协议包括 Finger Who is FTP (文件传输协议) Gopher HTTP (超文本传输协议)、 Telent (远程终端协议)、SMTP (简单邮件传送协议)、 IRC (因特网中继会话)、NNTP (网络新闻传输协议)等
综上,TCP 协议在网络 OSI 的七层模型中的第四层传输层, IP 协议在第三层网络层, ARP 协议在第二层数据链路层;在第二层上的数据叫 Frame ,在第三层上的数据叫 Packet ,第四层的数据叫 Segment。所有程序的数据首先会打包到 TCP Segment 中,然后 TCP Segment 会打包到 IP Packet ,然后再打包到以太网 Ethernet Frame 中,传到对端后,各个层解析自己的协议,然后把数据交给更高层的协议处理。
TCP头部
TCP头部格式如下:
- URG 标志,表示紧急指针(urgent pointer )是否有效
- ACK 标志,表示确认号是否有效,一般称携带 ACK 标志的 TCP 报文段为“确认报文段”
- PSH 标志,提示接收端应用程序应该立即从 TCP 接收缓冲区中读走数据,为接收后续数据腾出空间(如果应用程序不将接收到的数据读走,它们就会一直停留在 TCP 接收缓冲区中)
- RST 标志,表示要求对方重新建立连接,一般称携带 RST 标志的 TCP 报文段为“复位报文段”
- SYN 标志,表示请求建立一个连接,一般称携带 SYN 标志的 TCP 报文段为“同步报文段”
- FIN 标志,表示通知对方本端要关闭连接了,一般称携带 FIN 标志的 TCP 报文段为“结束报文段”
需要注意以下几点:
TCP的三次握手与四次挥手
TCP 连接的建立可以简单地称为3次握手,而连接的中止则可以称为4次握手
完成三次握手,客户端与服务器开始传送数据,也就是 ESTABLISHED 状态
结束连接:
TCP 一个特别的概念叫作半关闭,这个概念是说,TCP 的连接是全双工(可以同时发送和接收)连接,因此在关闭连接的时候,必须关闭传和送两个方向上的连接。客户机给服务器 FIN的TCP 报文,然后服务器返回给客户端一个确认 ACK 报文,并且发送一个 FIN 报文,当客户机回复 ACK 报文后(4次握手),连接就结束了
为什么建连接要3次握手,断连接需要4次挥手?
对于建连接的3次握手,主要是要初始化 Sequence Number 的初始值。通信的双方要互相通知对方自己的初始化的 Sequence Numbe -- 所以叫 SYN。 也就上图中的J 和 K。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输问题而乱序( TCP 会用这个序号来拼接数据)
对于4次挥手,其实仔细看则是两次,因为 TCP 是全双工的,所以,发送方和接收方都需要 FIN ACK 只不过,有一方是被动的,所以看上去就成了所谓的4次挥手。如果两边同时断连接,那就会就进入到 CLOSING 状态,然后到达 TIME_WAIT状态
TCP状态图
2MSL 等待状态:有一个 TIME_WAIT 等待状态,这个状态又叫作 2MSL状态,说的是在 TIME_WAIT_2 发送了最后一个 ACK 数据报以后,要进入 TIME_WAIT状态。这个状态是防止最后一次握手的数据报没有传送到对方那里而准备的
FIN_WAIT_2 状态:这就是著名的半关闭状态了,这是在关闭连接时,客户端和服务器两次握手之后的状态。在这个状态下,应用程序还有接收数据的能力,但是已经无法发送数据,但是也有一种可能是,客户端一直处于 FIN_WAIT 状态,而服务器则一直处于 WAIT_CLOSE 状态,直到应用层来决定关闭这个状态。
TCP超时重传
下图给出了正常与3中异常的网络传输情况:
当出现以上异常情况时,TCP就会超时重传。TCP 每发送一个报文段,就对这个报文段设置一次计时器,只要计时器设置的重传时间到了,但还没有收到确认,就要重传这一报文段,这个就叫作 超时重传
TCP 协议必须适应两个方面的时延差异:一个是达到不同目的端的时延的差异;另一个是统一连接上的传输时延随业务量负载的变化而出现的差异。为此, TCP 协议使用自适应算法( Adaptive Retransmission Algorithm )以适应互联网分组传输时延的变化。
这种算法的基本要点是 TCP 监视每个连接的性能(即传输时延),由每一个 TCP 的连接情况推算出合适的 RTO 值, 当连接时延性能变化时, TCP 也能够相应地自动修改 RTO 的设定,以适应这种网络的变化。
注:RTO ( Retransmission Timeout ,重传超时时间),指发送端发送数据后、重传数据前等待接受方收到该数据报文的 ack 时间
为了动态地设置, TCP 引入了 RTT (Round Trip Time ),也就是连接往返时间,指发送端从发送 TCP 包开始到接收它的立即响应所耗费的传输时间。
自适应重传算法的关键就在于对当前 RTT 的准确估计,以便适时调整 RTO。
RFC793 中定义的经典算法是这样的: 1. 先采样 RTT ,记下最近几次的 RTT 值;2. 然后做平滑计算 SRTT (Smoothed RTT)。公式中的 \(\alpha\) 取值为 0.8-0.9 ,这个算法叫加权移动平均:
UBOUND 是最大的 timeout 时间,上限值; LBOUND 最小的 timeout 时间 ,下限值;\(\beta\) 取值1.3-2.0
TCP 滑动窗口
TCP 的滑动窗口主要有两个作用:一是提供 TCP 的可靠性;二是提供 TCP 的流控特性。同时滑动窗口机制还体现了 TCP 面向字节流的设计思路
TCP 头部中滑动窗口所处的位置:
TCP 的窗口是 16bit 位字段它代表的是窗口的字节容量,也就是 TCP 的标准窗口最大为 \(2^{16}-1 = 65535\)字节
TCP 的选项字段中还包含了 TCP 窗口扩大因子, option-kind为3, option-length为3, option-data 取值范围 0-14。窗口扩大因子用来扩大 TCP 窗口,可把原来 16bit 的窗口,扩大为 32 bit。
对于 TCP 话的发送方,任何时候在其发送缓存内的数据都可以分为4类:1. 已经发送并得到对端 ACK;2. 发送但还未收到对端 ACK;3. 未发送但对端允许发送; 4. 未发送且对端不允许发送。其中“已发送但还未收到对端 ACK 的”和“未发送但对端允许发送的”这两部分数据称之为发送窗口
对于 TCP 的接收方,在某一时刻在它的接收缓存内存在3种状态:1. 已接收 2. 未接收准备接收 3. 未接收且为准备接收。其中“未接收准备接收”称之为接收窗口
TCP是双工协议,会话的双方都可以同时接收、发送数据 TCP 会话的双方都各自维一个“发送窗口”和一个“接收窗口”。其中各自的“接收窗口”大小取决于应用、系统、硬件的限制(TCP 传输速率不能大于应用的数据处理速率),各自的“发送窗口”则要求取决于对端通告的“接收窗口”,要求相同
滑动窗口实现面向流的可靠性来源于“确认重传”机制。TCP 的滑动窗口的可靠性也是建立在“确认重传”基础上的。
TCP拥塞控制
TC 的拥塞控制由4个核心算法组成:慢开始(Slow Start)、拥塞避免(Congestion Voidance)、快速重传(Fast Retransmit)和快速恢复(Fast Recovery)
发送方维持一个叫作拥塞窗口 cwnd ( congestion window )的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化,发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口
慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小
慢开始的原理如下所述:
ssthresh的用法如下:(发送方每收到一个确认就把窗口 cwnd+l)
拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间 RTT 就把发送方的拥塞窗口cwnd 加1,而不是加倍
拥塞控制具体过程如下所述:
慢开始和拥塞避免算法的实现举例:
快重传和快恢复
快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方),而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到3个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。
快重传示意图:
快重传配合使用的还有快恢复算法,有以下两个要点:
从连续收到3个重复的确认转入拥塞避免示意图:
从整体上来讲, TCP 拥塞控制窗口变化的原则是加法增大、乘法减小
TCP网络编程
在网络中,进程使用三元组 (Ip地址,协议,端口)来标识网络中的进程,网络中的进程通信就可以利用这个标志与其他进程进行交互。
网络中的进程是通过 socket 来通信的,用 "打开 (open) -> 读写 (write/read) -> 关闭 (close)" 模式来操作。 socket 是一种特殊的文件, 一些 socket 函数就是对其进行的操作(读/写 打开、关闭)。
使用 TCP/IP 协议的应用程序通常采用应用编程接口: UNIX BSD 的套接字 (socket),来实现网络进程之间的通信。
以 TCP 协议通信的 socket 为例,其交互流程大概如图所示:
具体流程如下:
TCP 头部的选项部分是为了 TCP 适应复杂的网络环境和更好地服务于应用层而进行设计的。TCP 选项部分最长可以达到 40 Byte ,再加上 TCP 选项外的固定的 20 Byte 部分, TCP的最长头部可达 60 Byte。TCP 头部长度可以通过 TCP 头部中的“数据偏移”位来查看。值得注意的是: TCP 偏移量的单位是 32bit ,也就是 4Byte。TCP 偏移量共占 4bite,取最大 llll (B) 计算也就是十进制的 15。15*4 Byte=60 Byte,这个也是 TCP 首部不超过 60 Byte 的原因。
网络字节序与主机序
不同的 CPU 有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,称为主机序。最常见的有两种:1. Little Endian ,将低序字节存储在起始地址;2. Big Endian,将高序字节存储在起始地址
大小端存储数字 Oxl2345678 示意图:
C/C++ 语言编写的程序里数据存储顺序是跟编译平台所在的 CPU 相关的,而 Java 编写的程序则唯一采用 Big Endian 方式来存储数据,所有网络协议也都是采用 Big Endian 的方式来传输数据的。所以有时也会把 Big Endian 方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。
TCP 是个“流”协议,所谓流,就是没有界限的一串数据。在进行数据传输时,由于网络或Nagle 算法的原因会导致“粘包”的情况,即,加上要同时发送A和B数据,A中的部分数据和B一起被接收或B中的部分数据和A一起被接收。可使用封包和拆包的方法来解决该问题。由于UDP本身就是个数据包协议,因此不存在该问题。
封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了。包头其实上是个大小固定的结构体,其中有个结构体成员变量表示包体的长度,这是个很重要的变量,其他的结构体成员可根据需要自己定义。根据固定的包头长度以及包头中含有的包体长度的变量值就能正确的拆分出一个完整的数据包。所以为了解决“粘包”的问题,大家通常会在所发送的内容前加上发送内容的长度,所以
对方就会先收4 Byte,解析获得接下来需要接收的长度,再进行收包。
原文:https://www.cnblogs.com/zhhfan/p/14546227.html