计网-TCP
总结计算机网络TCP相关的面试题目和知识点
1、TCP三次握手
握手过程
Tip:第三次握手是可以携带数据的,前两次握手是不可以携带数据的,这也是面试常问的题。
TCP 的连接状态查看,在 Linux 可以通过 netstat -napt
命令查看。
为什么要是三次,2次呢,四次呢
- 三次握手才可以阻止重复历史连接的初始化(主要原因):在两次握手的情况下,服务端没有中间状态给客户端来阻止历史连接,导致服务端可能建立一个历史连接,造成资源浪费
- 三次握手才可以同步双方的初始序列号
- 三次握手才可以避免资源浪费
两次握手不是也可以根据上下文信息丢弃 syn 历史报文吗?
我这里两次握手是假设「由于没有第三次握手,服务端不清楚客户端是否收到了自己发送的建立连接的
ACK
确认报文,所以每收到一个SYN
就只能先主动建立一个连接」这个场景
为什么每次建立 TCP 连接时,初始化的序列号都要求不一样呢?
主要原因有两个方面:
- 为了防止历史报文被下一个相同四元组的连接接收(主要方面);
- 为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收;
IP 层会分片,为什么 TCP 层还需要 MSS 呢?
因为 IP 层本身没有超时重传机制,它由传输层的 TCP 来负责超时和重传
当如果一个 IP 分片丢失,整个 IP 报文的所有分片都得重传
1 |
|
在建立连接的不同阶段握手丢失,会发生什么情况?
阶段 | 后果 |
---|---|
第一次握手丢失 | 超时重传SYN报文,序列号一样,每次超时的时间是上一次的 2 倍,5次后等待32秒,无反应则断开连接 |
第二次握手丢失 | 客户端和服务端都会重传,SYN和SYN-ACK |
第三次握手丢失 | 服务端重传SYN-ACK |
注意,ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文。
如何防范TCP洪泛攻击?
什么是 SYN Flood?
SYN Flood(SYN 洪泛攻击)就是 Client 在短时间内伪造大量不存在的源 IP 地址,并向 Server 不断地发送 SYN 包,Server 一旦接收到 Client 发来的 Syn 报文,就立即为该请求分配一个 TCB(Transmission Control Block),并回复 ACK 确认包,等待 Client 确认。由于源地址不存在,因此 Server 需要不断重发直至超时
这些伪造的 SYN 包将长时间占用「半连接队列」,并请求分配大量 TCB,消耗服务器资源,同时也导致正常的 SYN 请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪
如何防御 SYN Flood?
- 配置防火墙:防火墙可以通过限制每秒钟接收的 SYN 请求数量,或限制源地址的数量来降低攻击的威力。
- 使用反向代理:将流量分配到多个服务器上,通过负载均衡来减轻单台服务器的负担,从而使服务器更难受到攻击。
- 使用 CDN:使用 CDN 可以帮助过滤掉大部分的攻击流量,并将请求转发到最近的可用服务器上。
- 限制连接数:限制每个 IP 地址的连接数量,通过对 TCP 连接进行监控和记录,及时发现异常连接行为,限制同一 IP 地址的连接数量。
- 绕过半连接队列,使用cookies确认验证,
cookies
并不会有一个专门的队列保存,它是通过通信双方的IP地址端口、时间戳、MSS等信息进行实时计算的,保存在TCP报头的seq
里。
2、TCP四次挥手
为什么要是四次,可以变成三次吗?
Q1:
关闭连接时,客户端向服务端发送
FIN
时,仅仅表示客户端不再发送数据了但是还能接收数据。
服务端收到客户端的
FIN
报文时,先回一个ACK
应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN
报文给客户端来表示同意现在关闭连接。
Q2:
可以
次数由服务端的应用程序决定,看是否还有数据要发送
两种关闭函数:close、shutdown
close | 关闭发送方向和读取方向 | socket引用计数-1,其他线程还可以读写,直到计数为0,发出FIN报文 | 粗暴关闭,不会四次 |
---|---|---|---|
shutdown | 只关闭发送方向而不关闭读取方向 | 直接使得该 socket 不可用,然后发出 FIN 报文 | 优雅关闭,四次 |
当被动关闭方(上图的服务端)在 TCP 挥手过程中,「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。
TCP 延迟确认的策略:
当有响应数据要发送时,ACK 会随着响应数据一起立刻发送给对方
当没有响应数据要发送时,ACK 将会延迟一段时间,以等待是否有响应数据可以一起发送
如果在延迟等待发送 ACK 期间,对方的第二个数据报文又到达了,这时就会立刻发送 ACK
四次挥手丢失
注意永远不会重传ack报文
阶段 | 影响 |
---|---|
第一次丢失 | 客户端超时重传FIN |
第二次丢失 | 客户端超时重传FIN;使用close关闭,超过60s直接断开连接;使用shutdown,死等,一直处于finwait2状态 |
第三次丢失 | 服务端超时重传FIN |
第四次丢失 | 服务端重发FIN,客户端收到重置2MSL,当等待 2MSL 时长后,客户端就会断开连接 |
为什么需要TIME_WAIT状态?
需要 TIME-WAIT 状态,主要是两个原因:
- 防止历史连接中的数据,被后面相同四元组的连接错误的接收;
- 保证「被动关闭连接」的一方,能被正确的关闭;
序列号和初始化序列号并不是无限递增的,会发生回绕为初始值的情况,这意味着无法根据序列号来判断新老数据。这个2MSL时间足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的
为什么 TIME_WAIT 等待的时间是 2MSL?
1 |
|
网络中可能存在来自发送方的数据包,一来一回需要等待 2 倍的时间。
至少允许报文丢失一次。比如,若 ACK 在一个 MSL 内丢失,这样被动方重发的 FIN 会在第 2 个 MSL 内到达,TIME_WAIT 状态的连接可以应对。
TIME_WAIT过多危害
发起连接方:端口被占满,但不同服务端端口可以复用
连接接收方:端口不受限,但占用系统资源
场景题
3、讲讲你对套接字编程的理解,它的协议是如何的?
socket通常称为“套接字”,用于描述IP地址和端口
应用程序通过套接字向网络发出请求或应答网络请求。
服务器和客户端通过socket进行交互。服务器需要绑定在本机的某个端口号上,客户端需要声明自己连接哪个地址的哪个端口,这样服务器和客户端就能连接了。
连接步骤:服务器监听,客户端请求,连接确认
socket是对TCP/IP协议的封装和应用。结合TCP三次握手。
TCP socket编程流程
- 服务端和客户端初始化
socket
,得到文件描述符; - 服务端调用
bind
,将 socket 绑定在指定的 IP 地址和端口; - 服务端调用
listen
,进行监听; - 服务端调用
accept
,等待客户端连接; - 客户端调用
connect
,向服务端的地址和端口发起连接请求; - 服务端
accept
返回用于传输的socket
的文件描述符; - 客户端调用
write
写入数据;服务端调用read
读取数据; - 客户端断开连接时,会调用
close
,那么服务端read
读取数据的时候,就会读取到了EOF
,待处理完数据后,服务端调用close
,表示连接关闭。
监听的 socket 和真正用来传送数据的 socket,是「两个」 socket,一个叫作监听 socket,一个叫作已完成连接 socket。
成功连接建立之后,双方开始通过 read 和 write 函数来读写数据
结合图片理解
几个点:
listen的时候参数backlog代表accept队列长度
客户端 connect 成功返回是在第二次握手,服务端 accept 成功返回是在三次握手成功之后
半连接队列结构:哈希表
全连接队列结构:链表
针对半连接的攻击:SYN_FLOOD, 客户端死活不发第三次握手过来,挂着你
解决方法:绕过半连接队列,使用cookie,cookies
并不会有一个专门的队列保存,它是通过通信双方的IP地址端口、时间戳、MSS等信息进行实时计算的,保存在TCP报头的seq
里。
没有 accept,能建立 TCP 连接吗?
答案:可以的。
accept 系统调用并不参与 TCP 三次握手过程,它只是负责从 TCP 全连接队列取出一个已经建立连接的 socket,用户层通过 accpet 系统调用拿到了已经建立连接的 socket,就可以对该 socket 进行读写操作了。
没有 listen,能建立 TCP 连接吗?
答案:可以的。
客户端是可以自己连自己的形成连接(TCP自连接),也可以两个客户端同时向对方发出请求建立连接(TCP同时打开),这两个情况都有个共同点,就是没有服务端参与,也就是没有 listen,就能 TCP 建立连接。
在 TCP 自连接的情况中,客户端在 connect 方法时,最后会将自己的连接信息放入到这个全局 hash 表中,然后将信息发出,消息在经过回环地址重新回到 TCP 传输层的时候,就会根据 IP + 端口信息,再一次从这个全局 hash 中取出信息。于是握手包一来一回,最后成功建立连接
4.19 服务端没有 listen,客户端发起连接建立,会发生什么?
服务端如果只 bind 了 IP 地址和端口,而没有调用 listen 的话,然后客户端对服务端发起了连接建立,服务端会回 RST 报文
Socket和Web Socket区别?
WebSocket是应用层协议,基于TCP,模拟Socket协议,可以双向发送或接受信息,需要三次握手:
- 浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。
- TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)
- 服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。
- 当收到了连接成功的消息后,通过TCP通道进行传输通信。
Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口
4、TCP和UDP区别?
衍生题目:如何基于UDP实现可靠传输,使用UDP,TCP的协议
5、TCP 怎么保证可靠传输?
详细介绍下 TCP 滑动窗口(累积确认、流量控制、拥塞控制)?
TCP 是通过序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输的。
重传
超时重传
RTT | Round-Trip Time | 往返时延 | 数据发送时刻到接收到确认的时刻的差值,也就是包的往返时间 |
---|---|---|---|
RTO | Retransmission Timeout | 超时重传时间 | 超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。 |
- 当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
- 当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发
快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
SACK
( Selective Acknowledgment), 选择性确认,将已收到的数据的信息发送给「发送方」,只重传丢失的数据
Duplicate SACK 又称 D-SACK
,其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。
滑动窗口
结合累计确认
窗口的大小是由接收方的窗口大小来决定的,接收窗口的大小是约等于发送窗口的大小的
流量控制
TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。
发送窗口和接收窗口中所存放的字节数,都是放在操作系统内存缓冲区中的,而操作系统的缓冲区,会被操作系统调整
TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间再减少缓存,这样就可以避免了丢包情况
窗口关闭:如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。
当发生窗口关闭时,接收方处理完数据后,会向发送方通告一个窗口非 0 的 ACK 报文,如果这个通告窗口的 ACK 报文在网络中丢失,这会导致发送方一直等待接收方的非 0 窗口通知,接收方也一直等待发送方的数据,如不采取措施,这种相互等待的过程,会造成了死锁的现象
解决窗口死锁:
拥塞控制
慢启动、拥塞避免、拥塞发生、快速恢复
指标:cwnd 拥塞窗口
慢启动指数增长,拥塞避免线性增长
丢包触发重传机制:超时和快速两种
当发生了「超时重传」,则就会使用拥塞发生算法。
这个时候,ssthresh 和 cwnd 的值会发生变化:
ssthresh
设为cwnd/2
,cwnd
重置为1
(是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1)
缺点:变化太剧烈
使用快重传
cwnd = cwnd/2
,也就是设置为原来的一半;ssthresh = cwnd
;- 进入快速恢复算法
什么是TCP拆包和粘包问题?怎么解决?
首先明确原因:
- TCP面向字节流
- 用户消息通过 TCP 协议传输时,消息可能会被操作系统分组成多个的 TCP 报文
- 不知道消息边界无法读出有效的用户消息
为什么UDP没有?
粘包拆包问题在数据链路层、网络层以及传输层都有可能发生。日常的网络应用开发大都在传输层进行,由于UDP有消息保护边界,不会发生粘包拆包问题,因此粘包拆包问题只发生在TCP协议中。
操作系统在收到 UDP 报文后,会将其插入到队列里,队列里的每一个元素就是一个 UDP 报文,这样当用户调用 recvfrom() 系统调用读数据的时候,就会从队列里取出一个数据,然后从内核里拷贝给用户缓冲区。
粘包拆包发生场景:
因为TCP是面向流,没有边界,而操作系统在发送TCP数据时,会通过缓冲区来进行优化,例如缓冲区为1024个字节大小。
如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题。
如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包。
常见解决方案:
- 发送端将每个包都封装成固定的长度,比如100字节大小。如果不足100字节可通过补0或空等进行填充到指定长度;
- 发送端在每个包的末尾使用固定的分隔符,例如\r\n。如果发生拆包需等待多个包发送过来之后再找到其中的\r\n进行合并;例如,FTP协议;
- 将消息分为头部和消息体,头部中保存整个消息的长度,只有读取到足够长度的消息之后才算是读到了一个完整的消息;
- 通过自定义协议进行粘包和拆包的处理。
TCP糊涂窗口综合症(SWS)和解决方案?
是什么?
当发送端应用进程产生数据很慢、或接收端应用进程处理接收缓冲区数据很慢,或二者兼而有之;就会使应用进程间传送的报文段很小,特别是有效载荷很小。极端情况下,有效载荷可能只有1个字节;而传输开销有40字节(20字节的IP头+20字节的TCP头) 这种现象就叫糊涂窗口综合症。
解决方案?
发送方:
Nagle 算法:指发送端即使还有应该发送的数据,但如果这部分数据很少的话,则进行延迟发送的一种处理机制。具体来说,当满足已发送的数据都已经收到确认应答时;或者可以发送的数据大小 >= 内核缓冲区大小的一半时,才可以发送数据。这样,从发送方的角度去防止了小报文的发送
接收方:
方案 1:只要窗口大小 < 某个值(内核缓冲区大小的一半)的时候,就直接将窗口大小设置为 0,防止发送方发送小数据;然后等到窗口大小 >= 内核缓冲区大小的一半 的时候,才打开窗口,通告发送方,告知其可以发送数据。这样就可以防止发送方发送小报文了
方案 2(延迟确认应答):延迟确认应答其实是针对如果接受数据的主机在接收到报文的时候就立刻返回 ACK 应答的话,这时候返回的窗口可能比较小。如果接收方稍微等一会再应答,那么这个时候接收的数据已经被处理完了,从缓冲区中被清理出去了,这样的话,窗口就变大了,发送方能够发送的数据也就更多了
TCP首部包含哪些字段?
TCP 首部包含可选项,所以总体长度可变,但包含 20 字节的固定部分(和 IP 首部一样)
- 确认 ACK:仅当 ACK = 1 时确认号字段 ack 才有效。TCP 规定,TCP 规定除了最初建立连接时的
SYN
包之外该位必须设置为 1 - 推送 PSH(Push):当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP 就可以使用推送(push)操作。这时,发送方 TCP 把 PSH 置为 1,并立即创建一个报文段发送出去。接收方 TCP 收到 PSH = 1 的报文段,就尽快地交付接收应用进程。而不用等到整个缓存都填满了后再向上交付。
- 复位 RST(Reset):当 RST = 1 时,表明 TCP 连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立连接
- 同步 SYN:SYN = 1 表示这是希望建立连接
- 终止 FIN(Finish):用来释放一个连接。当 FIN = 1时,表明此报文段的发送发的数据已发送完毕,并要求释放连接
TCP中有哪些定时器?
对于每个TCP连接,TCP一般要管理4个不同的定时器:重传定时器、坚持定时器、保活定时器、2MSL定时器。
重传定时器:计算超时重传时间
坚持定时器:也叫持续计数器,为了解决窗口为0的问题
若坚持定时器设置的时间到期,就发送一个零窗体控測报文段(该报文段仅仅有一个字节的数据,它有一个序号,但该序号永远不须要确认,因此该序号能够持续重传),之后会出现下面三种情况:
1、对方在收到探測报文段后,在对该报文段的确认中给出如今的窗体值,假设窗体值仍未零,则收到这个报文段的一方将坚持定时器的值加倍并重新启动。坚持计数器最大仅仅能添加到约60秒,在此之后,每次收到零窗体通知,坚持计数器的值就定位60秒。 2、对方在收到探測报文段后,在对该报文段的确认中给出如今的窗体值,假设窗体不为零,那么死锁的僵局就被打破了。 3、该探測报文发出后,会同一时候启动重传定时器,假设重传定时器的时间到期,还没有收到接收到发来的响应,则超时重传探測报文。
保活定时器:应对两个TCP连接间长时间没有传输数据的情况。 通常为2小时,以后每隔75s发送一次,连续10次终止连接。
2MSL定时器:为什么 TIME_WAIT 等待的时间是 2MSL?
怎么快速回收TCP资源?
快速回收和重用TIME-WAIT
不再等待2MSL,等待一个重传时间直接释放。
IP层可以保存一个peer信息,有peer的IP地址信息以及TCP最后一次触摸它的时间戳,遵循一定规范就可快速接入。
TCP的TIME_WAIT快速回收与重用_dog250的博客-CSDN博客
DNS协议为什么同时使用TCP和UDP?
DNS使用TCP协议进行区域传输,使用UDP协议进行名称查询。
UDP协议用于交换小信息,而TCP协议必须用于交换大于512字节的信息。
如果客户端未从DNS获取响应,则必须在间隔3-5秒后使用TCP重新传输数据。
当域名解析的反馈报文的长度超过512字节时,将不能使用UDP协议进行解析,此时必须使用TCP。
区域传输的数据量相比单次DNS查询的数据量要大得多,区域传输对数据的可靠性和准确性相比普通的DNS查询要高得多,因此使用TCP协议。
一台机器最大支持多少TCP连接?
一台机器最多能撑多少个TCP连接? 今天掰扯清楚! - 知乎 (zhihu.com)
- TCP连接的客户端机:每一个ip可建立的TCP连接理论受限于ip_local_port_range参数,也受限于65535。但可以通过配置多ip的方式来加大自己的建立连接的能力。
- TCP连接的服务器机:每一个监听的端口虽然理论值很大,但这个数字没有实际意义。最大并发数取决你的内存大小,每一条静止状态的TCP连接大约需要吃3
.3K的内存。
一台主机可以有多少端口?
TCP/IP协议中的服务端口通过不同的逻辑端口来区分不同的服务。一个IP地址的端口通过16bit进行编号,最多可以有65535个端口。
常见的端口号及其用途
1 |
|