网站建设立项申请报告,百度商家平台登录,wordpress提示无法创建目录下,网站 数据库+1文章目录 1. TCP头部包含哪些内容#xff1f;2. 为什么需要 TCP 协议#xff1f; TCP 工作在哪一层#xff1f;3. 什么是 TCP #xff1f;4. 什么是 TCP 连接#xff1f;5. 如何唯一确定一个 TCP 连接呢#xff1f;6. UDP头部大小是多少#xff1f;包含哪些内容#xf… 文章目录 1. TCP头部包含哪些内容2. 为什么需要 TCP 协议 TCP 工作在哪一层3. 什么是 TCP 4. 什么是 TCP 连接5. 如何唯一确定一个 TCP 连接呢6. UDP头部大小是多少包含哪些内容7. TCP与UDP的区别9. TCP 和 UDP 可以使用同一个端口吗10. TCP 三次握手过程是怎样的11. 如何在 Linux 系统中查看 TCP 状态12. 为什么是三次握手不是两次、四次13. 为什么每次建立 TCP 连接时初始化的序列号都要求不一样呢14. 初始序列号 ISN 是如何随机产生的15. 既然 IP 层会分片为什么 TCP 层还需要 MSS 呢16. 第一次握手丢失了会发生什么17. 第二次握手丢失了会发生什么18. 第三次握手丢失了会发生什么19. 什么是 SYN 攻击如何避免 SYN 攻击20. TCP 四次挥手过程是怎样的21. 为什么挥手需要四次22. 第一次挥手丢失了会发生什么23. 第二次挥手丢失了会发生什么24. 第三次挥手丢失了会发生什么25. 第四次挥手丢失了会发生什么26. 为什么 TIME_WAIT 等待的时间是 2MSL27. 为什么需要 TIME_WAIT 状态28. 过多的TIME_WAIT 状态有什么危害29. 服务器出现大量 TIME_WAIT 状态的原因有哪些30. 如果已经建立了连接但是客户端突然出现了宕机或者断电怎么办31. 当服务器向客户端发送TCP保活的探测报文后续会出现哪几种情况32. 如果已经建立了连接但是服务端的进程崩溃会发生什么33. 针对 TCP 应该如何 Socket 编程34. listen 时候参数 backlog 的意义35. accept 发生在三次握手的哪一步36. 没有 accept能建立 TCP 连接吗37. 没有 listen能建立 TCP 连接吗38. 说说 TCP 如何保证数据的可靠性传输39. TCP针对数据包丢失的情况有哪些重传机制40. 滑动窗口41. 流量控制42. 拥塞控制43. 什么是TCP半连接队列和全连接队列44. 如何理解是 TCP 面向字节流协议45. 如何解决TCP粘包46. SYN 报文什么时候情况下会被丢弃47. 防御 SYN 攻击的方法有哪些48. 一个已经建立的 TCP 连接客户端中途宕机了而服务端此时也没有数据要发送一直处于 Established 状态客户端恢复后向服务端建立连接此时服务端会怎么处理49. 如何关闭一个 TCP 连接50. 在 FIN_WAIT_2 状态下是如何处理收到的乱序 FIN 报文然后 TCP 连接又是什么时候才进入到 TIME_WAIT 状态?51. 在 TCP 正常挥手过程中处于 TIME_WAIT 状态的连接收到相同四元组的 SYN 后会发生什么52. TCP连接客户端进程崩溃和客户端主机宕机发生后有什么区别53. 拔掉网线后 原本的 TCP 连接还存在吗54. HTTPS 中 TLS 和 TCP 能同时握手吗55. TCP Keepalive 和 HTTP Keep-Alive 是一个东西吗56. 多个 TCP 服务进程可以同时绑定同一个端口吗57. 如何解决服务端重启时报错“Address already in use”的问题58. 客户端的端口可以重复使用吗59. 客户端 TCP 连接 TIME_WAIT 状态过多会导致端口资源耗尽而无法建立新的连接吗60. 没有 accept能建立 TCP 连接吗61. 为什么半连接队列要设计成哈希表62. 有没有一种方法可以绕过半连接队列63. 没有listen为什么还能建立连接?64. 介绍以下关闭连接的close函数和shutdown函数65. 什么是 TCP 延迟确认机制66. TCP 四次挥手可以变成三次吗 1. TCP头部包含哪些内容 2. 为什么需要 TCP 协议 TCP 工作在哪一层
由于IP层是不可靠的它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。 为了保证网络数据包的可靠性传输那么就需要由传输层的 TCP 协议来负责。因为 TCP 是一个工作在传输层的可靠数据传输的服务它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。 3. 什么是 TCP
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。 4. 什么是 TCP 连接
用于保证可靠性和流量控制维护的某些状态信息这些信息的组合包括 Socket、序列号和窗口大小称为连接。 所以我们可以知道建立一个 TCP 连接是需要客户端与服务端达成上述三个信息的共识。 5. 如何唯一确定一个 TCP 连接呢
TCP 四元组可以唯一的确定一个连接四元组包括如下源IP地址、源端口、目标IP地址、目标端口。 6. UDP头部大小是多少包含哪些内容
UDP头部占8个字节包含源端口号16位、目标端口号16位、包长度16位、校验和16位。 7. TCP与UDP的区别
连接
TCP 是面向连接的传输层协议传输数据前先要建立连接。UDP 是不需要连接即刻传输数据。
服务对象
TCP 是一对一的两点服务即一条连接只有两个端点。UDP 支持一对一、一对多、多对多的交互通信。
可靠性
TCP 是可靠交付数据的数据可以无差错、不丢失、不重复、按序到达。UDP 是尽最大努力交付不保证可靠交付数据。但是我们可以基于 UDP 传输协议实现一个可靠的传输协议比如 QUIC 协议。
拥塞控制、流量控制
TCP 有拥塞控制和流量控制机制保证数据传输的安全性。UDP 则没有即使网络非常拥堵了也不会影响 UDP 的发送速率。
首部开销
TCP 首部长度较长会有一定的开销首部在没有使用选项字段时是 20 个字节如果使用了选项字段则会变长的。UDP 首部只有 8 个字节并且是固定不变的开销较小。
传输方式
TCP 是基于字节流进行数据传输没有边界但保证顺序和可靠。UDP 是一个包一个包的发送是有边界的但可能会丢包和乱序。
分片不同
TCP 的数据大小如果大于 MSS 大小则会在传输层进行分片目标主机收到后也同样在传输层组装 TCP 数据包如果中途丢失了一个分片只需要传输丢失的这个分片。UDP 的数据大小如果大于 MTU 大小则会在 IP 层进行分片目标主机收到后在 IP 层组装完数据接着再传给传输层。
应用场景不同
由于 TCP 是面向连接能保证数据的可靠性交付因此经常用于FTP 文件传输、HTTP / HTTPS等。由于 UDP 面向无连接它可以随时发送数据再加上 UDP 本身的处理既简单又高效因此经常用于包总量较少的通信如 DNS 、SNMP 等视频、音频等多媒体通信广播通信。 9. TCP 和 UDP 可以使用同一个端口吗
可以的。传输层有两个传输协议分别是 TCP 和 UDP在内核中是两个完全独立的软件模块。因此TCP/UDP 各自的端口号也相互独立如 TCP 有一个 80 号端口UDP 也可以有一个 80 号端口二者并不冲突。 10. TCP 三次握手过程是怎样的
一开始客户端和服务端都处于 CLOSE 状态先是服务端主动监听某个端口处于 LISTEN 状态。 第1次握手 客户端会随机初始化序号client_isn将此序号置于 TCP 首部的序号字段中同时把 SYN 标志位置为 1表示 SYN 报文。接着把第一个 SYN 报文发送给服务端表示向服务端发起连接之后客户端处于 SYN-SENT 状态。 第2次握手 服务端收到客户端的 SYN 报文后首先服务端也随机初始化自己的序号server_isn将此序号填入 TCP 首部的序号字段中其次把 TCP 首部的确认应答号字段填入 client_isn 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端之后服务端处于 SYN-RCVD 状态。 第3次握手 客户端收到服务端报文后还要向服务返回最后一个应答报文首先该应答报文 TCP 首部 ACK 标志位置为 1 其次确认应答号字段填入 server_isn 1 最后把报文发送给服务端这次报文可以携带客户到服务端的数据之后客户端处于 ESTABLISHED 状态。服务端收到客户端的应答报文后也进入 ESTABLISHED 状态。
注意⚠️第三次握手是可以携带数据的前两次握手是不可以携带数据的。 11. 如何在 Linux 系统中查看 TCP 状态
在 Linux 可以通过 netstat -napt 命令查看TCP的连接状态。 12. 为什么是三次握手不是两次、四次
首要原因是为了防止旧的连接建立。如果是两次握手连接服务端没有中间状态给客户端来阻止历史连接导致服务端可能建立一个历史连接造成资源浪费。三次握手可同步双方初始序列号。虽然四次握手也能够可靠的同步双方的初始化序号但由于第二步和第三步可以优化成一步所以就成了三次握手。而两次握手只保证了一方的初始序列号能被对方成功接收没办法保证双方的初始序列号都能被确认接收。三次握手可避免资源浪费。假设客户端发送的 SYN 报文在网络中阻塞了重复发送多次 SYN 报文如果采用两次握手那么服务端在收到请求后就会建立多个冗余的无效链接造成不必要的资源浪费。 13. 为什么每次建立 TCP 连接时初始化的序列号都要求不一样呢
为了防止历史报文被下一个相同四元组的连接接收主要方面。为了安全性防止伪造的相同序列号的 TCP 报文被对方接收。 14. 初始序列号 ISN 是如何随机产生的
ISN M F(localhost, localport, remotehost, remoteport)。
M 是一个计时器这个计时器每隔 4 微秒加 1。F 是一个 Hash 算法根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。要保证 Hash 算法不能被外部轻易推算得出用 MD5 算法是一个比较好的选择。 15. 既然 IP 层会分片为什么 TCP 层还需要 MSS 呢
如果在 TCP 的整个报文头部 数据交给 IP 层进行分片那么当如果一个 IP 分片丢失整个 IP 报文的所有分片都得重传。 因为当某一个 IP 分片丢失后接收方的 IP 层就无法组装成一个完整的 TCP 报文头部 数据也就无法将数据报文送到 TCP 层所以接收方不会响应 ACK 给发送方因为发送方迟迟收不到 ACK 确认报文所以会触发超时重传就会重传整个 TCP 报文头部 数据。经过 TCP 层分片后如果一个 TCP 分片丢失后进行重发时也是以 MSS 为单位只需要重传丢失的TCP分片大大增加了重传的效率。 16. 第一次握手丢失了会发生什么
如果达到超时时间客户端还没有收到服务端的 SYN-ACK 报文第二次握手就会触发超时重传机制重传 SYN 报文而且重传的 SYN 报文的序列号都是一样的每次超时的时间是上一次的 2 倍。当客户端达到最大重传次数再等待一段时间时间为上一次超时时间的 2 倍如果还是没能收到服务端的第二次握手SYN-ACK 报文那么客户端就会断开连接。 17. 第二次握手丢失了会发生什么
当第二次握手丢失了客户端和服务端都会重传
客户端会重传 SYN 报文也就是第一次握手服务端会重传 SYN-ACK 报文也就是第二次握手。
当达到最大重传次数再等待一段时间时间为上一次超时时间的 2 倍如果还没收到对端的响应就会断开连接。 18. 第三次握手丢失了会发生什么
当第三次握手丢失了如果服务端那一方迟迟收不到这个确认报文就会触发超时重传机制重传 SYN-ACK 报文直到收到第三次握手或者达到最大重传次数再等待一段时间时间为上一次超时时间的 2 倍如果还是没能收到客户端的第三次握手ACK 报文那么服务端就会断开连接。 19. 什么是 SYN 攻击如何避免 SYN 攻击
SYN 攻击方式最直接的表现就会把 TCP 半连接队列打满这样当 TCP 半连接队列满了后续再在收到 SYN 报文就会丢弃导致客户端无法和服务端建立连接。 最常见的就是攻击者短时间伪造不同 IP 地址的 SYN 报文服务端每接收到一个 SYN 报文就进入SYN_RCVD 状态但服务端发送出去的 ACK SYN 报文无法得到未知 IP 主机的 ACK 应答久而久之就会占满服务端的半连接队列使得服务端不能为正常用户服务。 避免SYN攻击的方法调大 netdev_max_backlog、增大 TCP 半连接队列、开启 tcp_syncookies、减少 SYNACK 重传次数。 20. TCP 四次挥手过程是怎样的
第一次挥手 客户端打算关闭连接此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文也即 FIN 报文之后客户端进入 FIN_WAIT_1 状态。 第二次挥手 服务端收到该报文后就向客户端发送 ACK 应答报文接着服务端进入 CLOSE_WAIT 状态。 客户端收到服务端的 ACK 应答报文后之后进入 FIN_WAIT_2 状态。 第三次挥手 等待服务端处理完数据后也向客户端发送 FIN 报文之后服务端进入 LAST_ACK 状态。 第四次挥手 客户端收到服务端的 FIN 报文后回一个 ACK 应答报文之后进入 TIME_WAIT 状态。然后再经过 2MSL 时间后自动进入 CLOSE 状态至此客户端完成连接的关闭。 服务端收到了 ACK 应答报文后就进入了 CLOSE 状态至此服务端完成连接的关闭。 21. 为什么挥手需要四次
服务端通常需要等待完成数据的发送和处理所以服务端的 ACK 和 FIN 一般都会分开发送因此是需要四次挥手。但是在特定情况下四次挥手是可以变成三次挥手的。 当被动关闭方在 TCP 挥手过程中如果没有数据要发送同时没有开启 TCP_QUICKACK默认情况就是没有开启没有开启 TCP_QUICKACK等于就是在使用 TCP 延迟确认机制那么第二和第三次挥手就会合并传输这样就出现了三次挥手。所以出现三次挥手现象是因为 TCP 延迟确认机制导致的。 22. 第一次挥手丢失了会发生什么
如果第一次挥手丢失了那么客户端迟迟收不到被动方的 ACK 的话也就会触发超时重传机制重传 FIN 报文当客户端重传 FIN 报文的次数超过最大重传次数后就不再发送 FIN 报文则会再等待一段时间时间为上一次超时时间的 2 倍如果还是没能收到第二次挥手那么直接进入到 close 状态。 23. 第二次挥手丢失了会发生什么
由于第二次挥手的ACK 报文是不会重传的所以如果服务端的第二次挥手丢失了客户端就会触发超时重传机制重传 FIN 报文直到收到服务端的第二次挥手或者达到最大的重传次数后再等待一段时间时间为上一次超时时间的 2 倍如果还是没能收到第二次挥手那么直接进入到 close 状态。 24. 第三次挥手丢失了会发生什么
如果服务器迟迟收不到客户端的 ACK也就会触发超时重传机制重传 FIN 报文当服务端重传 FIN 报文的次数超过最大重传次数后就不再发送 FIN 报文则会再等待一段时间时间为上一次超时时间的 2 倍如果还是没能收到第四次挥手那么直接进入到 close 状态。 25. 第四次挥手丢失了会发生什么
第四次挥手丢失时服务器服务器迟迟收不到客户端的 ACK也就会触发超时重传机制当服务端重传第三次挥手报文达到了最大重传次数于是再等待一段时间时间为上一次超时时间的 2 倍如果还是没能收到客户端的第四次挥手ACK 报文那么服务端就会断开连接。客户端在收到第三次挥手后就会进入 TIME_WAIT 状态开启时长为 2MSL 的定时器如果途中再次收到第三次挥手FIN 报文后就会重置定时器当等待 2MSL 时长后客户端就会断开连接。 26. 为什么 TIME_WAIT 等待的时间是 2MSL
MSL报文最大生存时间它是任何报文在网络上存在的最长时间超过这个时间报文将被丢弃。 2MSL时长 这其实是相当于至少允许报文丢失一次。比如若 ACK 在一个 MSL 内丢失这样被动方重发的 FIN 会在第 2 个 MSL 内到达TIME_WAIT 状态的连接可以应对。 为什么不是 4MSL 或者 8MSL 的时长呢你可以想象一个丢包率达到百分之一的糟糕网络连续两次丢包的概率只有万分之一这个概率实在是太小了忽略它比解决它更具性价比。 在 Linux 系统里 2MSL 默认是 60 秒那么一个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。 27. 为什么需要 TIME_WAIT 状态
原因一防止历史连接中的数据被后面相同四元组的连接错误的接收 序列号是一个 32 位的无符号数因此在到达 4G 之后再循环回到 0。初始化序列号可被视为一个 32 位的计数器该计数器的数值每 4 微秒加 1循环一次需要 4.55 小时。序列号和初始化序列号并不是无限递增的会发生回绕为初始值的情况这意味着无法根据序列号来判断新老数据。 为了防止历史连接中的数据被后面相同四元组的连接错误的接收因此 TCP 设计了 TIME_WAIT 状态状态会持续 2MSL 时长这个时间足以让两个方向上的数据包都被丢弃使得原来连接的数据包在网络中都自然消失再出现的数据包一定都是新建立连接所产生的。 原因二保证被动关闭连接的一方能被正确的关闭 主动断开连接的一方保持TIME-WAIT状态等待足够的时间以确保最后的 ACK 能让被动关闭方接收从而帮助其正常关闭。 28. 过多的TIME_WAIT 状态有什么危害
第一是占用系统资源比如文件描述符、内存资源、CPU 资源、线程资源等第二是占用端口资源端口资源也是有限的客户端发起连接方都是和目的 IP 目的 PORT 都一样的服务端建立连接的话当客户端的 TIME_WAIT 状态连接过多的话就会受端口资源限制如果占满了所有端口资源那么就无法再跟目的 IP 目的 PORT都一样的服务端建立连接了。 29. 服务器出现大量 TIME_WAIT 状态的原因有哪些
HTTP 没有使用长连接 当服务端出现大量的 TIME_WAIT 状态连接的时候可以排查下是否客户端和服务端都开启了 HTTP Keep-Alive因为任意一方没有开启 HTTP Keep-Alive都会导致服务端在处理完一个 HTTP 请求后就主动关闭连接此时服务端上就会出现大量的 TIME_WAIT 状态的连接。HTTP 长连接超时 当服务端出现大量 TIME_WAIT 状态的连接时如果现象是有大量的客户端建立完 TCP 连接后很长一段时间没有发送数据那么大概率就是因为 HTTP 长连接超时导致服务端主动关闭连接产生大量处于 TIME_WAIT 状态的连接。HTTP 长连接的请求数量达到上限 Web 服务端通常会有个参数来定义一条 HTTP 长连接上最大能处理的请求数量当超过最大限制时就会主动关闭连接那么此时服务端上可能就会出现 TIME_WAIT 状态的连接。 30. 如果已经建立了连接但是客户端突然出现了宕机或者断电怎么办
发生这种情况的时候如果服务端一直不会发送数据给客户端那么服务端最初是无法感知到客户端宕机这个事件的当达到保活时间以后服务器会主动向客户端发送TCP保活的探测报文每隔一个时间间隔发送一个探测报文该探测报文包含的数据非常少如果连续几个探测报文都没有得到响应则认为当前的 TCP 连接已经死亡服务器主动断开连接。 31. 当服务器向客户端发送TCP保活的探测报文后续会出现哪几种情况
对端程序是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应这样 TCP 保活时间会被重置等待下一个 TCP 保活时间的到来。对端主机宕机并重启。当 TCP 保活的探测报文发送给对端后对端是可以响应的但由于没有该连接的有效信息会产生一个 RST 报文这样很快就会发现 TCP 连接已经被重置。对端主机宕机注意不是进程崩溃进程崩溃后操作系统在回收进程资源的时候会发送 FIN 报文而主机宕机则是无法感知的所以需要 TCP 保活机制来探测对方是不是发生了主机宕机或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后没有响应连续发送几次达到保活探测次数后TCP 会报告该 TCP 连接已经死亡。 32. 如果已经建立了连接但是服务端的进程崩溃会发生什么
TCP 的连接信息是由内核维护的所以当服务端的进程崩溃后内核需要回收该进程的所有 TCP 连接资源于是内核会发送第一次挥手 FIN 报文后续的挥手过程也都是在内核完成并不需要进程的参与所以即使服务端的进程退出了还是能与客户端完成 TCP 四次挥手的过程。 33. 针对 TCP 应该如何 Socket 编程
服务端和客户端初始化 socket 得到用于监听的文件描述符服务端调用 bind将 socket 绑定在指定的 IP 地址和端口;服务端调用 listen进行监听服务端调用 accept等待客户端连接客户端调用 connect向服务端的地址和端口发起连接请求服务端 accept 返回用于传输的 socket 的文件描述符客户端调用 write 写入数据服务端调用 read 读取数据客户端断开连接时会调用 close那么服务端 read 读取数据的时候就会读取到了 EOF待处理完数据后服务端调用 close表示连接关闭。 34. listen 时候参数 backlog 的意义
Linux内核中会维护两个队列
半连接队列SYN 队列接收到一个 SYN 建立连接请求处于 SYN_RCVD 状态全连接队列Accpet 队列已完成 TCP 三次握手过程处于 ESTABLISHED 状态 int listen (int socketfd, int backlog)参数一 socketfd 为 socketfd 文件描述符。参数二 backlog这参数在历史版本有一定的变化。在早期 Linux 内核 backlog 是 SYN 队列大小也就是未完成的队列大小。在 Linux 内核 2.2 之后backlog 变成 accept 队列也就是已完成连接建立的队列长度所以现在通常认为 backlog 是 accept 队列。 35. accept 发生在三次握手的哪一步
客户端 connect 成功返回是在第二次握手服务端 accept 成功返回是在三次握手成功之后。 36. 没有 accept能建立 TCP 连接吗
可以的。accpet 系统调用并不参与 TCP 三次握手过程它只是负责从 TCP 全连接队列取出一个已经建立连接的 socket用户层通过 accpet 系统调用拿到了已经建立连接的 socket就可以对该 socket 进行读写操作了。 37. 没有 listen能建立 TCP 连接吗
可以的。客户端是可以自己连自己的形成连接TCP自连接也可以两个客户端同时向对方发出请求建立连接TCP同时打开这两个情况都有个共同点就是没有服务端参与也就是没有 listen也能建立TCP连接。 38. 说说 TCP 如何保证数据的可靠性传输
TCP主要提供了检验和、序列号/确认应答、重传机制、最大消息长度、滑动窗口控制、流量控制、拥塞控制等方法实现了可靠性传输。 39. TCP针对数据包丢失的情况有哪些重传机制
第一种超时重传 在发送数据时设定一个定时器当超过指定的时间后没有收到对方的 ACK 确认应答报文就会重发该数据也就是我们常说的超时重传。 超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。 如果超时重发的数据再次超时的时候又需要重传的时候TCP 的策略是超时间隔加倍。 第二种快速重传 快速重传Fast Retransmit机制它不以时间为驱动而是以数据驱动重传。 快速重传的工作方式是当收到三个相同的 ACK 报文时会在定时器过期之前重传丢失的报文段。 快速重传机制只解决了一个问题就是超时时间的问题。但是它依然面临着另外一个问题就是重传的时候是重传一个还是重传所有的问题。 第三种SACK 方法 SACK Selective Acknowledgment 选择性确认。 这种方式需要在 TCP 头部选项字段里加一个 SACK 的东西它可以将已收到的数据的信息发送给发送方这样发送方就可以知道哪些数据收到了哪些数据没收到知道了这些信息就可以只重传丢失的数据。Linux 2.4 后默认打开这个功能。 第四种Duplicate SACK Duplicate SACK 又称 D-SACK其主要使用了 SACK 来告诉发送方有哪些数据被重复接收了。 40. 滑动窗口
窗口大小指无需等待确认应答而可以继续发送数据的最大值。 假设窗口大小为3个TCP段那么发送方就可以连续发送3个TCP段并且中途若有ACK丢失可以通过下一个确认应答进行确认这个模式就叫累计确认或者累计应答。 TCP 头里有一个字段叫 Window也就是窗口大小。这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据所以通常窗口的大小是由接收方的窗口大小来决定的。 接收窗口和发送窗口并不是一定完全相等接收窗口的大小是约等于发送窗口大小的。因为新的发送窗口大小是通过TCP报文中的Windows字段来告诉发送方这个传输过程是存在时延的。 41. 流量控制
TCP 提供一种机制可以让发送方根据接收方的实际接收能力控制发送的数据量这就是所谓的流量控制。 实际上发送窗口和接收窗口中所存放的字节数都是放在操作系统内存缓冲区中的而操作系统的缓冲区会被操作系统调整。 TCP 规定是不允许同时减少缓存又收缩窗口的而是采用先收缩窗口过段时间再减少缓存这样就可以避免了丢包情况。 如果窗口大小为 0 时就会阻止发送方给接收方传递数据直到窗口变为非 0 为止这就是窗口关闭。 接收方向发送方通告窗口大小时是通过 ACK 报文来通告的。那么当发生窗口关闭时接收方处理完数据后会向发送方通告一个窗口非 0 的 ACK 报文如果这个通告窗口的 ACK 报文在网络中丢失了这会导致发送方一直等待接收方的非 0 窗口通知接收方也一直等待发送方的数据如不采取措施这种相互等待的过程会造成了死锁的现象。 为了解决这个死锁问题TCP 为每个连接设有一个持续定时器只要 TCP 连接一方收到对方的零窗口通知就启动持续计时器。如果持续计时器超时就会发送窗口探测 ( Window probe ) 报文而对方在确认这个探测报文时给出自己现在的接收窗口大小。 42. 拥塞控制
拥塞控制控制的目的就是避免发送方的数据填满整个网络。 拥塞窗口 cwnd是发送方维护的一个的状态变量它会根据网络的拥塞程度动态变化的。 发送窗口的值是swnd min(cwnd, rwnd)也就是拥塞窗口和接收窗口中的最小值。 其实只要发送方没有在规定时间内接收到 ACK 应答报文也就是发生了超时重传就会认为网络出现了拥塞。 拥塞控制主要是四个算法慢启动、拥塞避免、拥塞发生、快速恢复。 1慢启动 当发送方每收到一个 ACK拥塞窗口 cwnd 的大小就会加 1。可以发现慢启动算法发包的个数是指数性的增长。 当拥塞窗口大小大于等于慢启动门限 ssthresh slow start threshold时就会使用拥塞避免算法。 2拥塞避免算法 进入拥塞避免算法后每当收到一个 ACK 时cwnd 增加 1/cwnd。 拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长还是增长阶段但是增长速度缓慢了一些。 当拥塞窗口增大到一定程度时网络慢慢进入拥塞的状况出现丢包的现象这时就会触发重传机制也就进入了拥塞发生算法。 3拥塞发生算法
发生超时重传的拥塞发生算法 当发生了超时重传则就会使用拥塞发生算法。ssthresh 设为 cwnd/2cwnd 重置为初始化值。 发生快速重传的拥塞发生算法 cwnd cwnd/2也就是设置为原来的一半ssthresh cwnd进入快速恢复算法。
4快速恢复算法 快速重传和快速恢复算法一般同时使用进入快速恢复算法如下
拥塞窗口 cwnd ssthresh 3 3 的意思是确认有 3 个数据包被收到了重传丢失的数据包如果再收到重复的 ACK那么 cwnd 增加 1如果收到新数据的 ACK 后把 cwnd 设置为第一步中的 ssthresh 的值原因是该 ACK 确认了新的数据说明从 duplicated ACK 时的数据都已收到该恢复过程已经结束可以回到恢复之前的状态了也即再次进入拥塞避免状态 43. 什么是TCP半连接队列和全连接队列
在 TCP 三次握手的时候Linux 内核会维护两个队列分别是半连接队列也称 SYN 队列全连接队列也称 accept 队列。 服务端收到客户端发起的 SYN 请求后内核会把该连接存储到半连接队列并向客户端响应 SYNACK接着客户端会返回 ACK服务端收到第三次握手的 ACK 后内核会把连接从半连接队列移除然后创建新的完全的连接并将其添加到 accept 队列等待进程调用 accept 函数时把连接取出来。 44. 如何理解是 TCP 面向字节流协议
当用户消息通过 TCP 协议传输时消息可能会被操作系统分组成多个的 TCP 报文因此我们不能认为一个用户消息对应一个 TCP 报文正因为这样所以 TCP 是面向字节流的协议。 45. 如何解决TCP粘包
一般有三种方式分包的方式来解决TCP粘包分别是固定长度的消息、特殊字符作为边界、自定义消息结构。 其中对于自定义一个消息结构由包头和数据组成其中包头包是固定大小的而且包头里有一个字段来说明紧随其后的数据有多大。 46. SYN 报文什么时候情况下会被丢弃
开启 tcp_tw_recycle 参数并且在 NAT 环境下造成 SYN 报文被丢弃TCP 两个队列满了半连接队列或全连接队列造成 SYN 报文被丢弃。 当服务器造成syn攻击就有可能导致 TCP 半连接队列满了这时后面来的syn包都会被丢弃。但是如果开启了syncookies功能即使半连接队列满了也不会丢弃syn包。在服务端并发处理大量请求时如果 TCP accpet 队列过小或者应用程序调用 accept() 不及时就会造成 accpet 队列满了 这时后续的连接就会被丢弃这样就会出现服务端请求数量上不去的现象。 47. 防御 SYN 攻击的方法有哪些
增大半连接队列、开启 tcp_syncookies 功能、减少 SYNACK 重传次数。 48. 一个已经建立的 TCP 连接客户端中途宕机了而服务端此时也没有数据要发送一直处于 Established 状态客户端恢复后向服务端建立连接此时服务端会怎么处理
这个问题关键要看客户端发送的 SYN 报文中的源端口是否和上一次连接的源端口相同。 1.客户端的 SYN 报文里的端口号与历史连接不相同 如果客户端恢复后发送的 SYN 报文中的源端口号跟上一次连接的源端口号不一样此时服务端会认为是新的连接要建立于是就会通过三次握手来建立新的连接。 如果服务端发送了数据包给客户端由于客户端的连接已经被关闭了此时客户的内核就会回 RST 报文服务端收到后就会释放连接。 如果服务端一直没有发送数据包给客户端在超过一段时间后TCP 保活机制就会启动检测到客户端没有存活后接着服务端就会释放掉该连接。 2. 客户端的 SYN 报文里的端口号与历史连接相同 处于 Established 状态的服务端如果收到了客户端的 SYN 报文注意此时的 SYN 报文其实是乱序的因为 SYN 报文的初始化序列号其实是一个随机数会回复一个携带了正确序列号和确认号的 ACK 报文这个 ACK 被称之为 Challenge ACK。 接着客户端收到这个 Challenge ACK发现确认号ack num并不是自己期望收到的于是就会回 RST 报文服务端收到后就会释放掉该连接。 49. 如何关闭一个 TCP 连接
通过伪造一个能关闭 TCP 连接的 RST 报文来关闭一个TCP连接。 而且RSA报文必须同时满足四元组相同和序列号是对方期望的这两个条件。 要想获取到对方期望的序列号可以借助tcpkill和killcx工具。
tcpkill 工具只能用来关闭活跃的 TCP 连接无法关闭非活跃的 TCP 连接因为 tcpkill 工具是等双方进行 TCP 通信后才去获取正确的序列号如果这条 TCP 连接一直没有任何数据传输则就永远获取不到正确的序列号。killcx 工具可以用来关闭活跃和非活跃的 TCP 连接因为 killcx 工具是主动发送 SYN 报文这时对方就会回复 Challenge ACK 然后 killcx 工具就能从这个 ACK 获取到正确的序列号。 50. 在 FIN_WAIT_2 状态下是如何处理收到的乱序 FIN 报文然后 TCP 连接又是什么时候才进入到 TIME_WAIT 状态?
当服务器发送的数据包被网络延迟客户端处于FIN_WAIT_2状态如果提前收到了第三次挥手FIN报文那么就被会加入到乱序队列并不会进入到 TIME_WAIT 状态。 等再次收到前面被网络延迟的数据包时会判断乱序队列有没有数据然后会检测乱序队列中是否有可用的数据如果能在乱序队列中找到与当前报文的序列号保持的顺序的报文就会看该报文是否有 FIN 标志如果发现有 FIN 标志这时才会进入 TIME_WAIT 状态。 51. 在 TCP 正常挥手过程中处于 TIME_WAIT 状态的连接收到相同四元组的 SYN 后会发生什么
针对这个问题关键是要看SYN的序列号和时间戳是否合法然后根据收到的SYN是否合法做不同的处理。
收到合法SYN 如果处于 TIME_WAIT 状态的连接收到合法的 SYN后就会重用此四元组连接跳过 2MSL 而转变为 SYN_RECV 状态接着就能进行建立连接过程。收到非法的 SYN 如果处于 TIME_WAIT 状态的连接收到非法的 SYN后就会再回复一个第四次挥手的 ACK 报文客户端收到后发现并不是自己期望收到确认号ack num就回 RST 报文给服务端。 52. TCP连接客户端进程崩溃和客户端主机宕机发生后有什么区别
如果客户端进程崩溃客户端的进程在发生崩溃的时候内核会发送FIN报文与服务器进行四次挥手。 如果客户端主机宕机根据服务器是否发送数据分情况讨论
如果服务器会发送数据由于客户端已经不存在服务器收不到响应报文服务器的数据报文会超时重传当重传总间隔时长达到一定阈值后会断开连接。如果服务器一直不会发送数据再看服务器是否开启TCP keepalive机制 如果有开启服务端在一段时间没有进行数据交互时会触发 TCP keepalive 机制探测对方是否存在如果探测到对方已经消亡则会断开自身的 TCP 连接如果没有开启服务端的 TCP 连接会一直存在并且一直保持在 ESTABLISHED 状态。 53. 拔掉网线后 原本的 TCP 连接还存在吗
客户端拔掉网线后并不会直接影响 TCP 连接状态。所以拔掉网线后TCP 连接是否还会存在关键要看拔掉网线之后有没有进行数据传输。 有数据传输的情况
在客户端拔掉网线后如果服务端发送了数据报文那么在服务端重传次数没有达到最大值之前客户端就插回了网线那么双方原本的 TCP 连接还是能正常存在就好像什么事情都没有发生。在客户端拔掉网线后如果服务端发送了数据报文在客户端插回网线之前服务端重传次数达到了最大值时服务端就会断开 TCP 连接。等到客户端插回网线后向服务端发送了数据因为服务端已经断开了与客户端相同四元组的 TCP 连接所以就会回 RST 报文客户端收到后就会断开 TCP 连接。至此 双方的 TCP 连接都断开了。
没有数据传输的情况
如果双方都没有开启 TCP keepalive 机制那么在客户端拔掉网线后如果客户端一直不插回网线那么客户端和服务端的 TCP 连接状态将会一直保持存在。如果双方都开启了 TCP keepalive 机制那么在客户端拔掉网线后如果客户端一直不插回网线TCP keepalive 机制会探测到对方的 TCP 连接没有存活于是就会断开 TCP 连接。而如果在 TCP 探测期间客户端插回了网线那么双方原本的 TCP 连接还是能正常存在。 54. HTTPS 中 TLS 和 TCP 能同时握手吗
在同时满足以下两个条件的情况下HTTPS 中的 TLS 握手过程可以同时进行TCP三次握手
客户端和服务端都开启了 TCP Fast Open 功能且 TLS 版本是 1.3客户端和服务器已经完成过一次通信。 55. TCP Keepalive 和 HTTP Keep-Alive 是一个东西吗
TCP Keepalive 和 HTTP Keep-Alive是两个完全不同的机制。
HTTP 的 Keep-Alive 也叫 HTTP 长连接该功能是由应用程序实现的可以使得用同一个 TCP 连接来发送和接收多个 HTTP 请求/应答减少了 HTTP 短连接带来的多次 TCP 连接建立和释放的开销。TCP 的 Keepalive 也叫 TCP 保活机制该功能是由内核实现的当客户端和服务端长达一定时间没有进行数据交互时内核为了确保该连接是否还有效就会发送探测报文来检测对方是否还在线然后来决定是否要关闭该连接。 56. 多个 TCP 服务进程可以同时绑定同一个端口吗
如果两个 TCP 服务进程同时绑定的 IP 地址和端口都相同那么执行 bind() 时候就会出错错误是“Address already in use”。 如果两个 TCP 服务进程绑定的端口都相同而 IP 地址不同那么执行 bind() 不会出错。 57. 如何解决服务端重启时报错“Address already in use”的问题
我们可以对 socket 设置 SO_REUSEADDR 属性这样即使存在一个和绑定 IPPORT 一样的 TIME_WAIT 状态的连接依然可以正常绑定成功因此可以正常重启成功。 58. 客户端的端口可以重复使用吗
在客户端执行 connect 函数的时候只要客户端连接的服务器不是同一个内核允许端口重复使用。 TCP 连接是由四元组源IP地址源端口目的IP地址目的端口唯一确认的那么只要四元组中其中一个元素发生了变化那么就表示不同的 TCP 连接的。 59. 客户端 TCP 连接 TIME_WAIT 状态过多会导致端口资源耗尽而无法建立新的连接吗
要看客户端是否都是与同一个服务器目标地址和目标端口一样建立连接。 如果客户端都是与同一个服务器目标地址和目标端口一样建立连接那么如果客户端 TIME_WAIT 状态的连接过多当端口资源被耗尽就无法与这个服务器再建立连接了。即使在这种状态下还是可以与其他服务器建立连接的只要客户端连接的服务器不是同一个那么端口是重复使用的。 60. 没有 accept能建立 TCP 连接吗
建立连接的过程中根本不需要accept()参与 执行accept()只是为了从全连接队列里取出一条连接因此可以建立TCP连接。 61. 为什么半连接队列要设计成哈希表
虽然都叫队列但其实全连接队列icsk_accept_queue是个链表而半连接队列syn_table是个哈希表。 先对比下全连接里队列他本质是个链表因为也是线性结构说它是个队列也没毛病。它里面放的都是已经建立完成的连接这些连接正等待被取走。而服务端取走连接的过程中并不关心具体是哪个连接只要是个连接就行所以直接从队列头取就行了。这个过程算法复杂度为O(1)。 而半连接队列却不太一样因为队列里的都是不完整的连接嗷嗷等待着第三次握手的到来。那么现在有一个第三次握手来了则需要从队列里把相应IP端口的连接取出如果半连接队列还是个链表那我们就需要依次遍历才能拿到我们想要的那个连接算法复杂度就是O(n)。 而如果将半连接队列设计成哈希表那么查找半连接的算法复杂度就回到O(1)了。 62. 有没有一种方法可以绕过半连接队列
半连接队列满了可能是因为受到了SYN Flood攻击可以设置tcp_syncookies绕开半连接队列。 当tcp_syncookies被设置为1的时候客户端发来第一次握手SYN时服务端不会将其放入半连接队列中而是直接生成一个cookies这个cookies会跟着第二次握手发回客户端。客户端在发第三次握手的时候带上这个cookies服务端验证到它就是当初发出去的那个就会建立连接并放入到全连接队列中。可以看出整个过程不再需要半连接队列的参与。 63. 没有listen为什么还能建立连接?
内核有个全局hash表可以用于存放sock连接的信息。在TCP自连接的情况中客户端在connect方法时最后会将自己的连接信息放入到这个全局hash表中然后将信息发出消息在经过回环地址重新回到TCP传输层的时候就会根据IP端口信息再一次从这个全局hash中取出信息。于是握手包一来一回最后成功建立连接。 64. 介绍以下关闭连接的close函数和shutdown函数
close 函数同时 socket 关闭发送方向和读取方向也就是 socket 不再有发送和接收数据的能力。如果有多进程/多线程共享同一个 socket如果有一个进程调用了 close 关闭只是让 socket 引用计数 -1并不会导致 socket 不可用同时也不会发出 FIN 报文其他进程还是可以正常读写该 socket直到引用计数变为 0才会发出 FIN 报文。shutdown 函数可以指定 socket 只关闭发送方向而不关闭读取方向也就是 socket 不再有发送数据的能力但是还是具有接收数据的能力。如果有多进程/多线程共享同一个 socketshutdown 则不管引用计数直接使得该 socket 不可用然后发出 FIN 报文如果有别的进程企图使用该 socket将会受到影响。
注意⚠️shutdown 函数也可以指定只关闭读取方向而不关闭发送方向但是这时候内核是不会发送 FIN 报文的因为发送 FIN 报文是意味着我方将不再发送任何数据而 shutdown 如果指定不关闭发送方向就意味着 socket 还有发送数据的能力所以内核就不会发送 FIN。 65. 什么是 TCP 延迟确认机制
TCP 延迟确认的策略
当有响应数据要发送时ACK 会随着响应数据一起立刻发送给对方当没有响应数据要发送时ACK 将会延迟一段时间以等待是否有响应数据可以一起发送如果在延迟等待发送 ACK 期间对方的第二个数据报文又到达了这时就会立刻发送 ACK 66. TCP 四次挥手可以变成三次吗
当被动关闭方在 TCP 挥手过程中如果没有数据要发送同时没有开启 TCP_QUICKACK默认情况就是没有开启没有开启 TCP_QUICKACK等于就是在使用 TCP 延迟确认机制那么第二和第三次挥手就会合并传输这样就出现了三次挥手。
所以出现三次挥手现象是因为 TCP 延迟确认机制导致的。