三、TCP篇
1. TCP
【1】TCP是什么?有哪些特点?
TCP是面向连接的、可靠的、基于字节流的、具有流量控制、拥塞控制、重传机制的传输层控制协议
特点:
1. TCP是面向连接的传输层控制协议
2. 每一条TCP连接只能有两个端点,是点对点连接
3. TCP提供全双工通信,连接的两端都可以临时存放双向通信的数据
4. TCP提供可靠交付的服务,传送的数据保证无差错、不丢失、不重复、并且按序到达
5. TCP面向字节流,将应用程序交下来的数据仅看成是一串无结构的字节流,写入缓存后根据对方的窗口值和当前网络拥塞的程度来决定一个报文段应该包含多少个字节。
(2)TCP头格式
TCP头部有20字节固定,还有长度可变的选项。
1. 源端口和目的端口:各占2字节
2. 序列号:占4字节。在建⽴连接时由计算机⽣成的随机数作为其初始值。
通过SYN包传给接收端主机,每发送⼀次数据,就「累加」⼀次该「数据字节数」的⼤⼩。⽤来解决⽹络包乱序问题。
3. 确认应答号:占4字节。指下⼀次「期望」收到的数据的序列号。
发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。⽤来解决不丢包的问题。
4. 首部长度:占1字节,记录首部的长度,因为有可选项
5. 保留:占6位,保留为今后使用
6. 六个控制位:占6位
* 紧急URG:当'URG=1'时,表明'紧急指针'字段有效,告诉系统应尽快传送该报文段,配合紧急指针字段使用;
* 确认ACK:当'ACK=1'时,表明「确认应答」字段有效,在连接建立后所有传送的报文段都必须把ACK置1;
* 推送PSH:当两个应用进程进行交互时通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的相应,此时可以使用推送操作;
紧急`URG`和推送`PSH`之间的区别:`URG=1`时,紧急数据不进入接收缓存,直接交付给应用进程,剩下的数据进入接收缓存;`PSH=1`时,不用等到整个接收缓存填满,而是可以直接交付,但注意这里的交付还是从缓存中交付的;
* 复位RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
* 同步SYN:该位为 1 时,表示希望建⽴连接,并在其「序列号」的字段进⾏序列号初始值的设定。
* 终止FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。
当通信结束希望断开连接时,通信双⽅的主机之间就可以相互交换FIN位为1的TCP段
7. 窗口大小:2字节。是指发送本报文段的一方的接收窗口,表明从本报文段首部中的确认号算起,接收方目前允许对方发送的数据量;
8. 校验和:2字节。检验和字段检验的范围包括首部和数据两部分;计算校验和时,需要在TCP报文段前面加上12个字节的伪首部,接收方收到此报文段后,仍要加上这个伪首部来计算校验和;
9. 紧急指针:2字节。仅在`URG=1`时才有意义;值得注意的是,即使窗口为零时也可发送紧急数据;
10. 选项:长度可变,最长可达40个字节。
可以用于存放最大报文段长度MSS,即TCP报文段长度减去TCP首部长度。
(3)TCP的发送缓存和接收缓存分别用于存放什么?
发送缓存用于存放:
- 发送应用程序传送给发送方TCP准备发送的数据; 【准备发送的数据】
- 已发送出但尚未收到确认的数据。 【已发送但未收到确认的数据】
接收缓存用于存放:
- 按序到达的但尚未被接收应用程序读取的数据; 【按序到达但未读取的数据】
- 未按序到达的数据。 【未按序的数据】
(4)TCP怎么保证可靠的传输?
(1)数据包校验
目的是检测数据在传输过程中的任何变化,若检验出包有错,则丢弃报文段并且不给出响应,这时TCP发送数据端超时后会重发数据。
(2)对失序数据包重排序
既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的达到也可能失序。TCP将对失序数据进行重新排序,然后才交给应用层。
(3)丢弃重复数据
(4)应答机制
当收到来自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送的,通常推迟几分之一秒。
(5)超时重传
当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能即时收到一个确认,将重发这个报文段。
(6)流量控制
TCP连接的每一方都有一个一定的大小的缓冲空间。TCP的接收端只允许发送接收端缓冲区所能接纳的数据量,这可以防止较快主机致使较慢主机的缓冲区溢出,这就是流量控制。
TCP使用的流量控制协议是可变大小的滑动窗口协议。
//TCP会利用窗口控制来提高传输速度,意思是在一个窗口大小内,不用一定要等到应答才能发送下一段数据,窗口大小就是无需等待确认而可以继续发送数据的最大值。如果不使用窗口控制,每一个没收到确认应答的数据都要重发。
(7)拥塞控制
* 如果把窗口定的很大,发送端连续发送大量的数据,可能会造成网络的拥堵(大家都在用网,你在这狂发,吞吐量就那么大,当然会堵),甚至造成网络的瘫痪。所以TCP在为了防止这种情况而进行了拥塞控制。
(5)为什么需要TCP协议?
因为IP层是不可靠的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。
所以需要TCP来实现可靠的传输
(6)如何确定一个TCP连接?
TCP四元组:源地址、源端口、目的地址、目的端口
源地址和⽬的地址的字段(32位)是在IP头部中,作⽤是通过IP协议发送报⽂给对⽅主机。
源端⼝和⽬的端⼝的字段(16位)是在TCP头部中,作⽤是告诉TCP协议应该把报⽂发给哪个进程。
(7)TCP最大连接数?
问:有⼀个IP的服务器监听了⼀个端口,它的TCP的最大连接数是多少?
答:最大TCP连接数 = 客户端的IP数 * 客户端的端口数
(8)什么是粘包、拆包?
问:什么是粘包?
TCP是基于字节流的,虽然应用层和TCP传输层之间的数据交互是大小不等的数据块,但是TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界;
从TCP的帧结构也可以看出,在TCP的首部没有表示数据长度的字段。
基于上面两点,在使用 TCP 传输数据时,才有粘包或者拆包现象发生的可能。一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。
接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。拆包和粘包的问题导致接收端在处理的时候会非常困难,因为无法区分一个完整的数据包。
问:TCP粘包怎么产生?
1. 发送方产生粘包
采用 TCP 协议传输数据的客户端与服务器经常是保持一个长连接的状态(一次连接发一次数据不存在粘包),双方在连接不断开的情况下,可以一直传输数据。但当发送的数据包过于的小时,那么 TCP 协议默认的会启用 Nagle 算法,将这些较小的数据包进行合并发送(缓冲区数据发送是一个堆压的过程);这个合并过程就是在发送缓冲区中进行的,也就是说数据发送出来它已经是粘包的状态了。
2. 接收方产生粘包
接收方采用 TCP 协议接收数据时的过程是这样的:数据到接收方,从网络模型的下方传递至传输层,传输层的 TCP 协议处理是将其放置接收缓冲区,然后由应用层来主动获取(C 语言用 recv、read 等函数);这时会出现一个问题,就是我们在程序中调用的读取数据函数不能及时的把缓冲区中的数据拿出来,而下一个数据又到来并有一部分放入的缓冲区末尾,等我们读取数据时就是一个粘包。(放数据的速度 > 应用层拿数据速度)
问:怎么解决拆包和粘包?
分包机制一般有两个通用的解决方法:
1. 特殊字符控制;
2. 在包头首都添加数据包的长度。
如果使用 netty 的话,就有专门的编码器和解码器解决拆包和粘包问题了。
tips:UDP没有粘包问题,但是有丢包和乱序。不完整的包是不会有的,收到的都是完全正确的包。传送的数据单位协议是UDP报文或用户数据报,发送的时候既不合并,也不拆分。
2. UDP
(1)UDP协议的特点?
1. 无连接的传输层协议
2. 支持一对一、一对多、多对多的交互通信
3. 尽最大努力交付,即不保证不可靠的传输
4. 面向报文,将应用程序交下来的数据,既不合并也不拆分,保留报文的边界,添加首部后就向下交付到IP层;因此,应用程序必须选择合适大小的报文。
5. UDP首部开销小,只有8字节。TCP则至少需要20字节。
6. 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低。
(2)UDP头格式
1. ⽬标和源端⼝:主要是告诉UDP协议应该把报⽂发给哪个进程。
2. 包⻓度:UDP用户数据报长度(UDP首部+数据)
3. 校验和:检测UDP用户数据报在传输中是否有错,有错则丢弃
(3)UDP如何计算校验和
- 在计算检验和时,需要在UDP用户数据报之前增加12个字节的伪首部,不向下传送也不向上递交,仅用于计算校验和;
- 在发送方,把全零放入检验和字段,再把伪首部和UDP用户数据报看成是由许多16位字,使用全零补齐字节后,按照二进制反码计算这些16位字的和,将此和的二进制反码写入校验和字段,然后发送;
- 在接收方,把伪首部、收到的UDP用户数据报以及可能的补齐字节后,按照二进制反码求这些16位字的和,无差错时其结果应为全1,否则出现差错。
(4)TCP、UDP区别?
1. 连接
TCP面向连接、
UDP无连接
2. 服务对象
TCP是一对一的两点服务
UDP支持一对一、一对多、多对多的交互通信
3. 可靠性
TCP是可靠交付:无差错,不丢失,不重复,按序到达
UDP是尽最大努力交付,不保证可靠交付
4. 拥塞控制、流量控制
TCP有拥塞控制和流量控制保证数据传输的安全性
UDP没有拥塞控制,网络拥塞不会影响源主机的发送效率
5. 首部开销
TCP:20字节
UDP:8字节
6. 传输方式
TCP:基于字节流
UDP:基于数据包
7. 分片不同
TCP 的数据⼤⼩如果⼤于 MSS ⼤⼩,则会在传输层进⾏分⽚,⽬标主机收到后,也同样在传输层组装 TCP
数据包,如果中途丢失了⼀个分⽚,只需要传输丢失的这个分⽚。
UDP 的数据⼤⼩如果⼤于 MTU ⼤⼩,则会在 IP 层进⾏分⽚,⽬标主机收到后,在 IP 层组装完数据,接着
再传给传输层,但是如果中途丢了⼀个分⽚,在实现可靠传输的 UDP 时则就需要᯿传所有的数据包,这样
传输效率⾮常差,所以通常 UDP 的报⽂应该⼩于 MTU。
8. 应用场景
TCP:可靠但传输速度慢,若需保证数据完整性用TCP;常用于FTP文件传输、HTTP/HTTPS
UDP:不可靠但传输速度快;常用于视频、音频等多媒体通信;广播通信
问:为什么UDP头部没有首部字段,而TCP有
答:因为TCP有可变长的选项字段,而UDP头部长度是固定的,无需多一个字段去记录UDP的首部长度
问:为什么UDP头部有包长度字段,TCP没有?
(5)TCP、UDP分别对应的常见应用层协议有哪些?
TCP对应的应用层协议:
1. FTP协议:文件传输协议,使用21端口。常说某某计算机打开了FTP服务便是启动了文件传输协议。下载文件、上传主页都要用FTP服务。
2. Telnet:是一种用于远程登录的端口,用户可以以自己的身份远程连接到计算机上,通过这种端口可以提供一种基于DOS模式下的通信服务。如以前的BBS是纯字符界面的,支持BBS的服务器将23端口打开,对外提供服务。
3. SMTP:简单邮件传输协议,现在很多邮件服务器都用此协议用于发送邮件。常见的免费邮件服务器中用的就是这个邮件服务端口,所以在电子邮件设置中常看到有SMTP端口设置这个栏,服务器开放的是25端口。
4. POP3:与SMTP对应,POP3用于接收邮件。通常情况下,POP3协议所用的是110端口。只要你有相应的使用POP3协议的程序(Fo-xmail、Outlook等),就可以不以Web方式登录进邮箱界面,直接用邮件程序就可以收到邮件。
5. HTTP
基于UDP的应用层协议:
1. DNS:用于域名解析服务,将域名地址转换为IP地址。DNS用的是53号端口。
2. SNMP:简单网络管理协议,使用161端口,用来管理网络设备的,由于网络设备很多,无连接的服务就体现出其优势。
3. TFTP:Trival File Transfer Protocol,简单文件传输协议,69端口
3. TCP三次握手
(1)TCP三次握手
1. 一开始,客户端和服务器都处于CLOSE状态。先是服务器主动监听某个端口,处于LISTEN状态
2. 客户端向服务端发出连接请求报文段。
客户端会随机初始化序列号(client_isn),同时把SYN标志置1,表示SYN报文。
接着把该报文发送给服务器,该报文不包含应用层数据,之后客户端处于SYN_SENT状态
3. 服务器接收到客户端的连接请求报文后,如果同意连接,
首先服务器也随机初始化自己的序列号(server_isn),
其次把TCP首部的确认应答号字段填入client_isn+1,
接着把SYN和ACK标志位置1.
最后把报文发给客户端,
该报文也不包含应用层数据,之后服务器处于SYN_RCVD状态。
4. 客户端接收到服务器报文后,还要向服务器回应最后一个应答报文。
首先该应答报文TCP首部ACK标志位置1,
其次确认应答号字段填入server_isn+1,
最后把报文发送给服务器,
这次报文可以携带客户端到服务器的数据,之后客户端处于ESTABLISHED状态。
5. 服务器收到客户端的应答报文后,也进入ESTABLISHED状态。
(2)如何在Linux系统中查看TCP状态
netstat -napt
(3)为什么是三次握手?不是两次、四次
为什么不四次:
1. 三次就行没必要四次;避免资源浪费。
为什么不两次:
回答1、两次不能确认双方的接受能力、发送能力是否正常。
* 第一次握手,服务器得出结论:服务器的接收、客户端的发送没问题
* 第二次握手,客户端得出结论:客户端发送和接收没问题,服务器发送接收没问题。
* 第三次握手,服务器得出结论:客户端发送和接收没问题,服务器发送接收没问题。
回答2、两次握手不能同步双方初始序列号
三次握手是为了防止客户端的请求报文在网络滞留,客户端超时重传了请求报文,服务器建立连接,传输数据,释放连接之后,服务器又收到了客户端滞留的请求报文,建立连接一直等待客户端发送数据。
两次握手的话:
服务器对客户端的请求进行回应(第二次握手后),就会理所当然的认为已建立连接,而如果客户端并没有收到服务器的回应呢?此时,客户端仍认为连接未建立,服务器会对已建立的保存必要的资源。如果大量的这种情况,服务器会奔溃。
三次握手原因有三点:
1. 避免历史连接
2. 同步双方初始序列号
3. 避免资源浪费
序列号的作用:除去重复数据、按序接收,哪些收到哪些没收到
- 接收方可以去除重复的数据
- 接收方可以根据数据包的序列号按序接收
- 可以标识发送出去的数据包中,哪些是已经被对方收到的
之所以需要进行三次握手,也就是客户端最后要发送一次确认报文段,主要是为了防止已失效的连接请求报文段突然传送到了服务端,因而产生错误;所谓“已失效的连接请求报文段”,是指:
- 客户端发送一次连接请求报文段,在某些网络节点长时间滞留而未丢失,于是客户端再发送一次连接请求,成功建立连接后又成功释放连接;
- 此时第一个连接请求终于到达,服务端误认为这是客户端新的连接请求,于是就向客户端发送确认报文段,同意建立连接;
- 此时如果不采用第三次握手,那么只要服务端发出确认请求,新的连接就建立了,但由于客户端实际上并没有发出新的连接请求,自然不会理睬,而服务端一直等待客户端发送数据,造成资源浪费。
(4)为什么客户端和服务器端的初始序列号ISN不相同?
1. 如果⼀个已经失效的连接被重⽤了,但是该旧连接的历史报⽂还残留在⽹络中,如果序列号相同,那么就⽆法分辨出该报⽂是不是历史报⽂,如果历史报⽂被新的连接接收了,则会产⽣数据错乱。
所以,每次建⽴连接前重新初始化⼀个序列号主要是为了通信双⽅能够根据序号将不属于本连接的报⽂段丢弃。
2. 另⼀⽅⾯是为了安全性,防⽌⿊客伪造的相同序列号的TCP报⽂被对⽅接收。
(5)既然IP层会分片,为什么TCP层还需要MSS呢?
MTU:一个网络包的最大长度,以太网中一般为1500字节。
MSS:除去IP和TCP头部后,一个网络包能容纳的TCP数据的最大长度
如果一个IP分片丢失,整个IP报文的所有分片都得重传。所以,为了达到最佳的传输效能TCP协议在建立连接的时候通常要协商双方的MSS值,之后形成的IP包的长度也就不会大于MTU,自然就不用IP分片了。
4. TCP四次挥手
(1)TCP四次挥手
双方都可以通过四次挥手主动断开连接
以客户端主动断开连接为例:
1. 客户端打算关闭连接,此时会发送一个TCP首部FIN标志置1的报文,也即FIN报文,之后客户端进入FIN_WAIT_1状态
2. 服务器收到该报文后,就向客户端发送ACK应答报文,接着客户端进入CLOSED_WAIT状态
3. 客户端收到服务端的ACK应答报文后,进入FIN_WAIT_2状态
4. 等待服务器处理完数据后,也向客户端发送FIN报文,之后服务器进入LAST_ACK状态
5. 客户端收到服务器的FIN报文后,回一个ACK应答报文,之后进入TIME_WAIT状态
6. 服务器收到了ACK应答报文后,就进入了CLOSED状态,至此服务器已经完成了连接的关闭
7. 客户端在经过一段时间后(2MSL),自动进入CLOSED状态,至此客户端也完成了连接的关闭
主动关闭连接的,才有TIME_WAIT状态
(2)为什么挥手需要四次
* 关闭连接时,客户端向服务器发送FIN时,仅仅表示客户端不再发送数据了但是还能接收数据。
* 服务器收到客户端的FIN报文后,先回一个ACK应答报文,而服务器可能还有数据需要处理和发送,等服务器不再发送数据后,才发送FIN报文给 客户端表示同意现在关闭连接。
服务器需要等待完成数据的发送和处理,所以服务器的ACK和FIN一般都会分开发送。
(3)TIME_WAIT是什么?为什么需要?
问:为什么TIME_WAIT必须等待2MSL的时间?
1. 保证最后一次握手报文能到B,能进行超时重传。
2. 2MSL后,这次连接的所有报文都会消失,不会影响下一次连接。
详细解答:
1. 为了保证发送方发送的最后一个ACK报文能够到达接收方,帮助接收方关闭连接。如果这个报文丢失,处于LAST_ACK的接收方就会超时重传FIN报文,接收方在2MSL时间内接收到这个重传报文,接着重传一次ACK确认报文,重新启动2MSL计时器,接收方收到后则关闭连接。
如果A在TIME_WAIT不等待你一段时间,而是发送完ACK后直接释放连接,该报文丢失另一方则不能按照正常步骤进入CLOSED状态。
2. 防止已失效的连接请求报文段出现在本连接中。
A发送完最后一个ACK报文段后,再经过2MSL就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,这样就可以使下一个连接中不会出现这种旧的连接请求报文段。
问:为什么TIME_WAIT等待的时间是2MSL?
MSL是Maximum Segment Lifetime,报文最大生存时间。
MSL和TTL的区别是:MSL的单位是时间,TTL是经过路由跳数
MSL >= TTL
网络中可能存在来自接收方的数据包,当这些数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待2倍的时间
2MSL的时间是从客户端接收到FIN后发送ACK开始计时的。如果在TIME_WAIT时间内,因为客户端的ACK没有传输到服务端,客户端又接收到了服务器重发的FIN报文,那么2MSL时间将重新计时。
TIME_WAIT等待时间应合理
* 服务器正常收到四次挥手的最后一个ACK报文,则服务器正常关闭连接
* 服务器没有收到四次挥手的最后一个ACK报文时,则会重发FIN关闭连接报文(重新第三次挥手)等待新的ACK报文。
问:TIME_WAIT过多有什么危害?
1. 第一是内存资源占用
2. 第二是对端口资源的占用,一个TCP连接至少消耗一个本地端口;端口被占满会导致无法创建新的连接。
(4)保活计时器的作用?如果已建立连接,但客户端突出故障怎么办?
TCP有一个机制是保活机制
TCP有一个保活计时器,在这个时间段内,如果没有任何连接相关的活动,TCP保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前TCP连接已经死亡,系统内核将错误信息通知给上层应用程序。
//服务器每次收到一个客户的数据,就重新设置保活计时器,时间的设置通常是两个小时。若两个小时都没有收到客户端的数据,服务器就发送一个探测报文,以后则隔75秒发送一个。若连续发送10个探测报文段后仍然无客户端的响应,服务器就认为客户端出了故障,接着就关闭这个连接。
如果开启了TCP报活,需要考虑以下几种情况:
第⼀种,对端程序是正常⼯作的。当TCP保活的探测报⽂发送给对端, 对端会正常响应,这样TCP保活时间会被重置,等待下⼀个TCP保活时间的到来。
第⼆种,对端程序崩溃并重启。当TCP保活的探测报⽂发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,会产⽣⼀个RST报⽂,这样很快就会发现TCP连接已经被重置。
第三种,是对端程序崩溃,或对端由于其他原因导致报⽂不可达。当TCP保活的探测报⽂发送给对端后,⽯沉⼤海,没有响应,连续⼏次,达到保活探测次数后,TCP会报告该TCP连接已经死亡。
5. 重传机制
(1)重传机制
TCP实现可靠传输的方式之一,是通过序列号与确认应答。
在TCP中,当发送端的数据达到接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。
数据在传输过程中丢失了怎么办?—重传机制
常见的重传机制:
1. 超时重传
2. 快速重传
3. SACK
4. D-SACK
(2)超时重传?
超时重传:在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的ACK确认应答报文,就会重发该数据,即超时重传。
发生超时重传的两种情况:
1. 数据包丢失
2. 确认应答丢失
问:RTT和RTO?
RTT:就是数据从网络一段传送到另一端所需的时间,也就是包的往返时间。
RTO:Retransmission Timeout;超时重传时间
RTO设置的不合理?
1. RTO较大:重发就慢,没有效率,性能差
2. RTO较小:会导致可能并没有丢就重发,会增加网络拥塞,导致更多的超时,更多的超时又导致更多的重发。
RTO值需略大于RTT的值
RTT是动态变化的,所以RTO也是动态的
(3)超时重传时间如何选择?
TCP采用了一种自适应算法,记录一个报文段发送的时间,以及收到相应确认的时间,这两个时间之差就是报文段的往返时间RTT。TCP保留了加权平均往返时间RTTs,按照下式进行计算:
新
的
R
T
T
S
=
(
1
−
α
)
∗
(
旧
的
R
T
T
S
)
+
α
∗
(
新
的
R
T
T
)
新的RTT_S=(1-\alpha)*(旧的RTT_S)+\alpha*(新的RTT)
新的RTTS=(1−α)∗(旧的RTTS)+α∗(新的RTT)
RFC 6298推荐alpha=1/8
,且超时重传时间RTO应略大于RTTs,按照下式进行计算:
R
T
O
=
R
T
T
S
+
4
∗
R
T
T
D
RTO=RTT_S+4*RTT_D
RTO=RTTS+4∗RTTD
其中,RTT_D
是RTT的偏差的加权平均值,第一次测量时取为RTT样本值的一般,之后按照下式进行计算:
新
的
R
T
T
D
=
(
1
−
β
)
∗
(
旧
的
R
T
T
D
)
+
β
∗
(
R
T
T
S
−
新
的
R
T
T
)
新的RTT_D=(1-\beta)*(旧的RTT_D)+\beta*(RTT_S-新的RTT)
新的RTTD=(1−β)∗(旧的RTTD)+β∗(RTTS−新的RTT)
RFC 6298推荐beta=1/4
。
但是出现的问题:在发生重传之后,如何判定返回的确认报文段是对先发送的报文段的确认,还是对后来重传的报文段的确认?
想要解决这个问题,可以:报文段每次重传一次,就把超时重传时间RTO增大为原来的2倍,当不再发生报文段的重传时,才根据公式计算超时重传时间。
(4)快速重传
它不以时间为驱动,而是以数据驱动重传
Seq1到,ACK回2
Seq丢失
Seq3到,Ack回2;Seq3、Seq4也是如此
发送端收到了三个Ack=2的确认,知道了Seq2还没有收到,就会在定时器过期之前,重传丢失的Seq2
快速重传机制只解决了一个问题,就是超时时间的问题;
但是它依然面临着另外一个问题,就是重传的时候,是重传一个,还是重传后面的全部;于是有了SACK方法
(5)SACK方法
SACK:Selective Acknowledgment;选择性确认
在TCP头部选项
字段里面加一个SACK,它可以将缓存的地图发送给发送方,这样发送方可以知道哪些数据收到了,哪些没有收到,知道了这些信息,就可以只重传丢失的数据。
(6)Duplicate SACK
其主要使用了SACK来告诉发送方有哪些数据被重复接收了;确认号丢失,或者传输的数据网络延迟
例子:
例子:
D-SACK的几个好处:
1. 可以让「发送⽅」知道,是发出去的包丢了,还是接收⽅回应的 ACK 包丢了;
2. 可以知道是不是「发送⽅」的数据包被⽹络延迟了;
3. 可以知道⽹络中是不是把「发送⽅」的数据包给复制了;
6. 流量控制
(1)TCP为什么需要流量控制?怎么实现?
问:为什么需要?
流量控制是避免发送方的数据填满接收方的缓存。
* 双方在通信的时候,发送方与接收方的速率是不一定相等的,如果发送方的发送速率太快,会导致接收方处理不过来,这时候接收方只能把处理不过来的数据存在缓存区。
* 如果缓存区满了发送方还在不停的发送数据,接收方只能把收到的数据包丢掉,大量的丢包会造成网络资源的浪费。
* 所以TCP引入流量控制机制来控制发送方的速率。
问:怎么实现?如何控制?
TCP通过滑动窗口实现流量控制,即发送方的发送窗口不能超过接收方给出的接收窗口的数值,使得发送方的发送速率不要太快,要让接收方来得及接收。
(2)窗口关闭是什么?有什么风险?
问:什么是窗口关闭?
TCP通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制;
如果窗口大小为0时,就会阻止发送方给接收方传递数据,知道窗口变为非0为止,即窗口关闭。
问:窗口关闭潜在风险?--死锁
窗口关闭后,接收方的接收缓存又有了一些存储空间,于是向发送方返回非零窗口的报文段,但这个报文段在传送过程中丢失了,双方都在等待,形成死锁。
解决方法:
每个TCP连接设置一个持续计时器,发送方只要收到零窗口通知,就启动持续计时器;若持续计时器设置的时间到期,就发送一个零窗口探测(window probe)的报文段,仅携带1字节的数据,而接收方就在确认这个探测报文段时给出了现在的窗口值;如果此时窗口依然是零,那么发送方重置持续计时器。
窗口探测是次数一般为3次,每次大约30-60秒,如果3次过后接收窗口还是0,有的TCP就会发送RST报文来中断连接。
(3)糊涂窗口综合症
如果接收方太忙了,来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小。
到最后,如果接收⽅腾出⼏个字节并告诉发送⽅现在有⼏个字节的窗⼝,⽽发送⽅会义⽆反顾地发送这⼏个字节, 这就是糊涂窗⼝综合症。
TCP+IP头有40字节,为了传输几字节的数据,要搭上这么大的开销,不合理。
问:怎么让接受方不通告小窗口呢?
当窗口大小小于一定值时(min(MSS, 缓存空间/2)),就会向发送方通告窗口为0,阻止发送方再发送数据
问:怎么让发送方避免发送小数据?
使⽤Nagle算法,该算法的思路是延时处理,它满⾜以下两个条件中的⼀条才可以发送数据:
1. 要等到窗⼝⼤⼩ >= MSS 或是 数据⼤⼩ >= MSS
2. 收到之前发送数据的ack回包
(4)滑动窗口是什么?
引入窗口原因?
TCP每发送一个数据,都要进行一次确认应答,当上一个数据包收到了应答,再发送下一个。这样效率很低。
//缺点:数据包的往返时间越长,通信的效率就越低。
于是引入窗口的概念,实现累积确认。
//窗口大小:指无需等待确认应答,而可以继续发送数据的最大值。
//窗口:实际上是操作系统开辟的一个缓存空间
问:谈谈你对滑动窗口的了解?
TCP利用滑动窗口(Sliding window)实现流量控制。
早期的网络通信中,通信双方不会考虑网络的拥挤情况直接发送数据,由于大家不知道网络拥塞状况,同时发送数据,导致中间节点阻塞掉包,谁也发不了数据,所以就有了滑动窗口机制来解决此问题。
TCP中采用滑动窗口进行控制传输,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过窗口的大小来确定发送多少字节的数据。
当当滑动窗口为 0 时,发送方一般不能再发送数据报,但有两种情况除外,一种情况是可以发送紧急数据,例如,允许用户终止在远端机上的运行进程。另一种情况是发送方可以发送一个 1 字节的数据报来通知接收方重新声明它希望接收的下一字节及发送方的滑动窗口大小。
(5)累积确认
问:为什么TCP接收方必须有累积确认的功能?
答:累计确认的功能可以减少传输开销,接收方可以在合适的时候发送确认,接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,这反而浪费了网络资源;
TCP标准规定,确认推迟的时间不应超过0.5秒,若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认。
(6)窗口的大小由哪一方决定?
通常窗口的大小由接收方的窗口大小来决定
TCP头部有一个Window
字段,这个字段是接收端告诉发送端自己还有多少个缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。
(7)发送方的滑动窗口swnd
程序如何表示发送方的四个部分呢?
可用窗口大小 = SND.WND - (SND.NXT - SND.UNA)
(8)接收方的滑动窗口rwnd
(9)关于窗口的一些问题
问:接收方和发送方窗口是否相等呢?
答:并不完全相等,约等于
因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样接收窗口可以很快的就空缺出来。
那么新的接收窗口大小,是通过TCP报文中的Window字段来告诉发送方。
那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。
问:接收窗口固定吗?
答:不固定,根据某种算法动态调整
问:接收方窗口越大越好吗?
答:不是。
接受窗口如果太小的话,显然这是不行的,这会严重浪费链路利用率,增加丢包率。
当接收窗口达到某个值的时候,再增大的话也不怎么会减少丢包率的了,而且还会更加消耗内存。
所以接收窗口的大小必须根据网络环境以及拥塞窗口来动态调整。
7. 拥塞控制
(1)拥塞控制、流量控制的区别?
流量控制是点对点的通信量控制;而拥塞控制是全局的网络流量整体性的控制。
流量控制与接收方的缓存状态相关联;而拥塞控制与网络的拥堵情况相关联。
(2)网络什么时候出现拥塞?
发送方判断网络拥塞的依据是:出现了超时。
`只要发送方没有在规定时间内接收到ACK应答报文`,也就是发生了【超时重传】,就会认为网络出现了拥塞
(3)为什么需要拥塞控制?
答:避免发送方的数据填满整个网络。
在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时TCP就会重传数据,但是一重传就会导致网络的负担更重,导致更大的延迟以及更多的丢包,于是就有了拥塞控制。
为了在发送方调节所要发送数据的量,定义了一个叫做拥塞窗口的概念
(4)拥塞窗口cwnd
拥塞窗口cwnd是发送方维护的一个状态变量,它会根据网络的拥塞程度动态变化
swnd = min(cwnd, rwnd)
只要网络中没有出现拥塞,cwnd就会增大
一旦网络出现了拥塞,cwnd就减小
(5)谈一谈你对拥塞控制的了解?如何实现拥塞控制?
拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致于过载。
TCP进行拥塞控制的算法共有四种:`慢启动、拥塞避免、快重传、快恢复。`
//发送方维持拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就可以再增大一些,以便发送更多的分组,从而提高网络利用率;但是只要网络出现拥塞或可能出现拥塞,则必须把拥塞窗口减小,以便缓解网络拥塞。
拥塞算法:
1. 慢启动
最开始发送方的拥塞窗口为1,由小到大逐渐增大发送窗口和拥塞窗口。每经过一个往返RTT,拥塞窗口cwnd加倍。当cwnd超过慢启动门限ssthresh(slow start threshold,一般为65535字节),则使用拥塞避免算法,避免cwnd增长过大。
2. 拥塞避免
每经过一个往返时间RTT,cwnd就增加1。
3. 拥塞发生
* 若重传计时器超时,则超时重传:ssthresh设为cwnd/2,cwnd重置为1,之后重新开始慢启动。
* 若当发送方连续收到三个重复ACK确认应答的时候,则快速重传:cwnd = cwnd/2,ssthresh = cwnd,进入快速恢复算法
4. 快速恢复
快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到3个重复ACK确认应答说明网络没那么糟糕,所以没有必要像RTO超时那么强烈。
快速恢复算法如下:
* cwnd = ssthresh + 3
* 重传丢失的数据包
* 如果再收到重复的ACK,那么cnwd+1
* 如果收到新数据的ACK后,把cwnd设置为第⼀步中的ssthresh的值,原因是该ACK确认了新的数据,说明从duplicated ACK时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进⼊拥塞避免状态;
8. Socket编程
(1)使用Socket进行网络编程的一般步骤有哪些?
- TCP:
- 服务端:socket -> bind -> listen -> accept -> read/write -> close
- 客户端:socket -> connect -> write/read -> close
- UDP:
- 服务端:socket -> bind -> read/write -> close
- 客户端:socket -> write/read -> close
(2)基于TCP的socket编程
1. 服务端和客户端初始化socket,得到文件描述符。 socket()函数
2. 服务端调用bind,绑定IP地址和端口。 bind()函数
3. 服务端调用listen,进行监听 listen()函数
4. 服务端调用accept,等待客户端连接 accept()函数
5. 客户端调用connect,向服务器端的地址和端口发起连接请求
6. 服务端accept返回用于传输的socket的文件描述符
7. 客户端调用write写入数据;服务端调用read读取数据
8. 客户端断开连接时,会调用close,那么服务端read读取数据的时候,就会读取到EOF,待处理完数据后,服务端调用close,表示连接关闭。
这里需要注意的是:服务端调用accept时,连接成功了会返回一个已完成连接的socket,后续用来传输数据。
(3)基于UDP的socket编程
服务器流程:
1. 建立套接字文件描述符,socket()函数
2. 服务端调用bind,绑定IP地址和端口。
3. 接收客户端的数据, recvfrom()函数
4. 向客户端发送数据,sendto()函数
5. 关闭套接字,释放资源。close()函数
客户端流程:
1. 建立套接字文件描述符。socket()函数
2. 设置服务器地址和端口,struct sockaddr
3. 向服务器发送数据 sendto()
4. 接收服务器数据 recvfrom()
5. 关闭套接字 close()
(3)accept发生在三次握手的哪一步?
客户端connect成功返回是在第二次握手
服务端accept成功返回是在第三次握手成功后
(4)客户端调用close了,连接断开的流程是什么?
1. 客户端调用close,表明客户端没有数据需要发送了,则此时会想服务器发送FIN报文,进入FIN_WAIT_1状态
2. 服务端接收到了FIN报文,TCP协议栈会为FIN包插入一个文件结束符EOF到接收缓冲区,应用程序可以通过read调用来感知这个FIN包。这个EOF会被放在已排队等候的其他已接收的数据之后,这就意味着服务端需要处理这种异常情况,因为EOF表示在该连接上再无数据到达。此时,服务器进入CLOSE_WAIT状态。
3. 接着,当处理完数据后,自然就会读到EOF,于是也调用close关闭它的套接字,这会使得服务端会发出一个FIN包,之后处于LSAT_ACK状态。
4. 客户端接收到服务端的FIN包,并发送ACK确认包给服务端,此时客户端将进入TIME_WAIT状态;
5. 服务端接收到ACK确认包后,就进入了最后的CLOSE状态
6. 客户端经过2MSL时间之后,也进入CLOSE状态
(5)请你介绍一下UDP的connect函数
(6)请描述一下Socket编程的send()、recv()、accept()函数
send()函数用来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户端发送应答。send的作用是将要发送的数据拷贝到缓冲区,协议负责传输。
recv函数用来从TCP连接的另一端接收数据,当应用程序调用recv函数时,recv先等待send的发送缓冲中的数据被协议传送完毕,然后从缓冲区中读取接收到的内容给应用层。
accept函数用来接收一个连接,内核维护了半连接队列和一个已完成连接队列,当队列为空的时候,accept阻塞,不为空的时候accept函数从上边取下来一个已完成连接,返回一个文件描述符