文章目录
综合许多大佬的总结
TCPIP_2">OSI和TCP/IP模型
OSI七层网络模型 | TCP/IP四层概念模型 | 对应网络协议 |
应用层(Application) | 应用层 | HTTP、TFTP, FTP, NFS, WAIS、SMTP |
表示层(Presentation) | Telnet, Rlogin, SNMP, Gopher | |
会话层(Session) | SMTP, DNS | |
传输层(Transport) | 传输层 | |
网络层(Network) | 网络层 | IP, ICMP, ARP, RARP, AKP, UUCP |
数据链路层(Data Link) | 数据链路层 | FDDI, Ethernet, Arpanet, PDN, SLIP, PPP |
物理层(Physical) | IEEE 802.1A, IEEE 802.2到IEEE 802.11 |
UDP_8">UDP
UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。
UDP_11">UDP协议的特点
-
无连接
首先 UDP 是不需要和 TCP一样在发送数据前进行三次握手建立连接的,想发数据就可以开始发送了。即:只知道对端的IP和端口号就可以发送,不需要实现建立连接。
-
单播,多播,广播
UDP 不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说 UDP 提供了单播,多播,广播的功能。
-
面向数据报
UDP只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接操作。
具体来说就是:应用层交给UDP多长的报文, UDP原样发送既不会拆分,也不会合并。如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个 字节,而不能循环调用10次recvfrom, 每次接收10个字节。所以UDP不能够灵活的控制读写数据的次数和数量。
-
不可靠
没有确认机制, 没有重传机制。如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息。
-
实时高效
UDP报文格式:
可以看到,UDP报文包括数据报文
和UDP头部
,而UDP头部只包括源端口、目的端口、数据包长度和校验和。因此,UDP头部开销小,只有8个字节,相比 TCP 的至少二十字节要少得多,在传输数据报文时是很高效的。
UDP_36">UDP的缓冲区
UDP存在接收缓冲区,但不存在发送缓冲区。
UDP__41">UDP 的主要应用场景
- 需要资源少,网络情况稳定的内网,或者对于丢包不敏感的应用,比如 DHCP 就是基于 UDP 协议的。
- 不需要一对一沟通,建立连接,而是可以广播的应用。因为它不面向连接,所以可以做到一对多,承担广播或者多播的协议。
- 需要处理速度快,可以容忍丢包,但是即使网络拥塞,也毫不退缩,一往无前的时候
基于 UDP 的几个例子:
TCP_51">TCP
TCP协议全称是传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的RFC 793定义。TCP 是面向连接的、可靠的流协议。流就是指不间断的数据结构,你可以把它想象成排水管中的水流。
TCP_54">TCP协议的特点
-
面向连接
面向连接,是指发送数据之前必须在两端建立连接。建立连接的方法是“三次握手”,这样能建立可靠的连接。建立连接,是为数据的可靠传输打下了基础。
-
单播
每条TCP传输连接只能有两个端点,只能进行点对点的数据传输,不支持多播和广播传输方式。
-
面向字节流
-
可靠传输
对于可靠传输,判断丢包,误码靠的是TCP的段编号以及确认号。TCP为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的字节发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。
-
拥塞控制
当网络出现拥塞的时候,TCP能够减小向网络注入数据的速率和数量,缓解拥塞
TCP_70">TCP报文格式
三次握手
过程:
第一次:客户端发送初始序号x和syn=1请求标志
第二次:服务器发送请求标志syn,发送确认标志ACK,发送自己的序号seq=y,发送客户端的确认序号ack=x+1
第三次:客户端发送ACK确认号,发送自己的序号seq=x+1,发送对方的确认号ack=y+1
分析:
第一次:客户端发送请求到服务器,服务器知道客户端发送,自己接收正常。SYN=1,seq=x
第二次:服务器发给客户端,客户端知道自己发送、接收正常,服务器接收、发送正常。ACK=1,ack=x+1,SYN=1,seq=y
第三次:客户端发给服务器:服务器知道客户端发送,接收正常,自己接收,发送也正常.seq=x+1,ACK=1,ack=y+1
可以看出,握手两次达不到让双方都得出自己、对方的接收、发送能力都正常的结论的。
四次挥手
过程:
第一次挥手:客户端发出释放FIN=1,自己序列号seq=u,进入FIN-WAIT-1状态
第二次挥手:服务器收到客户端的后,发出ACK=1确认标志和客户端的确认号ack=u+1,自己的序列号seq=v,进入CLOSE-WAIT状态
第三次挥手:客户端收到服务器确认结果后,进入FIN-WAIT-2状态。此时服务器发送释放FIN=1信号,确认标志ACK=1,确认序号ack=u+1,自己序号seq=w,服务器进入LAST-ACK(最后确认态)
第四次挥手:客户端收到回复后,发送确认ACK=1,ack=w+1,自己的seq=u+1,客户端进入TIME-WAIT(时间等待)。客户端经过2个最长报文段寿命后,客户端CLOSE;服务器收到确认后,立刻进入CLOSE状态。
分析:
第一次:客户端请求断开FIN,seq=u
第二次:服务器确认客户端的断开请求ACK,ack=u+1,seq=v
第三次:服务器请求断开FIN,seq=w,ACK,ack=u+1
第四次:客户端确认服务器的断开ACK,ack=w+1,seq=u+1
总结
-
为什么三次握手和四次挥手?
三次握手时,服务器同时把ACK和SYN放在一起发送到了客户端那里
四次挥手时,当收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方 ACK 和 FIN 一般都会分开发送。 -
为什么客户端最后还要等待2MSL?
客户端需要保证最后一次发送的ACK报文到服务器,如果服务器未收到,可以请求客户端重发,这样客户端还有时间再发,重启2MSL计时。
累计确认
首先为了保证顺序性,每个包都有一个 ID。在建立连接的时候会商定起始 ID 是什么,然后按照 ID 一个个发送,为了保证不丢包,需要对发送的包都要进行应答,当然,这个应答不是一个一个来的,而是会应答某个之前的 ID,表示都收到了,这种模式成为累计应答或累计确认。
为了记录所有发送的包和接收的包,TCP 需要发送端和接收端分别来缓存这些记录,按照包的 ID 一个个排列。
发送端缓存
结构如下:
- 发送并且确认的
- 发送尚未确认的
- 没有发送等待发送的
- 没有发送并且暂时不会发送的
第三部分和第四部分就属于流量控制的内容
在 TCP 里,接收端会给发送端报一个窗口大小,叫 Advertised window。这个窗口应该等于上面的第二部分加上第三部分,超过这个窗口,接收端做不过来,就不能发送了
接收端缓存
结构如下:
- 接收并且确认过的
- 还没接收,但是马上就能接收的
- 还没接收,但也无法接收的
顺序问题和丢包问题
结合上面的图看,在发送端,1、2、3 已发送并确认;4、5、6、7、8、9 都是发送了还没确认;10、11、12 是还没发出的;13、14、15 是接收方没有空间,不准备发的。
在接收端来看,1、2、3、4、5 是已经完成 ACK 但是还没读取的;6、7 是等待接收的;8、9 是已经接收还没有 ACK 的。
发送端和接收端当前的状态如下:
1、2、3 没有问题,双方达成了一致
4、5 接收方说 ACK 了,但是发送方还没收到
6、7、8、9 肯定都发了,但是 8、9 已经到了,6、7 没到,出现了乱序,缓存着但是没办法 ACK。
根据这个例子可以知道顺序问题和丢包问题都有可能存在,所以我们先来看确认与重传机制。
假设 4 的确认收到了,5 的 ACK 丢了,6、7 的数据包丢了,该怎么办?
一种方法是超时重试,即对每一个发送了但是没有 ACK 的包设定一个定时器,超过了一定的事件就重新尝试。这个时间必须大于往返时间,但也不宜过长,否则超时时间变长,访问就变慢了。
如果过一段时间,5、6、7 都超时了就会重新发送。接收方发现 5 原来接收过,于是丢弃 5;6 收到了,发送 ACK,要求下一个是 7,7 不幸又丢了。当 7 再次超时的时候,TCP 的策略是超时间隔加倍。每当遇到一次超时重传的时候,都会讲下一次超时时间间隔设为先前值的两倍。
超时重传的机制是超时周期可能相对较长,是否有更快的方式呢?
有一个快速重传的机制,即当接收方接收到一个序号大于期望的报文段时,就检测到了数据流之间的间隔,于是发送三个冗余的 ACK,客户端接收到之后,知道数据报丢失,于是重传丢失的报文段。
例如,接收方发现 6、8、9 都接收了,但是 7 没来,所以肯定丢了,于是发送三个 6 的 ACK,要求下一个是 7。客户端接收到 3 个,就会发现 7 的确又丢了,不等超时,马上重发。
流量控制
双方在通信的时候,发送方的速率与接收方的速率是不一定相等,如果发送方的发送速率太快,会导致接收方处理不过来,这时候接收方只能把处理不过来的数据存在缓存区里(失序的数据包也会被存放在缓存区里)
如果缓存区满了发送方还在疯狂着发送数据,接收方只能把收到的数据包丢掉,大量的丢包会极大着浪费网络资源,因此,我们需要控制发送方的发送速率,让接收方与发送方处于一种动态平衡才好。对发送方发送速率的控制,我们称之为流量控制。
如何控制:
接收方每次收到数据包,可以在发送确定报文的时候,同时告诉发送方自己的缓存区还剩余多少是空闲的,我们也把缓存区的剩余大小称之为接收窗口大小,用变量win来表示接收窗口的大小。
发送方收到之后,便会调整自己的发送速率,也就是调整自己发送窗口的大小,当发送方收到接收窗口的大小为0时,发送方就会停止发送数据,防止出现大量丢包情况的发生。
发送方何时再继续发送数据:
当发送方收到接受窗口 win = 0 时,这时发送方停止发送报文,并且同时开启一个定时器,每隔一段时间就发个测试报文去询问接收方,打听是否可以继续发送数据了,如果可以,接收方就告诉他此时接受窗口的大小;如果接受窗口大小还是为0,则发送方再次刷新启动定时器。
说明:
由于TCP/IP支持全双工传输,因此通信的双方都拥有两个滑动窗口,一个用于接受数据,称之为接收窗口;一个用于发送数据,称之为拥塞窗口(即发送窗口)。指出接收窗口大小的通知我们称之为窗口通告。
接收窗口的大小是根据某种算法动态调整的,
- 太小的话,会严重浪费链路利用率,增加丢包率。
- 当接收窗口达到某个值的时候,再增大的话也不怎么会减少丢包率的了,而且还会更加消耗内存。
所以接收窗口的大小必须根据网络环境以及发送发的的拥塞窗口来动态调整。
拥塞控制
TCP 拥塞控制主要来避免两种现象,包丢失和超时重传,一旦出现了这些现象说明发送的太快了,要慢一点。
具体的方法就是发送端慢启动,比如倒水,刚开始倒的很慢,渐渐变快。然后设置一个阈值,当超过这个值的时候就要慢下来。
慢下来还是在增长,这时候就可能水满则溢,出现拥塞,需要降低倒水的速度,等水慢慢渗下去。
拥塞的一种表现是丢包,需要超时重传,这个时候,采用快速重传算法,将当前速度变为一半。所以速度还是在比较高的值,也没有一夜回到解放前。
总结
UDP | TCP | |
---|---|---|
是否连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
连接对象个数 | 支持一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 首部开销小,仅8字节 | 首部最小20字节,最大60字节 |
适用场景 | 适用于实时应用(IP电话、视频会议、直播等) | 适用于要求可靠传输的应用,例如文件传输 |
参考链接:一文读懂OSI七层模型与TCP/IP四层的区别/联系
参考链接:一文搞懂TCP与UDP的区别
参考链接:TCP 和 UDP 的区别
参考链接:TCP三次握手和四次挥手通俗理解
参考链接:通俗易懂讲解TCP流量控制机制,了解一下