• TCP常见问题


    一、TCP连接的保活机制

    在一段时间内,如果TCP连接两方都没有数据交互,TCP的保活机制**(TCP keepalive)**会起作用,每隔一个时间段会发送一个探测报文,如果连着好几个探测报文都没有得到相应,则会认为当前TCP连接已经GG,系统内核会将错误信息通知给上层应用程序。
    在这里插入图片描述

    二、TCP连接一端宕机和进程崩溃分别会发生什么?

    在没有开启TCP keepalive的情况下

    • 宕机:主机崩了,另外一端是感知不到的,另外因为没有开启保活机制,也没有数据交互,所以一直会处以ESTABLISHD状态。
    • 进程崩溃:进程崩溃了,操作系统可以感知到,所以操作系统回给对方发送FIN报文,进行四次挥手断开连接。

    客户端主机宕机,又立即重启

    • 客户端没有进程监听对应的端口号,会发送RST报文,重置该连接。
    • 客户端有进程监听改端口,但是之前的TCP连接对应的结构体已丢失,所以找不到对应的socket,所以回RST,重置该连接。

    客户端宕机,没有重启
    服务端会发送发送的探测报文超时次数达到一定阈值,就会认为该TCP连接有问题。

    三、被动关闭方因网络延时导致在第三次握手的FIN比之前正常发送的数据包早到达主动关闭方会发生什么?

    在这里插入图片描述
    就是当主动关闭方收到FIN报文时是怎样处理的。根据源码分析,在FIN_WAIT2状态时会检查FIN报文的序列号,如果前面还有报文还未到达,所以此FIN是乱序的,因此会加入乱序队列。等到有新的报文到达时,会判断乱序报文队列里面有无可用数据,会在乱序队列中找与新到报文顺序的报文,查看此报文是否是FIN报文,如果是,则会进入TIME_WAIT状态。
    在这里插入图片描述

    四、客户端掉线了,服务端不知道,客户端重启后重新发送SYN请求服务端会作何处理?

    在三次握手时初始的序列号是随机的,所以一般情况下,客户端重新发送的SYN请求中的初始化序列号不是当前服务端期望收到序列号,服务端会回复RST。

    五、如何不杀死进程关闭一个TCP连接?

    伪造一个相同的四元组,发送的的RST报文序列号落在对端可接收的滑动窗口序列范围内。
    伪造相同的四元组容易,如何获取到对端期望的下一个报文序列号呢?
    根据问题四,可以先给对端发送一个SYN请求,服务端会回复challenge ACK,这个ACK的确认号,便是服务端期望接收的下一个报文序列号,然后使用此序列号作为发送RST的序列号,服务端收到后会释放连接。
    在 Linux 上有个叫 killcx 的工具,就是基于上面这样的方式实现的,它会主动发送 SYN 包获取 SEQ/ACK 号,然后利用 SEQ/ACK 号伪造两个 RST 报文分别发给客户端和服务端,这样双方的 TCP 连接都会被释放,这种方式活跃和非活跃的 TCP 连接都可以杀掉。

    六、SYN报文什么情况下会被丢弃?

    • accept或者SYN队列(没有开启cookies)满了的时候;
    • 开启tcp_tw_recycle,在NET环境下没可能会被丢弃;

    在tcp连接中有两个参数:

    • net.ipv4.tcp_tw_resuse:开启后客户端(发起连接方),connect时会随机找到一个TIME_WAIT时间超过1S的连接给新连接使用。
    • net,ipv4.tcp_tw_recycle:允许处于TIME_WAIT状态的连接被快速收回。

    要使得这两个选项生效,有一个前提条件,就是要打开 TCP 时间戳,即 net.ipv4.tcp_timestamps=1(默认即为 1)。
    **PAWS机制:**收发两方维护最近一次收到数据的时间戳,要求收到的数据包时间戳是递增的,如果不是递增,说明此数据包过期。

    当开启net,ipv4.tcp_tw_recycle和时间戳时会开启一种叫per-host的PWAS机制:
    对IP做PAWS检查,不是对IP+端口四元组做PAWS检查。因此客户端环境使用了NAT网关,客户端所有机器对外的IP地址都是相同,就会导致客户端A和客户端B的连接A比B的时间戳大,便会丢弃B的SYN包。

    七、tcp_tw_reus默认为什么不是开启的?

    八、正常挥手过程中,处于TIME_WAIT状态的连接收到相同四元组的SYN会怎么处理?

    如果处于 TIME_WAIT 状态的连接收到「合法的 SYN 」后,就会重用此四元组连接,跳过 2MSL 而转变为 SYN_RECV 状态,接着就能进行建立连接过程。
    如果处于 TIME_WAIT 状态的连接收到「非法的 SYN 」后,就会再回复一个第四次挥手的 ACK 报文,客户端收到后,发现并不是自己期望收到确认号(ack num),就回 RST 报文给服务端。

    九、如何使用UDP实现可靠传输?

    基于UDP实现的可靠传输QUIC协议。
    要给予UDP实现可靠传输就要在应用层上下功夫。首先在建立连接阶段确定连接ID,使用此ID可以实现迁移功能。在传输数据时Packet Number每个报文是独一无二的,严格递增,传输中有数据包丢失也不影响后续数据包的传输,从而解决了TCP接收窗口队头阻塞问题,后续重传丢失的数据包的Packet Number值也是一个比之前丢失的包id值大。TCP重传丢失报文的序列号是一样的会导致计算RTT时间不太准确。
    所以,Packet Number 单调递增的两个好处:

    • 可以更加精确计算 RTT,没有 TCP 重传的歧义性问题;
    • 可以支持乱序确认,防止因为丢包重传将当前窗口阻塞在原地,而 TCP 必须是顺序确认的,丢包时会导致窗口不滑动;

    在这里插入图片描述
    在这里插入图片描述
    重传的数据包通过Offset保证数据的顺序性和可靠性。

    十、UDP和TCP可以使用同一端口号吗?

    可以的。
    在这里插入图片描述
    多个TCP服务进程可以绑定同一个端口号吗?
    如果两个 TCP 服务进程同时绑定的 IP 地址和端口都相同,那么执行 bind() 时候就会出错,错误是“Address already in use”。

    客户端的端口号可以重复使用吗?
    TCP 连接是由四元组(源IP地址,源端口,目的IP地址,目的端口)唯一确认的,那么只要四元组中其中一个元素发生了变化,那么就表示不同的 TCP 连接的。所以如果客户端已使用端口 64992 与服务端 A 建立了连接,那么客户端要与服务端 B 建立连接,还是可以使用端口 64992 的,因为内核是通过四元祖信息来定位一个 TCP 连接的,并不会因为客户端的端口号相同,而导致连接冲突的问题。

    多个客户端可以 bind 同一个端口吗?
    要看多个客户端绑定的 IP + PORT 是否都相同,如果都是相同的,那么在执行 bind() 时候就会出错,错误是“Address already in use”。

    如果一个绑定在 192.168.1.100:6666,一个绑定在 192.168.1.200:6666,因为 IP 不相同,所以执行 bind() 的时候,能正常绑定。

    所以, 如果多个客户端同时绑定的 IP 地址和端口都是相同的,那么执行 bind() 时候就会出错,错误是“Address already in use”。

    一般而言,客户端不建议使用 bind 函数,应该交由 connect 函数来选择端口会比较好,因为客户端的端口通常都没什么意义。

    十一、服务端只bind了IP和port,没有进行listen这个socket监听连接,客户端向服务端发送socket数据,会怎样?

    服务端如果只 bind 了 IP 地址和端口,而没有调用 listen 的话,然后客户端对服务端发起了连接建立,服务端会回 RST 报文。
    参照源码TCP报文入口函数tcp_v4_rcv():
    1)调用__inet_lookup_skb函数查找此报文的的socket;

    int tcp_v4_rcv(struct sk_buff *skb)
    {
     ...
      
     sk = __inet_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
     if (!sk)
      goto no_tcp_socket;
     ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2)__inet_lookup_skb函数首先查找连接建立状态socket,找不到的话查找监听套接字接口;
    在这里插入图片描述3)找不到监听改端口的socket,会RST;
    在这里插入图片描述

    十二、TCP四次挥手可以变为三次吗?

    可以。
    延迟确认机制。
    在这里插入图片描述
    最长/最短延时确认时间,开启/关闭TCP快速确认参数TCP_QUICKACK
    在这里插入图片描述

    // 1 表示开启 TCP_QUICKACK,即关闭 TCP 延迟确认机制
    int value = 1;
    setsockopt(socketfd, IPPROTO_TCP, TCP_QUICKACK, (char*)& value, sizeof(int));
    
    • 1
    • 2
    • 3

    十三‘、客户端收到第二次握手的ACK不是自己期望收到的,会怎么处理?

    会回复RST。
    三次握手是为了避免旧的历史连接被初始化。
    在这里插入图片描述

    就是这事,散会!

  • 相关阅读:
    【Android】2、Android开发进阶超详细介绍
    解决 Failed to load class “org.slf4j.impl.StaticLoggerBinder“
    ATF源码篇(二):docs文件夹
    【Linux】进程间通信3——system V共享内存
    为云环境开发的 RADIUS 认证服务
    腾讯大佬手码的 K8S+Jenkins 笔记,太强了
    Kubectl 使用详解——k8s陈述式资源管理
    c++ 智能指针使用注意事项及解决方案
    HPC技术:MPICH源码分析
    TiniXml C++ 开源代码中的几个概念
  • 原文地址:https://blog.csdn.net/peng_shakalaka/article/details/127831052