一、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;
...
}
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));
十三‘、客户端收到第二次握手的ACK不是自己期望收到的,会怎么处理?
会回复RST。
三次握手是为了避免旧的历史连接被初始化。
文章来源:https://www.toymoban.com/news/detail-430816.html
就是这事,散会!文章来源地址https://www.toymoban.com/news/detail-430816.html
到了这里,关于TCP常见问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!