TCP问题总结

本文围绕TCP三次握手和四次挥手展开,介绍了ACK数据包序列号消耗、握手能否带应用层数据等问题。还探讨了服务端CLOSE_WAIT状态成因、SYN报文丢弃情况,以及确认应答机制、快重传、SACK选择性确认等内容,最后分析拔掉网线后TCP连接状态。

三次握手

TCP三次握手四次挥手过程

在这里插入图片描述

ACK数据包消耗TCP的序列号吗

单纯的ACK包不消耗,因为如果消耗序列号,那么对端也需要对这个ACK包进行确认,那么网络中就全是ACK了
如果是捎带应答,psh包和ACK包在一起,就会消耗序列号

TCP三次握手可以携带应用层数据吗

  • 前两次不可以,因为此时客户端服务端都没有进入连接状态,第三次可以携带数据,但是很少有这么做的。
  • 如果前两次可以携带数据,那么攻击者就可以对服务器进行连接在第一次握手时,发送大量的数据,完全不管服务器能否接收
  • TCP的拥塞控制,会在连接初始阶段进行慢启动,试探网络状况,因此一般都不会在第三次握手携带数据

服务端不进行accept,最多可以三次握手完成多少次连接

在这里插入图片描述
在这里插入图片描述

  • 三次握手都是由内核完成,而accept是从已完成连接队列中获取连接,如果服务端一直不accept,那么能完成的连接总数就是backlog + 1,已完成连接队列的容量

tcp三次握手的序号是从0开始的吗

  • 都不一定是从0开始的,而是一个32位计数器,动态变化的
  • 序号的作用:为了防止在网络中被延迟的分组后又被传送到,从而导致连接一方做出错误的解释,
  • 如果从0开始,当一个连接完成后,序号从0开始,第一个消息发送序号是1,此时1号包在网络中阻塞了,同时服务端或者客户端掉线了断开了,之后重新连接重新开始通信后,之前发送的1号包又重新到达,发生错误

tcp最多创建多少连接

  • 即一个进程最多打开多少文件描述符
  • 通过ulimit -a 查看open files,默认是65535或者1024 需要去掉标准输入标准输出标准错误+监听套接字
  • ulimit -n nums 进行修改为nums个

tcp为什么需要MSS

tcp规定一次性最多发送的数据大小就是MSS字节,以客户端和服务端的最小的那个MSS为准

  1. 为了防止数据包过大,因为ip不可靠,防止重传大数据包
  2. MSS + tcp头部+ ip头部<=MTU, MTU是最大传输单元,ip一个数据帧的最大值
  3. 数据小于MSS ip就一定不会分片,如果分片了,丢失的

四次挥手

服务端出现大量的CLOSE_WAIT状态

  • CLOSE_WAIT状态是被动断开方接受到对方的fin报文后发出ack后的状态,服务端发送完所有数据后,发送fin包后变成LAST_ACK状态(调用close())
    1. 可能是服务端的线程阻塞无法调用close
    2. 在close之前有大量消耗时间的逻辑

其他

SYN报文什么时候会被丢弃

可能性1. TCP的半连接队列已满(半连接队列和全连接队列也叫未完成连接队列和已完成连接队列)
详细原因:
- 服务端收到客户端的SYN请求后,内核将连接存储到半连接队列,并向客户端相应SYN+ACK
- 客户端返回ACK后,将连接加入到全连接队列
- 服务端调用accept后,将连接从全连接队列中取出
- 当半连接队列满了,那么SYN就会丢弃
解决办法:
+ 增大半连接队列以及全连接队列
+ 开启tcp_syncookies功能
在第二次握手时,向对方发送一个cookies,对方回复ack后校验cookies来看连接是否合法
让连接在第二次握手的时候不进入半连接队列,通过校验cookies后直接进入全连接队列
/proc/sys/net/ipv4/tcp_syncookies 0是关闭 1是当半连接队列满时启用 2是无条件开启
+ 减少SYN+ACK重传次数
syn泛洪攻击,攻击者发送SYN后不发送ACK,让这个连接一直占用半连接队列,服务端默认的超时重传5次ACK+SYN,这个时间内连接一直占用半连接队列,通过设置重传次数减少占用时间
+ 检查代码为什么不调用accept,当全连接队列满了之后,完成三次握手的连接也会继续占用半连接队列,因此也可以通过

可能性 2. 开启tcp_tw_recycle参数,且在NAt环境下,tcp_tw_recycle+NAT是不安全的

  1. linux下TIME_WAIT时间是60s,即使主动断开连接的那个进程退出了,被占用的那个端口还在被占用。
  2. 客户端连接可以不通过自己bind,通过connect函数,操作系统分配端口,一般可分配的端口是32768-61000,如果是客户端发起了大量连接,同时这些连接都主动断开,如果客户端此时没有端口了,但是还想发起连接咋办,tcp_tw_recycle参数就起作用了
    1. tcp_tw_recycle可以让客户端的time_wait快速回收,还有一个tcp_tw_reuse选项,这个选项开启的话,是如果客户端主动发起连接(connect)发现端口是被相同的四元组占用,且处于timewait状态,time_wait状态持续超过1s就会重新启用这个连接,开启这两个选项需要开启时间戳选项
    2. 私有ip是无法直接访问互联网的,私网ip是为了解决ipv4地址枯竭问题的,不同的真实client经过同一个NAT服务器转变为同一个公网ip再去访问互联网,在服务端看来这都是和同一个host打交道,但是不同的真实client有自己的时间戳,无法保证时间戳严格递增,那么服务器的PAWS机制(防止序号回环的机制)被触发后就会丢弃时间戳不符合的数据包,例如当先到了一个时间戳1-10的数据包,后来又到了个8的时间戳,此时服务端就会丢弃此数据包
      在这里插入图片描述

什么是确认应答机制

  • 本质是对序列号的确认,tcp对每个字节的数据都进行了编号,数据接收到1~1000,ack回复确认1001,告诉对方我需要1001的数据,

什么是快重传

  • 区别于拥塞控制的快速重传
  • 快重传是为了在没有触发超时重传的时候,就立即重传丢失的数据,本质是为了提升传输效率
    发送方接收到三次同样的确认之后,会立即重传丢失的报文,
    那么为什么是三次呢?

如果发送发已经发送了数据包,N-1,N,N+1,N+2
此时收到一次ack(N),可以认为是N还未到
收到第二次ack(N),可以认为是N+1先于N到达,此时可能是网络乱序
收到第三次ack(N),此时可能是N+2先于N到达,或者是N丢包了,此时后者的概率会比较的大,所以此时触发快重传是合理的

快重传的漏洞就是,不知道是重传一个N,还是重传后面的多个包比如:

  1. 发送方发送了1-10号包,此时2,3,4都丢失了
  2. 此时收到三次ack(2),但是快重传只会重传2,不能一次重传2-4
  3. 通过SACK和DSACK解决

SACK 选择性确认

在三次握手时协商双方是否都支持SACK,协商成功后在tcp头部添加SACK字段,将已经收到的数据信息发送给对方

拔掉网线后tcp连接还在吗

  • tcp连接并不是实体的,是通信双方各自维护的一个struct socket,包含对方的ip。端口等信息,拔掉网线之后(也可以是中间的路由器断掉),这个结构体依然存在,因此可以认为:
  1. 如果有数据传输,触发超时重传上限
    1. 如果超时重传上限之前,网线重连了,对程序员是无感的
    2. 触发上限了, tcp通知上层应用程序连接断开,程序员根据通知继续后续的工作
  2. 如果没有超时重传,触发心跳机制
    1. tcp keepalive机制,当空闲超过两个小时,每隔75s发送一次探测报文,探测9次若9次都没有收到ack,则认为连接断开了,这个机制需要设置tcp keepalive
### TCP 常见问题及解决方案汇总 #### 1. **TCP 连接状态保持问题** 在没有数据传输的情况下,如果双方都没有开启 TCP keepalive 机制,客户端拔掉网线后,即使客户端一直插回网线,客户端和服务端的 TCP 连接状态仍会保持存在[^3]。这可能导致资源浪费或连接假象。 **解决方案**: - 开启 TCP keepalive 机制,通过定期发送探测包来检测对方是否存活。如果探测失败,则断开连接[^3]。 - 配置操作系统层面的 keepalive 参数,如 `tcp_keepalive_time`、`tcp_keepalive_intvl` 和 `tcp_keepalive_probes`,以调整探测频率和超时时间。 #### 2. **TCP 拥塞控制问题** 当网络状况较差时,所有连接无脑重传会导致网络更加拥堵。因此需要拥塞控制来避免这种情况的发生[^2]。 **解决方案**: - 使用 TCP 的拥塞控制算法(如慢启动、拥塞避免、快速重传和快速恢复)来动态调整发送窗口大小,从而优化网络性能[^2]。 - 在应用层实现自适应重传策略,根据网络延迟和丢包率调整重传频率。 #### 3. **TCP 粘包与拆包问题** TCP 是面向流的协议,底层并了解上层业务数据的具体含义,可能会将一个完整的包拆分成多个包发送,或者将多个小包封装成一个大包发送,这就是所谓的 TCP 粘包和拆包问题[^4]。 **解决方案**: - 在应用层定义消息边界,例如通过固定长度的消息头或特殊字符分隔符来标识消息的开始和结束[^4]。 - 使用序列化协议(如 Protobuf 或 JSON)明确消息结构,避免因粘包或拆包导致的数据解析错误。 #### 4. **TCP 超时与重传问题** 在网络质量较差的情况下,可能出现大量 TCP Retransmission 或 Segment Lost,甚至出现 TCP Out-Of-Order 的情况。这通常是因为网络路径上的丢包或负载均衡架构导致封包走同路径而引发的问题[^5]。 **解决方案**: - 调整 TCP 的超时重传参数(如 `tcp_retries2` 和 `tcp_fin_timeout`),以适应同的网络环境[^5]。 - 在网络架构中使用更可靠的负载均衡策略,确保同一连接的封包尽量走相同的路径。 #### 5. **TCP 连接关闭问题** 在实际应用中,可能会遇到服务端或客户端异常关闭连接的情况,导致对端无法及时感知连接状态。 **解决方案**: - 使用优雅关闭机制(如 `FIN` 和 `ACK` 握手)确保双方都能正确感知连接关闭状态。 - 在应用层实现心跳机制,定期检测连接状态,及时发现并处理异常关闭。 ```python # 示例代码:设置 TCP keepalive 参数 import socket def enable_tcp_keepalive(sock): sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # 启用 keepalive sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) # 设置空闲时间 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) # 设置探测间隔 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) # 设置探测次数 ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值