计算机网络八股
公钥和个人等信息,经过Hash算法加密,形成消息摘要;将消息摘要拿到拥有公信力的认证中心(CA),用它的私钥对消息摘要加密,形成数字签名。公钥和个人信息、数字签名共同构成数字证书。为什么不能是两次?防止已失效的连接请求报文段突然又传到服务端,因而产生错误客户端发送出去的第一个连接请求报文段并没有丢失,而是因为某些未知原因在某个网络节点上发生滞留,导致延迟到连接释放以后的某个时间点才到达服务端。
1.TCP和UDP的区别
- TCP是面向连接的,UDP是面向无连接的;
- TCP只能一对一通信,UDP支持一对一,一对多,多对一和多对多交互通信;
- TCP是面向字节流的,UDP是面向报文的;
- TCP是可靠传输,使用流量控制和拥塞控制;UDP是不可靠传输
- TCP首部最小20字节,最大60字节;UDP首部仅8字节。
2.ISO七层模型及相关协议
- 物理层:建立、维护、断开物理连接。
- 数据链路层:在物理层提供比特流服务的基础上,建立相邻结点之间的数据链路。
- 网络层:进行逻辑地址寻址,实现不同网络之间的路径选择,协议有ICMP、IGMP、IP等。
- 传输层:定义传输数据的协议端口号,以及流量控制和差错校验,协议有TCP、UDP。
- 会话层:建立、管理、终止会话,指本地主机与远程主机正在进行的会话。
- 表示层:确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取。
- 应用层:网络服务与最终用户的一个接口,常见的协议有:HTTP、FTP、SMTP、DNS。
TCP/IP 四层模型
- 网络接口层
- 网际层
- 传输层
- 应用层
五层体系结构
- 物理层
- 数据链路层
- 网络层:IP网际协议、ARP地址转换协议、RIP路由信息协议
- 传输层:TCP传输控制协议、UDP用户数据报文协议
- 应用层:HTTP超文本传输协议、FTP文本传输协议、DNS域名系统
3.如何理解HTTP协议是无状态的
当浏览器第一次发送请求给服务器时,服务器响应了;
如果同个浏览器发起第二次请求给服务器时,它还是会响应。但是呢,服务器不知道你就是刚才的那个浏览器。
简而言之,服务器不会去记住你是谁,所以是无状态协议。
4.简述从浏览器地址栏输入url到显示主页的过程
- DNS解析,查找域名对应的IP地址。
- 与服务器通过三次握手,建立TCP连接。
- 向服务器发送HTTP请求。
- 服务器处理请求,返回网页内容。
- 浏览器解析并渲染页面。
- TCP四次握手,连接结束。
5.说下 HTTP/1.0,1.1,2.0 的区别
HTTP/1.0
默认使用短连接,每次请求都需要建立一个TCP连接。它可以设置Connection: keep-alive
这个字段,强制开启长连接。
HTTP/1.1
- 默认使用长连接,即TCP连接默认不关闭,可以被多个请求复用。
- 分块传输编码,即服务端每产生一块数据,就发送一块,用“流模式”取代“缓存模式”。
- 管道机制,即在同一个TCP连接里面,客户端可以同时发送多个请求。
HTTP/2.0
-
二进制协议,1.1版本的头信息是文本(ASCII编码),数据体可以是文本或二进制;2.0中,头信息和数据体都是二进制。
-
完全多路复用,在一个连接里,客户端和服务器都可以同时发送多个请求或响应,而且不用按照顺序一一对应。
-
报头压缩,HTTP协议不带有状态,每次请求都必须带上所有信息。HTTP/2.0引入了头信息压缩机制,使用gzip或compress压缩后再发送。
-
服务端推送,允许服务器未经请求,主动向客户端发送资源。
6.POST和GET有哪些区别
7.HTTP 如何实现长连接?在什么时候会超时?
什么是HTTP的长连接?
- HTTP分为长连接和短连接,本质上说的是TCP的长短连接。TCP连接是一个双向的通道,它是可以保持一段时间不关闭的,因此TCP连接才具有真正的长连接和短连接这一说法。
- TCP长连接可以复用一个TCP连接,来发起多次HTTP请求,这样就可以减少资源消耗,比如一次HTML请求,如果是短连接的话,可能还需要请求后续的JS/CSS。
如何设置长连接?
通过在请求头和响应头设置Connection字段指定为keep-alive
,HTTP/1.0协议支持,但默认是关闭的,从HTTP/1.1以后,连接默认都是长连接。
在什么时候会超时?
- HTTP一般会有httpd守护进程,里面可以设置keep-alive timeout,当tcp连接闲置超过这个时间就会关闭,也可以在HTTP的header里面设置超时时间。
- TCP 的keep-alive包含三个参数,支持在系统内核的net.ipv4里面设置;当 TCP 连接之后,闲置了tcp_keepalive_time,则会发生侦测包,如果没有收到对方的ACK,那么会每隔 tcp_keepalive_intvl 再发一次,直到发送了tcp_keepalive_probes,就会丢弃该连接。
tcp_keepalive_intvl = 15
tcp_keepalive_probes = 5
tcp_keepalive_time = 1800
8.HTTP 与 HTTPS 的区别
HTTP 即超文本传输协议,是一个基于TCP/IP通信协议来传递明文数据的协议。HTTP会存在这几个问题:
- 请求信息是明文传输,容易被窃听截取。
- 没有验证对方身份,存在被冒充的风险。
- 数据的完整性未校验,容易被中间人篡改。
为了解决HTTP存在的问题,HTTPS出现啦。
HTTPS是什么?
HTTPS= HTTP+SSL/TLS,可以理解为 HTTPS 是身披 SSL(Secure Socket Layer,安全套接层)的HTTP。
它们的主要区别如下:
9.HTTPS的工作流程是怎样的?
- HTTPS = HTTP + SSL/TLS,也就是用SSL/TLS对数据进行加密和解密,用HTTP进行传输。
- SSL,即Secure Sockets Layer(安全套接层协议),是网络通信提供安全及数据完整性的一种安全协议。
- TLS,即Transport Layer Security(安全传输层协议),它是SSL3.0的后续版本。
- 客户端发起HTTPS请求,连接到服务器的443端口。
- 服务器必须要有一套数字证书(证书内容有公钥、证书颁发机构、失效日期等)。
- 服务器将自己的数字证书发送给客户端(公钥在证书里面,私钥由服务器持有)。
- 客户端收到数字证书之后,会验证证书的合法性。如果证书验证通过,就会生成一个随机的对称密钥,用证书的公钥加密。
- 客户端将公钥加密后的密钥发送到服务器。
- 服务器接收到客户端发来的密文密钥之后,用自己之前保留的私钥对其进行非对称解密,解密之后就得到客户端的密钥,然后用客户端密钥对返回数据进行对称加密,这样子传输的数据都是密文啦。
- 服务器将加密后的密文返回到客户端。
- 客户端收到后,用自己的密钥对其进行对称解密,就能得到服务器返回的数据。
10.说说HTTP的状态码,301和302的区别?
- 301:永久重定向,表示所请求的资源已经永久地转移到新的位置,这包含域名的改变或者是资源路径的改变。
- 302:临时重定向,表示所请求的资源临时地转移到新的位置,一般是24到48小时以内的转移会用到302。
11.说说什么是数字签名?什么是数字证书?
数字证书构成:
- 公钥和个人等信息,经过Hash算法加密,形成消息摘要;将消息摘要拿到拥有公信力的认证中心(CA),用它的私钥对消息摘要加密,形成数字签名。
- 公钥和个人信息、数字签名共同构成数字证书。
12.对称加密和非对称加密有什么区别
对称加密:指加密和解密使用同一密钥,优点是运算速度较快,缺点是不能安全地将密钥传输给另一方。常见的对称加密算法有:DES、AES等。
非对称加密:指的是加密和解密使用不同的密钥(即公钥和私钥)。公钥与私钥是成对存在的,如果用公钥对数据进行加密,只有对应的私钥才能解密。常见的非对称加密算法有 RSA。
13.说说 DNS 的解析过程?
DNS的解析过程如下图:
假设你要查询www.baidu.com的IP地址:浏览器 -> 本地DNS服务器 -> 根域名服务器 -> 顶级域名服务器 -> 权威域名服务器
- 首先会查找浏览器的缓存,看看是否能找到www.baidu.com对应的IP地址,找到就直接返回;否则进行下一步。
- 将请求发往本地DNS服务器,如果查找到也直接返回,否则继续进行下一步;
- 本地DNS服务器向根域名服务器发送请求,根域名服务器返回负责
.com
的顶级域名服务器的列表。 - 本地DNS服务器再向其中一个顶级域名服务器发送一个请求,返回负责
.baidu
的权威域名服务器的列表。 - 本地DNS服务器再向其中一个权威域名服务器发送一个请求,返回www.baidu.com所对应的IP地址。
14.说说 WebSocket与socket的区别
Socket是一套标准,它完成了对TCP/IP的高度封装,屏蔽网络细节,以便开发者更好地进行网络编程。
Socket等于IP地址 + 端口 + 协议。
WebSocket是一个持久化的协议,它是伴随H5而出的协议,用来解决HTTP不支持持久化连接的问题。
Socket是一个网络编程的标准接口,而WebSocket则是应用层通信协议。
15.HTTP请求的过程与原理
HTTP是一个基于TCP/IP协议来传递数据的超文本传输协议,传输的数据类型有HTML、图片等。
- 客户端进行DNS域名解析,得到对应的IP地址
- 根据这个IP地址,找到对应的服务器建立TCP连接(三次握手)
- 建立TCP连接后发起HTTP请求(一个完整的http请求报文)
- 服务器响应HTTP请求,客户端得到html代码
- 客户端解析html代码,用html代码中的资源(如 js、css、图片等等)渲染页面。
- 服务器关闭TCP连接(四次挥手)
16.forward和redirect的区别?
是servlet中的两种主要跳转方式。forward:转发,redirect:重定向
- 从地址栏显示来说
forward是服务器内部的重定向,服务器直接访问目标地址,把里面的东西取出来,但是客户端并不知道,因此用forward的话,客户端浏览器的网址是不会发生变化的。
redirect是服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,所以地址栏显示的是新地址。
- 从数据共享来说
由于在整个转发的过程中使用的是同一个request,因此forward会将request信息带到被重定向的jsp或servlet中使用,即可以共享数据。
redirect不能共享数据。
- 从运用的地方来说
forward一般用于用户登录时,根据角色转发到相应的模块
redirect一般用于用户注销登录时返回主页面
- 从本质上来说
forward转发是服务器上的行为,redirect重定向是客户端的行为。
- 从效率上来说
forword效率高,而redirect效率低。
- 从请求的次数来说
forword只有一次请求,而redirect有两次请求。
17.Session和Cookie的区别
Cookie 是保存在客户端的一小块文本串的数据。客户端向服务器发起请求时,服务器会向客户端发送一个 Cookie,客户端就把 Cookie 保存起来。下次向同一服务器再发起请求时,Cookie 就被携带发送到服务器。服务器可以根据这个 Cookie 判断用户的身份和状态。
Session 指的是服务器和客户端一次会话的过程。它是另一种记录客户端状态的机制。不同的是 Cookie 是保存在客户端浏览器中的,而 Session 是保存在服务器上的。客户端浏览器在访问服务器时,服务器会把客户端信息以某种形式记录在服务器上,这就是 Session。客户端浏览器再次访问时只需要从该 Session 中查找用户的状态。
Session 和 Cookie 到底有什么不同呢?
Session 和 Cookie 有什么关联呢?
可以使用 Cookie 记录 Session 的唯一标识
- 用户第一次请求服务器时,服务器根据用户提交的信息,创建对应的Session,请求返回时将此Session的唯一标识信息SessionID返回给浏览器,浏览器会将此SessionID信息存入Cookie中,同时Cookie记录此SessionID是属于哪个域名的。
- 当用户第二次访问服务器时,请求会自动判断此域名下是否存在Cookie信息,如果存在,则自动将Cookie信息也发送给服务器,服务器会从Cookie中获取SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到则说明用户没有登录或者登录失效,如果找到则证明用户已经登录可执行后面的操作。
分布式环境下 Session 该怎么处理呢?
分布式环境下,客户端请求经过负载均衡,可能会分配到不同的服务器上,假如一个用户的请求两次没有落到同一台服务器上,那么在新的服务器上就没有记录该用户状态对应的 Session。
可以使用 Redis 等分布式缓存来存储 Session,保证在多台服务器间共享。
客户端如果无法使用 Cookie 怎么办呢?
有可能客户端无法使用 Cookie,比如浏览器禁用 Cookie,或者客户端是 安卓、IOS 设备等。
这时候怎么办呢?SessionID 怎么存呢?怎么传给服务器呢?
首先是 SessionID 的存储,可以使用客户端的本地存储,比如浏览器的 sessionStorage
。
接下来要怎么传呢?
- 拼接到 URL:直接把 SessionID 作为 URL 的请求参数。
- 放到请求头:把 SessionID 放到请求头里面,比较常用。
18.详细说一下 TCP 的三次握手机制
TCP 三次握手过程:
- 最开始,客户端和服务端都处于 CLOSE(关闭)状态,服务端监听客户端的请求,进入 LISTEN(监听) 状态。
- 客户端发送连接请求,进行第一次握手(同步位 SYN=1,序号字段 seq=x),发送完毕后,客户端就进入 SYN_SENT(同步已发送) 状态。
- 服务端确认连接,进行第二次握手(同步位 SYN=1,确认位 ACK=1,序号字段 seq=y,确认号字段 ack=x+1), 发送完毕后,服务端就进入 SYN_RCV(同步已接收) 状态。
- 客户端收到服务端的确认后,再次向服务端确认,进行第三次握手(确认位 ACK=1,确认号字段 ack=y+1),发送完毕后,客户端就进入 ESTABLISHED(连接已建立) 状态,当服务端接收到这个包时,也进入 ESTABLISHED(连接已建立) 状态。
19.TCP 握手为什么是三次,为什么不能是两次?不能是四次?
为什么不能是两次?
防止已失效的连接请求报文段突然又传到服务端,因而产生错误
- 客户端发送出去的第一个连接请求报文段并没有丢失,而是因为某些未知原因在某个网络节点上发生滞留,导致延迟到连接释放以后的某个时间点才到达服务端。
- 本来这是一个早已失效的报文段,但是服务端收到此失效的报文段后,会误认为这是客户端再次发起的一个新的连接请求,于是服务端向客户端又发出确认报文,表示同意建立连接。
- 如果不采用 “三次握手”,那么只要服务端发出确认报文后就会认为新的连接已经建立了,但是客户端并没有发出建立连接的请求,因此不会向服务端发送数据,服务端没有收到数据就会一直等待,这样服务端就会白白浪费掉很多资源。
所以我们需要 “第三次握手” 来确认这个过程:
- 通过第三次握手的数据来告诉服务端,客户端有没有收到服务端 “第二次握手” 时传过去的数据,以及这个连接的序号是不是有效的。
- 若发送的这个数据是 “收到且没有问题” 的信息,服务端接收后就可以正常建立 TCP 连接,否则建立 TCP 连接失败,服务器关闭连接端口。由此减少服务器开销和接收到失效请求时发生的错误。
为什么不是四次?
简单来说,就是三次握手已经足够创建可靠的连接,没有必要再多一次握手导致花费更多的时间在建立连接上。
20.三次握手中每一次没收到报文会发生什么情况?
第一次握手服务端未收到
SYN
报文
服务端不会进行任何的动作,而客户端由于一段时间内没有收到服务端发来的确认报文,等待一段时间后会重新发送 SYN 报文,
如果仍然没有回应,会重复这个过程,直到发送次数超过最大重传次数,就会返回连接建立失败。
第二次握手客户端未收到服务端响应的
ACK
报文
因为第二次握手是包含对客户端第一次握手的 ACK 确认报文,所以如果客户端迟迟没有收到第二次握手,那么客户端就会觉得可能是自己的 SYN 报文(第一次握手)丢失了,于是客户端就会触发超时重传机制,重传 SYN 报文。
然后,因为第二次握手是包含服务端的 SYN 报文,所以当客户端收到后,需要给服务端发送 ACK 确认报文(第三次握手),服务端才会认为该 SYN 报文被客户端收到了。
那么,如果第二次握手丢失了,服务端就收不到第三次握手,于是服务端这边会触发超时重传机制,重传 SYN-ACK
报文。
第三次握手服务端未收到客户端发送过来的
ACK
报文
客户端收到服务端的 SYN-ACK
报文后,就会给服务端发送一个 ACK 报文,也就是第三次握手,此时客户端进入到 ESTABLISH(连接已建立) 状态。
因为这个第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN-ACK
报文,直到收到第三次握手,或者达到最大重传次数。
21.第二次握手传回了 ACK,为什么还要传回 SYN?
ACK
是为了告诉客户端传来的数据已经接收无误。
而传回 SYN
是为了告诉客户端,服务端响应的确实是客户端发送的报文。
22.第三次握手可以携带数据吗?
第三次握手是可以携带数据的。
此时客户端已经处于连接已建立
状态。对于客户端来说,它已经建立连接成功了,并且确认服务端的接收和发送能力是正常的。
第一次握手不能携带数据是出于安全的考虑,因为如果允许携带数据,攻击者每次在 SYN
报文中携带大量数据,就会导致服务端消耗更多的时间和空间去处理这些报文,会造成CPU和内存的消耗。
23.说说 TCP 四次挥手的过程?
- 数据传输结束后,通信双方都可以主动发起断开连接请求,这里假定客户端发起。
- 客户端发送释放连接报文,进行第一次挥手(FIN=1,ACK=1,seq=u,ack=v),发送完毕后,客户端进入 FIN_WAIT_1(终止等待1) 状态。
- 服务端发送确认报文,进行第二次挥手(ACK=1,seq =v,ack=u+1),发送完毕后,服务端进入 CLOSE_WAIT(关闭等待) 状态,客户端收到这个确认包后,进入 FIN_WAIT_2(终止等待2) 状态。
- 服务端发送释放连接报文,进行第三次挥手(FIN=1,ACK1,seq=w,ack=u+1),发送完毕后,服务端进入LAST_ACK(最后确认) 状态,等待来自客户端的最后一个 ACK 报文。
- 客户端发送确认报文,进行第四次挥手(ACK=1,seq=u+1,ack=w+1),客户端收到来自服务端的关闭请求,发送一个确认包,并进入 TIME_WAIT(时间等待) 状态,服务端接收到这个确认包后,关闭连接,进入 CLOSED(关闭) 状态。
- 客户端再经过
2MSL
后,也进入 CLOSED(关闭) 状态。
客户端在发送完最后一个确认报文后,为什么不直接进入关闭状态 ? 而是要进入时间等待状态,2MSL 后才进入关闭状态,这是否有必要呢 ?
服务端发送TCP连接释放报文段后进入最后确认状态。
客户端收到该报文段后,发送普通的TCP确认报文段,并进入关闭状态而不是时间等待状态。然而,该TCP确认报文段丢失了。
这必然会造成服务端对之前所发送的TCP连接释放报文段的超时重传,并仍处于最后确认状态。重传的TCP连接释放报文段到达客户端,由于客户端处于关闭状态,因此不理睬该报文段,这必然会造成服务端反复重传TCP连接释放报文段,并一直处于最后确认状态而无法进入关闭状态。
因此时间等待状态以及处于该状态2MSL
时长,可以确保服务端可以收到最后一个TCP确认报文段而进入关闭状态。
另外,客户端在发送完最后一个TCP确认报文段后,再经过2MSL
时长,就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的TCP连接中,不会出现旧连接中的报文段。
为什么等待时间是
2MSL
?
MSL 是报⽂最⼤⽣存时间,它是任何报⽂在⽹络上存在的最⻓时间,超过这个时间报⽂将被丢弃。
TIME_WAIT 等待 2 倍的 MSL,是因为⽹络中可能存在来⾃发送⽅的数据包,当这些发送⽅的数据包被接收⽅处理后⼜会向对⽅发送响应,所以⼀来⼀回需要等待 2 倍的时间。
⽐如服务端如果没有收到客户端发送的TCP确认报文段,就会触发超时重传,重新发送TCP连接释放报文段,客户端收到后,会重发TCP确认报文段给服务端, ⼀来⼀去正好 2 个 MSL。
24.TCP 挥手为什么需要四次呢?
再来回顾下四次挥手双方发
FIN
包的过程,就能理解为什么需要四次了。
关闭连接时,客户端向服务端发送 FIN
报文,仅仅表示客户端不再发送数据了但是还能接收数据。
服务端收到客户端的 FIN
报文后,先返回一个 ACK
确认报文;而服务端可能还有数据需要处理和发送,等服务端不再发送数据了,再发送 FIN
报文给客户端来表示同意现在关闭连接。
从上面的过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK
和 FIN
一般都会分开发送,从而导致比三次握手多了一次。
25.TCP 保活计时器有什么用?
除了时间等待计时器外,TCP 还有一个保活计时器(keepalive timer)。
设想这样的场景:
TCP 双方已经建立了连接,后来,客户端的主机突然出现了故障。显然,服务端以后就不能再收到客户端发来的数据。因此,应当有措施使服务端不要再白白等待下去。这就需要使用保活计时器了。
服务端每收到一次客户端的数据,就重新设置并启动保活计时器(2小时定时)。若定时周期内都没有收到客户端发来的数据,服务端就发送一个探测报文段,以后每隔 75 秒钟发送一次。若连续发送 10 个探测报文段后仍然无客户端的响应,服务端就认为客户端出了故障,接着就关闭这个连接。
26.CLOSE-WAIT 的状态和意义?
服务端收到客户端关闭连接的请求并确认之后,就会进入 CLOSE-WAIT
状态。
此时服务端可能还有一些数据没有传输完成,因此不能立即关闭连接,而 CLOSE-WAIT
状态就是为了保证服务端在关闭连接之前将待发送的数据处理完。
27.说说 TCP 报文首部有哪些字段?
-
16位端口号:源端口号,标识发送该 TCP 报文段的应用进程;目的端口号,标识接收该 TCP 报文段的应用进程。
-
32位序号:指出本 TCP 报文段数据载荷的第一个字节的序号。
-
32位确认号:指出期望收到对方下一个 TCP 报文段的数据载荷的第一个字节的序号,同时也是对之前收到的所有数据的确认。若确认号 = n,则表明到序号 n-1 为止的所有数据都已正确接收,期望收到序号为 n 的数据。
-
4位头部长度:指出 TCP 报文段的首部长度。
-
6位标志位:
- 确认标志位ACK:取值为1时确认号字段才有效,取值为0时确认号字段无效。TCP 规定,在连接建立后所有传送的 TCP 报文段都必须把 ACK 置为1。
- 同步标志位SYN:在 TCP 连接建立时用来同步序号。
- 终止标志位FIN:用来释放 TCP 连接。
-
16位窗口大小:
- 指出发送本报文段的一方的接收窗口。
- 窗口值作为接收方让发送方设置其发送窗口的依据。
- 这是以接收方的接收能力来控制发送方的发送能力,称为流量控制。
- 发送窗口的大小还取决于拥塞窗口的大小,也就是应该从接收窗口和拥塞窗口中取小者。
-
16位校验和:用来检查整个 TCP 报文段在传输过程中是否出现了误码。
-
16位紧急指针:当发送方有紧急数据时,可将紧急数据插队到发送缓存的最前面,并立刻封装到一个 TCP 报文段中进行发送。紧急指针会指出本报文段的数据载荷部分包含了多长的紧急数据,紧急数据之后是普通数据。
28.TCP 是如何保证可靠性的?
TCP主要提供了 连接管理、校验和、序列号/确认应答、流量控制、最大消息长度、超时重传、拥塞控制等方式实现了可靠传输。
-
连接管理:TCP 使用三次握手和四次挥手来保证可靠地建立连接和释放连接。
-
校验和:用来检查整个 TCP 报文段在传输过程中是否出现了误码。
-
序列号/确认应答:TCP 会给发送的每一个包进行编号,接收方会对收到的包进行应答,发送方就会知道接收方是否收到对应的包,如果发现没有收到,就会重发,这样就能保证数据的完整性了。
-
流量控制:TCP 连接的每一方都有固定大小的缓冲空间,TCP 的接收端只允许发送端发送接收端缓冲区能接纳的数据大小。当接收方来不及处理发送方的数据时,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。(TCP 利用滑动窗口实现流量控制)
-
最大消息长度:在建立 TCP 连接的时候,双方约定一个最大的长度(MSS)作为发送的单位,重传的时候也是以这个单位来进行重传的。理想情况下是该长度的数据刚好不被网络层分块。
-
超时重传:超时重传是指发送出去的数据包到接收到确认包之间的时间,如果超过了这个时间,就会被认为是丢包了,需要重传。
-
拥塞控制:如果网络非常拥堵,此时再发送数据就会加重网络负担,那么发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题。为此 TCP 引入了慢启动机制,先发出少量数据,就像探路一样,先摸清当前的网络拥堵状态后,再决定按照多大的速度传送数据。
29.说说 TCP 的流量控制?
TCP 提供了一种机制,可以让发送方根据接收方的实际接收能力控制发送的数据量,这就是流量控制。
TCP 通过「滑动窗口」来实现流量控制
- 首先 TCP 双方进行三次握手,初始化各自的窗口大小,均为 400 字节。
- 假如当前发送方给接收方发送了 200 字节,那么,发送方的
SND.NXT
会右移 200 字节,也就是说当前的可用窗口减少了 200 字节。 - 接收方收到后,放到缓冲队列里面,
REV.WND
= 400-200=200 字节,所以 win=200 字节返回给发送方。接收方会在 ACK 的报文首部带上缩小后的滑动窗口 200 字节 - 发送方又发送 200 字节过来,200 字节到达,继续放到缓冲队列里面。不过这时候,由于大量负载的原因,接收方处理不了这么多字节,只能处理 100 字节,剩余的 100 字节继续放到缓冲队列里面。这时候,
REV.WND
= 400-200-100=100 字节,即 win=100 字节返回给发送方。 - 发送方继续发送 100 字节过来,这时候,接收窗口 win 变为 0。
- 发送方停止发送,开启一个定时任务,每隔一段时间,就去询问接收方,直到 win 大于 0,才开始继续发送。
30.详细说说 TCP 的滑动窗口?
TCP 发送一个数据,如果需要收到确认应答才会发送下一个数据。这样的话就会有个缺点:效率会比较低。
为了解决这个问题,TCP 引入了滑动窗口,它是操作系统开辟的一个缓存空间。窗口大小表示无需等待确认应答而可以继续发送数据的最大值。
TCP 头部有个 16 位的窗口大小,它告诉对方本端的 TCP 接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度,从而达到流量控制的目的。
通俗点讲,就是接收方每次收到数据包,在发送确认报文的时候,同时告诉发送方,自己的接收缓冲区还有多少空闲空间,缓冲区的空闲空间,我们就称之为接收窗口大小。
TCP 滑动窗口分为两种: 发送窗口和接收窗口。
发送方的滑动窗口包含四个部分:
- 已发送且已收到 ACK 确认
- 已发送但未收到 ACK 确认
- 未发送但可以发送
- 未发送且不可发送
- 虚线矩形框,就是发送窗口。
SND.WND
:表示发送窗口的大小,上图虚线框的格子数是 14 个,即发送窗口大小是 14。SND.NXT
:下一个发送的位置,它指向未发送但可以发送的第一个字节的序列号。SND.UNA
:一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号。
接收方的滑动窗口包含三个部分:
- 已成功接收并确认
- 未收到数据但可以接收
- 未收到数据且不可以接收的数据
- 虚线矩形框,就是接收窗口。
REV.WND
:表示接收窗口的大小,上图虚线框的格子数就是 9 个,即接收窗口的大小是 9。REV.NXT
:下一个接收的位置,它指向未收到但可以接收的第一个字节的序列号。
31.说说 TCP 的拥塞控制?
什么是拥塞控制?不是有了流量控制吗?
前⾯的流量控制是避免发送⽅的数据填满接收⽅的缓存,但是并不知道整个⽹络中发⽣了什么。
⼀般来说,计算机⽹络都处在⼀个共享的环境。因此也有可能会因为其他主机之间的通信使得⽹络出现拥堵。
在⽹络出现拥堵时,如果继续发送⼤量数据包,可能会导致数据包延迟、丢失等,这时 TCP 就会重传数据,但是⼀重传就会导致⽹络的负担更重,于是会导致更⼤的延迟以及更多的丢包,这个情况就会进⼊恶性循环并且被不断地放⼤…
所以,TCP 不能忽略整个网络中发⽣的事,它被设计成⼀个⽆私的协议,当⽹络发送拥塞时,TCP 会⾃我牺牲,降低发送的数据流。
于是,就有了拥塞控制,拥塞控制的⽬的就是为了避免发送⽅的数据填满整个⽹络。
就像是一个水管,不能让太多的水(数据流)流入水管,如果超过水管的承受能力,水管就会被撑爆(丢包)。
发送方维护一个拥塞窗口 cwnd(congestion window) 的变量,调节所要发送数据的量。
什么是拥塞窗⼝?和发送窗⼝有什么关系呢?
拥塞窗⼝ **cwnd **是发送⽅维护的⼀个状态变量,它会根据⽹络的拥塞程度动态变化。
发送窗⼝ swnd 和接收窗⼝ rwnd 是约等于的关系,那么由于加⼊了拥塞窗⼝的概念后,此时发送窗⼝的值 swnd = min(cwnd, rwnd)
,也就是取拥塞窗⼝和接收窗⼝中的最⼩值。
拥塞窗⼝ cwnd 变化的规则:
- 只要⽹络中没有出现拥塞, cwnd 就会增⼤
- 但如果⽹络中出现了拥塞, cwnd 就会减小
拥塞控制有哪些常用算法?
- 慢启动
慢启动算法,慢慢启动。
它表示 TCP 建立连接完成后,一开始不要发送大量的数据,而是先探测一下网络的拥塞程度。由小到大逐渐增加拥塞窗口的大小,如果没有出现丢包,每收到一个 ACK,就将拥塞窗口 cwnd 的大小加 1(单位是 MSS)。每轮次发送窗口增加一倍,呈指数增长,如果出现丢包,拥塞窗口就减半,进入拥塞避免阶段。
举个例子:
- 连接建⽴完成后,⼀开始初始化 cwnd = 1 ,表示可以传⼀个 MSS ⼤⼩的数据。
- 当收到⼀个 ACK 确认应答后,cwnd 增加 1,于是⼀次性能够发送 2 个。
- 当收到 2 个 ACK 确认应答后, cwnd 增加 2,于是就能⽐之前多发送 2 个,所以这⼀次能够发送 4 个。
- 当这 4 个 ACK 确认到来的时候,每个确认 cwnd 增加 1, 4 个确认 cwnd 增加 4,于是就能⽐之前多发送 4 个,所以这⼀次能够发送 8 个。
发送包的个数是呈指数性增⻓的。
为了防止 cwnd 增长过大而引起网络拥塞,还需设置一个慢启动阀值 ssthresh(slow start threshold)的状态变量。当 cwnd
到达该阀值后,就好像水管被关小了水龙头一样,减少了拥塞状态。即当 cwnd > ssthresh
时,进入拥塞避免算法。
- 拥塞避免
一般来说,慢启动阀值 ssthresh
的大小是 65535 字节,cwnd
到达慢启动阀值后
- 每收到一个 ACK 时,
cwnd = cwnd + 1/cwnd
- 当每过一个 RTT 时,
cwnd = cwnd + 1
显然这是一个线性上升的算法,可以避免发送过快导致网络出现拥塞问题。
接着上面慢启动的例子,假定 ssthresh
为 8:
- 当 8 个 ACK 确认应答到来时,每个确认增加
1/8
,8 个 ACK 确认后 cwnd ⼀共增加 1,于是这⼀次能够发送 9 个 MSS ⼤⼩的数据,变成了线性增⻓。
- 拥塞发生
当网络拥塞发生丢包时,会有两种情况:
- RTO 超时重传
- 快速重传
如果是发生了RTO 超时重传,就会使用「拥塞发生」算法
- 慢启动阀值
sshthresh = cwnd/2
- cwnd 重置为 1
- 进入新的慢启动过程
这真的是辛辛苦苦几十年,一朝回到解放前。其实还有更好的处理方式,就是「快速重传」。当发送方收到 3 个连续的重复 ACK 时,就会快速地重传,不必等待 RTO超时 再重传。
发⽣「快速重传」的拥塞发⽣算法:
- 拥塞窗口大小
cwnd = cwnd/2
- 慢启动阀值
ssthresh = cwnd
- 进入快速恢复算法
- 快速恢复
快速重传和快速恢复算法一般是同时使用的。快速恢复算法认为,还能收到 3 个重复的 ACK,说明网络也没有那么糟糕,所以没必要像 RTO超时重传 那样强烈。
正如前面所说的,进入快速恢复之前,cwnd 和 sshthresh 已被更新:
cwnd = cwnd/2
sshthresh = cwnd
然后,真正进入「快速恢复」算法:
cwnd = sshthresh + 3
- 重传重复的那几个 ACK(即丢失的那几个数据包)
- 如果再收到重复的 ACK,那么
cwnd = cwnd +1
- 如果收到新数据的 ACK 后,
cwnd = sshthresh
。因为收到新数据的 ACK,表明恢复过程已经结束,可以再次进入「拥塞避免」算法了。
32.说说 TCP 的重传机制?
重传包括:超时重传、快速重传、带选择确认的重传(SACK)、重复 SACK 四种。
- 超时重传
超时重传,是 TCP 协议保证数据可靠性的另一个重要机制,其原理是在发送某一个数据以后就开启一个重传计时器,在一定时间内如果没有收到发送的数据报的 ACK 报文,那么就重新发送数据,直到收到 ACK 报文为止。
超时时间应该设置为多少合适呢?
RTT 就是数据完全发送完,到收到确认信号的时间,即数据包的一次往返时间。
超时重传时间,就是 RTO(Retransmission Timeout)。那么,RTO 应该设置多大呢?
- 如果 RTO 设置很大,等了很久都没重发,这样肯定不行。
- 如果 RTO 设置很小,那很可能数据都没有丢失,就开始重发了,这将会导致网络阻塞,从而发生恶性循环,导致更多的超时出现。
一般来说,RTO 略微大于 RTT,效果是最佳的。
超时重传并不是十分完美的重传方案,它有这些缺点:
- 当一个报文丢失时,会等待一定的超时周期,才重传分组,增加了端到端的时延。
- 当一个报文丢失时,在其等待超时的过程中,可能会出现这种情况:其后面的报文段已经被接收方接收了但却迟迟得不到确认,发送方会认为其后面的报文段也丢失了,从而引起不必要的重传,既浪费资源也浪费时间。
- 快速重传
快速重传可以用来解决超时重发的时间等待问题。
它不以时间驱动,而是以数据驱动。它是基于接收方的反馈信息来引发重传的。
快速重传的流程如下:
发送方发送了 1,2,3,4,5,6
份数据:
- 第一份 Seq=1 先送到了,于是 ACK 回2;
- 第二份 Seq=2 也送到了,于是 ACK 回3;
- 第三份 Seq=3 由于网络等某些原因,没送到;
- 第四份 Seq=4 送到了,但是由于 Seq=3 没收到。因此 ACK 还是回3;
- 后面的 Seq=5,6 也送到了,ACK 还是回复3,因为 Seq=3 没有收到。
- 发送方连续收到三个重复冗余的 ACK=3 的确认(其实是4个哈,但是因为前面的一个是正常的ACK,后面三个才是重复冗余的),于是知道哪个报文段在传输过程中丢失了;发送方就在重传定时器过期之前,重传该报文段。
- 最后,接收方收到了 Seq=3,此时因为 Seq=4,5,6 都收到了,于是它回 ACK=7。
快速重传机制也有缺点:发送方并不知道到底是哪个报文丢失了,到底该重传多少个数据包?
是只重传 Seq=3 ?还是重传 Seq=3、Seq=4、Seq=5、Seq=6 呢?因为发送方并不清楚这三个连续的 ACK=3 是谁传回来的。
- 带选择确认的重传(SACK)
为了解决应该重传多少个包的问题? TCP 提供了带选择确认的重传(即 SACK,Selective Acknowledgment)。
SACK 机制就是,在快速重传的基础上,接收方返回最近收到报文段的序列号范围,这样发送方就知道接收方哪些数据包是没收到的。这样就很清楚应该重传哪些数据包。
如上图中,发送⽅收到了三次同样的 ACK 确认报⽂,于是就会触发「快速重传」机制,通过 SACK 信息发现只有 200~299
这段数据丢失,则重发时,就只选择了这个 TCP 段进⾏重发。
- 重复 SACK(D-SACK)
D-SACK,英文是 Duplicate SACK,是在 SACK 的基础上做了一些扩展,主要用来告诉发送方,有哪些数据包,自己重复接受了。
D-SACK 的目的是帮助发送方判断,是否发生了包失序、ACK 丢失、包重复或伪重传。让 TCP 可以更好的做网络流控。
例如 ACK 丢包导致的数据包重复:
-
接收⽅发给发送⽅的两个 ACK 确认应答都丢失了,所以发送⽅超时后,重传第⼀个数据包(3000 ~ 3499)
-
接收⽅发现数据是重复收到的,于是回了⼀个 SACK = 3000~3500,告诉「发送⽅」 3000~3500的数据早已被接收了,因为 ACK 都到 4000 了,意味着 4000 之前的所有数据都已经收到了,所以这个 SACK 就代表着
D-SACK
。这样发送⽅就知道了,数据并没有丢,而是接收⽅的 ACK 确认报⽂丢了。
33.说说 TCP 的粘包和拆包?
什么是 TCP 粘包和拆包?
TCP 是面向字节流,没有界限的一串数据。TCP 底层并不了解上层业务数据的具体含义,它会根据 TCP 缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被 TCP 拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包进行发送,这就是所谓的 TCP 粘包和拆包问题。
为什么会产生粘包和拆包呢?
- 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次性发送出去,将会发生粘包;
- 接收方的应用层没有及时读取接收缓冲区的数据,将会发生粘包;
- 要发送的数据大于 TCP 发送缓冲区剩余空间的大小,将会发生拆包;
- 待发送的数据大于 MSS(最大报文长度),TCP 在传输前将会进行拆包。即
TCP报文长度 - TCP头部长度 > MSS
。
解决方案:
- 发送方给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收方在收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。
- 发送方将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收方每次从接收缓冲区中读取固定长度的数据,就自然而然的把每个数据包拆分开来。
- 可以在数据包之间设置边界,如添加特殊符号,这样接收方通过这个边界就可以将不同的数据包拆分开来。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)