新闻详情

新闻详情

首页 / 资讯中心 / 详情

TCP结束

发布时间:2026/9/4 6:41:52来源:尧图网络
TCP结束
连接管理机制在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接TCP 状态本质就是内核里的整数。每一条 TCP 连接内核都要用结构体struct Link保存信息建立连接消耗时间 内存空间所以tcp比udp更复杂要建立连接。细节TCP 状态本质就是内核里的整数。每一条 TCP 连接内核都要用结构体struct Link保存信息建立连接消耗时间 内存空间所以tcp比udp更复杂要建立连接。三次握手客户端socket()→connect()connect系统调用触发 OS 内核自动发送 SYN发起三次握手用户态connect阻塞等待握手完成。 客户端状态CLOSED → SYN_SENT → ESTABLISHED握手成功后connect返回。服务端socket()→bind()→listen()listen 之后 fd 变成监听套接字状态LISTEN。内核在操作系统内核内部完成三次握手收到 SYN、回复 SYNACK、收到 ACK。accept()不参与三次握手accept 只是从内核已经完成握手的半连接队列里取出一条建好的连接返回新的通信套接字 connfd。服务端状态LISTEN → SYN_RCVD → ESTABLISHEDaccept返回之后应用层拿到 connfd。重点三次握手全部由操作系统内核自动完成connect/accept只是用户层接口。为什么 TCP 要三次握手3次握手干了什么① 验证双方的发送、接收能力全双工第一次 C→S (SYN)客户端喊我要连你 服务端知道客户端发得过来我能收到第二次 S→C (SYNACK)服务端回收到我也想连你客户端知道双向都通了。 但服务端不知道自己发的回复客户端收到没有。第三次 C→S (ACK)客户端再回一声收到你的消息服务端这下才确认双向通路没问题连接正式建立前两次握手结束只有客户端确认双向通路没问题服务端还不知道自己发出去的包客户端收到没有直到服务端收到第三次的 ACK服务端才确认整条双向链路完整可用才进入 ESTABLISHED。TCP 是全双工双方都要确认对方收发正常不能只一方确认。②最小成本确认双方通信意愿4次握手也可以但SYN 和 ACK 可以合并成一个报文SYNACK即可以携带应答合并之后就变成 3 次用最小开销完成全部目标所以不需要 4 次。③ 同步双方初始序列号 ISN为可靠传输做准备主要防止防止网络延迟的旧 SYN 报文ISN 每个连接不一样随机、随时间增长用来防止旧延迟报文干扰新连接。ISN 是起跑线连接建立完成后后续报文 seq 在 ISN 基础上持续递增SYN 报文种就携带双方各自的初始序列号 ISN握手时就确定好。客户端告诉服务端我接下来数据用这个序号开始。服务端告诉客户端我接下来数据用这个序号开始。TCP 靠序列号做确认、重传、去重、按序。必须双方互相交换初始序列号不能只单向交换。eg客户端 ISN_c 100 服务端 ISN_s 500客户端 → SYNseq100以客户端 ISN开始服务端 → SYN‑ACKseq500以服务端 ISN开始ack101客户端 → ACKseq101SYN吃了1个序列号所以100被用了就从101开始ack501ack 对端 seq NSYN、FIN 各吃 1 个序号所以N1数据吃字节数个序号N为多少取决于字节个数光秃秃的 ACK啥也不吃N0。回到旧报文问题现在客户端新连接 ISN200。 网络飘来一个旧延迟 SYN 报文它的 seq100老连接的 ISN。 服务端收到旧 SYN回复 SYN‑ACKack 1001 101。 客户端收到这个 SYN‑ACK看 ack101但我现在这条连接的 ISN 是 200期待 ack 是 201。 不匹配 → 判断这是旧报文回复 RST。服务端不会保存历史所有已经关闭连接的 ISN 序列号所以收到旧SYN只能交给客户端判断四次挥手断开连接正常四次挥手完整流程客户端调用 close (fd)读写都关闭了→ 发FIN报文 客户端状态FIN_WAIT_1含义客户端不再发送数据但还可以收数据。我这边发完了要断开。怎么理解网络层面tcp的接受缓冲区确实还能收到数据但是fd已经关闭了不可能调用 read () 把缓冲区的数据读走只是能收而已2.服务端收到 FIN → 返回ACK应答 服务端状态CLOSE_WAIT客户端收到 ACK状态变成FIN_WAIT_2因为客户端已经close了不会再向客户端写了呀所以服务端read返回0读端自己就关闭了但同样服务端还可以继续给客户端发数据3.等服务端自己的数据全部发送完毕服务端调用 close (connfd)发出自己的FIN报文服务端状态LAST_ACK如果服务端代码不调用 close (connfd)服务端就卡在CLOSE_WAIT不动 文件描述符 connfd 不会释放一直被占用 →fd 泄漏文件描述符泄露。4.客户端收到服务端 FIN回复ACK客户端进入TIME_WAIT等待2MSL时间之后变为CLOSED。服务端收到这个 ACK直接变成CLOSED连接彻底结束现在的问题是对方已经发过来数据还留在我方内核tcp的接收缓冲区但是我方调用了close(fd)读不上来了这些数据怎么办调用 close (fd)引用计数为 0直接丢弃接收缓冲区里还没被应用 read 的剩余数据。没有任何办法再读到。所以想要把缓冲区剩余数据读完就不能用 close要用半关闭shutdown(SHUT_WR)只选择关闭读写某一方理解TIME_WAIT又为什么是2MSL谁主动关闭谁就进入 TIME_WAIT。MSLMaximum Segment Lifetime报文最大生存时间一个 TCP 报文在互联网里面最多能活多久。超过这个时间路由器就会把这个报文丢弃。Linux 默认一般 MSL30s所以 2MSL 就是 60 秒。TIME_WAIT 是主动关闭连接的那一方发送完最后一个 ACK 之后进入的状态要等满 2MSL 才彻底 CLOSED。原因 1保证最后那个 ACK 报文可靠到达对端场景四次挥手最后一步客户端主动关闭方发送最后的 ACK给服务端。客户端进入 TIME_WAIT。最坏情况这个 ACK 在网络丢包了服务端收不到 ACK停留在LAST_ACK超时后会重新发送 FIN 报文。如果客户端已经关闭没有 TIME_WAIT直接 CLOSED进程、连接内核数据都没了。收到服务端重发过来的 FIN只能回复 RST 重置。服务端直接报错连接异常断开。如果客户端还处在TIME_WAIT内核这个连接的信息还保留着。收到服务端重传的 FIN内核自动重新发出 ACK不需要应用程序参与。TIME_WAIT 期间内核还保存这条连接四元组信息用来应对对端重发 FIN。那为什么是2 倍MSL1 个 MSL足够让我方发出去的 ACK 走完到服务端。如果 ACK 丢了服务端重发 FIN这个 FIN 最多再花 1 个 MSL 传回到我方。一来一回最坏情况合计2*MSL原因 2让网络里旧的、延迟滞留的报文全部消失防止旧报文干扰新连接假设没有 TIME_WAIT客户端发完最后 ACK 立刻 CLOSED。马上复用同样的四元组源 IP 源端口目的 IP 目的端口建立一条全新 TCP 连接。但是网络中还飘着上一条旧连接迟到的数据包。这些延迟很久的旧报文姗姗来迟到达服务端。新连接会错误接收属于旧连接的残留数据造成数据错乱等待2MSL 时长能保证上一次连接所有方向上网络里残留的迟到报文全部都因为超过 MSL 被路由器丢掉。等 2MSL 过完旧报文全部死光再允许使用相同四元组建立新连接就不会被老报文骚扰。这一点可以和为什么要3次握手的第3条原因关联起来“③ 同步双方初始序列号 ISN为可靠传输做准备主要防止防止网络延迟的旧 SYN 报文”都是为了保证tcp的可靠传输解决TIME_WAIT状态引起的bind失败的方法现在我们就能解释为什么断开连接后立刻再去bind同一个端口号会失败的原因了因为当服务端主动关闭连接服务端就会进入TIME_WAIT状态而此时内核认为该端口还被 TIME_WAIT 的连接占用2MSL 之内就不让 bind。避免旧报文干扰只有2MSL过了进入CLOSED才算真正的释放了该端口才能再去bind高并发短连接场景服务器主动关闭连接踢空闲客户端服务器成为主动关闭方于是服务器产生大量TIME_WAIT当新客户端连接的五元组和 TIME_WAIT 旧连接冲突就会出问题造成bind绑定端口失败客户端主动关客户端 TIME_WAITTIME_WAIT 占用的是客户端自己的随机端口OS分的不会占用服务端的固定端口不影响服务端 bind。服务端主动关服务端 TIME_WAIT占用服务端固定端口会 bind 失败。所以为了解决这个问题就得采用下面的方式SO_REUSEADDRint opt 1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt));开启SO_REUSEADDR 1允许 bind 绑定处于 TIME_WAIT 状态的端口使用顺序setsockopt →bind() → listen()。如果 bind 之后再 setsockopt完全无效。额外补充一句如果能立刻重新bind了会不会又有旧报文的问题呢这时就可以根据序列号即为什么要3次握手的第3个原因来判断当然也还有许多其它方法比如TCP 时间戳选项TCP 滑动窗口流量控制send/write只是把数据拷贝到内核发送缓冲区一次能发多少数据不是应用决定由滑动窗口决定滑动窗口属于发送缓冲区内部的一块区间用 [start, end) 标记。start已发送并且收到确认的下标end窗口末尾end‑start 当前滑动窗口大小窗口含义不需要等待 ACK可以直接发出去的数据最大字节数窗口外面的不能发必须等 ACK 回来滑动窗口不需要等待 ACK 是什么意思正常停等协议 发 1 段数据 →停下来死等对方 ACK 应答→ ACK 回来才能发下一段。 每发一份就要等回复效率很低。TCP 滑动窗口窗口里的一批数据不用等前一段的 ACK 回来就可以连续把窗口内全部数据一股脑发出去一次发多个。滑动窗口是内核发送缓冲区里面的一段区间不是应用内存。发送方一次能发多少数据由什么决定接收窗口 rwnd对端告诉我的接收方把自己接收缓冲区剩余空间放到 TCP 头部window字段通过 ACK 告诉发送方。流量控制防止把接收方缓冲区打爆。滑动窗口大小不能超过对方的接收窗口。拥塞窗口 cwnd发送方自己算发送方根据网络情况自己计算防止网络拥堵。发送窗口 min(rwnd(对方接收窗口), cwnd(拥塞窗口))滑动窗口能不能向左滑动不能只会向右滑动。start指针只增不减ACK 到来只能往右挪不会回退向左。窗口只能前进不能后退窗口大小可以变大、变小、变为 0 吗可以。窗口大小由对端rwnd接收窗口、拥塞窗口cwnd共同决定变大对方接收缓冲区空闲变多 / 网络变好变小对方接收缓冲区剩余变少 / 网络拥塞可以等于 0对方接收缓冲区满rwnd0发送方停止发送报文丢失三种场景重点确认号是累计确认确认号 N代表N 之前全部字节都收到不会跳过中间报文做确认。窗口不会跳跃前进。最左侧报文丢失最早发的报文丢了窗口[1‑10001001‑20002001‑30003001‑4000]最左侧报文1‑1000它丢了分两种完全不一样场景场景 A数据报文 1‑1000 直接丢在网络里数据包丢失2.场景 B数据报文 1‑1000 成功到达接收方但是它对应的 ACK 应答在网络丢了ACK 丢失场景 A最左侧数据报文丢了数据包丢失快重传出场1‑1000 报文网络丢失但是后面1001‑2000、2001‑3000、3001‑4000全部顺利到达接收 B。TCP 是累计确认虽然后面分片收到但 1‑1000 没收到B 只能一直回复ACK1001。发送方 A 连续收到3 次重复的 ACK1001→快速重传不等超时定时器立刻重传 1‑1000。没有重传之前窗口不会移动重传的 1‑1000 到达 B此时 B 接收缓冲区已经缓存好后面1001‑4000的数据。B 一次性返回ACK5001窗口整体向右大步滑动场景 B数据报文正常到达ACK 丢了ACK 报文丢失不需要重传1‑1000 数据成功给到 BB 回复ACK1001但是这个 ACK 在半路丢了。 后面1001‑2000、2001‑30003000-4000陆续继续发给 B。 B 整体共收到了1‑4000回复新的ACK4001。A 虽然没有收到旧的ACK1001但是收到后面新的ACK4001。累计确认特性ACK4001 就代表 1‑4000 全部收到发送方直接滑动窗口不需要重传任何报文。中间报文和最右层报文都会随着start的向右移动成为“最左端”所以情况都是一样的快速重传 vs 超时重传2者底层都是滑动窗口超时重传定时器超时还没收到 ACK → 重传最左侧未确认报文。慢。快速重传收到3 个重复 ACK不等定时器超时直接重传丢失报文提高效率。TCP 发送出去还没收到 ACK 的报文保存到哪里如何理解保存保存位置操作系统内核发送缓冲区。保存含义TCP 把报文发送到网络后不会立刻删除数据副本只要还没有收到对应的 ACK就必须在内核发送缓冲区保留该数据用于超时重传 / 快速重传。收到 ACK 之后才删除这份副本释放缓冲区内存前面讲过和滑动窗口的关系[start ~ end)这一段就是已经发出去但还没收到 ACK、被保存待确认的数据。ACK 回来start右移这部分数据就可以丢弃缓冲区空间回收。没收到 ACK这份数据就一直保留在内核发送缓冲区。滑动窗口一直向右会不会溢出环形缓冲区不会溢出。物理内存是普通线性数组但逻辑上当做环形缓冲区用取模%运算管理指针。流量控制最后补充发送方收到窗口 0立刻停止发送数据等到接受缓冲区有空间了再发但是不能完全干等呀这时就有2种方法1.窗口更新通知接收方后续缓冲区腾出空间主动发送窗口更新报文2.窗口探测发送方收到零窗口后会周期性发送窗口探测报文目的逼接收方回复 ACKACK 头部带上最新的接收窗口大小。16 位窗口字段的局限 窗口扩大选项TCP 头部窗口占16bit理论最大只能表示 65535 字节。 现代高速网络需要更大窗口靠窗口扩大选项 Mshift count。实际窗口大小 窗口字段值 M左移 M 位等价乘以\(2^M\)M 在三次握手阶段协商连接建立之后不能修改。拥塞控制虽然 TCP 有了滑动窗口这个大杀器能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据仍然可能引发问题。因为网络上有很多的计算机可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下贸然发送大量的数据是很有可能引起雪上加霜的。多个客户端 C、多个服务器 S 共用同一个网络很多主机共享同一条网络链路 网络一旦拥堵会有大量主机同时发生丢包每台机器都感知到 “我报文丢了”。所以拥堵控制是每个发送主机统一遵守一套策略不仅仅是我这一台主机TCP 引入慢启动机制先发少量的数据探探路摸清当前的网络拥塞状态再决定按照多大的速度传输数据TCP 拥塞控制图讲解cwnd拥塞窗口值ssthresh慢启动阈值区分慢启动 / 拥塞避免的分界线RTTRound‑Trip Time往返时间一个报文从发送方发出去到收到对方确认 ACK 的整个来回时间。RTO 设置的超时闹钟。慢启动指数增长刚建立连接发送方完全不知道网络负载情况。如果一开始就猛发大量数据直接把路由器队列打满直接拥塞崩溃可以结合刚刚从cwnd1开始即前面已经拥塞一次了cwnd又从1开始开始慢一点给拥塞的报文更多处理时间保证网络慢慢恢复。先少量发送靠指数快速试探尽快摸到网络承受能力既不冒进又不会太慢。不是 “慢” 就一直慢是起步慢增长快。2、到达 ssthresh 之后切换拥塞避免加法增大指数增长涨得太猛如果继续翻倍很快就冲垮网络。改成每轮 RTT 只 1小心翼翼线性试探一点点压榨网络带宽避免一下子冲爆。指数增长慢开始快速往拥塞点靠近尽快摸到网络大概上限不想慢吞吞。线性增长拥塞避免离拥塞临界点很近了不敢翻倍猛冲就一小步一小步往上试探。两者目标一样找拥塞值3、超时判定拥塞时ssthresh 减半、cwnd 置 1重新慢启动超时 判定网络已经真的堵了路由器在大量丢包。如果继续维持大窗口发送会源源不断往拥堵的网络扔数据包雪上加霜。ssthresh cwnd/2记下本次大约的临界带宽以后不要轻易冲到这个值之上。cwnd置1彻底降速从头少量试探少发数据给网络留出时间消化积压报文指数增长开始慢。问题拥塞窗口在线性探测拥塞避免阶段会一直增大吗答案不会一直增大理想逻辑网络一直通畅没有丢包那 cwnd 确实会一轮轮 1持续上涨。现实网络网络带宽是有上限的。当cwnd涨超过网络承载能力就会出现丢包。TCP 延迟应答Delayed ACK延迟应答收到数据不立刻回 ACK稍微等一小会儿再回复 ACK。例子接收缓冲区总大小1M收到 500K 数据如果立刻回 ACK此时缓冲区还被占了 500K通告窗口 500K告诉发送方最多再发 500K。但上层程序处理很快10ms 就把这 500K 读完缓冲区又空回 1M。立刻应答就浪费了明明我能接收 1M却只告诉对方 500K发送方不敢多发吞吐量上不去。2.如果延迟等一会比如 200ms再发 ACK这时候 500K 已经被应用程序读走缓冲区空闲回到 1M。 回复 ACK 时通告窗口直接报 1M发送方可以发更多数据传输效率更高。核心思想等应用把缓冲区数据读走再回 ACK返回更大的接收窗口提升吞吐。但不能无限等有两个限制防止 ACK 迟迟不发导致超时重传数量限制每收到 N 个包必须应答一次一般 N2收到 2 个数据包不管怎样都要回复 ACK。时间限制最大延迟时间一般 200ms到时间就算只收到 1 个包也要回复 ACK。如果超时还不发 ACK发送方 RTO 计时器到期会误以为丢包触发不必要的超时重传。带来的好处减少 ACK 报文数量2 个数据报只回 1 个 ACK减少网络里面小报文。返回更大接收窗口提高传输效率。TCP 四种异常场景进程终止程序崩溃、exit 退出进程死掉操作系统回收所有文件描述符。 内核会自动给对端发送FIN 报文走正常四次挥手关闭连接。和正常关闭没有什么区别机器重启:和进程终止的情况相同关机之前操作系统要先关闭所有进程机器掉电 / 网线拔掉对方机器直接断电、网线拔了对方机器直接消失发不出任何报文FIN 都发不出来。本地这边的内核连接还挂着内核以为连接仍然存活。分两种情况本端要发数据发送报文对方彻底消失没有任何应答。多次超时重传失败内核给本端返回RST 复位。本端完全不发数据一直 idle 空闲此时什么都不会发生连接会一直挂在内核里。 需要依靠TCP 保活定时器keepalive 内核隔一段时间发探测报文试探对方还活着吗。 多次探测全部无响应内核判定对方死亡释放这条 TCP 连接关于keppaliveTCP 自带但几十分钟粒度太大业务一般不用。一般应用层根据自己的业务要求自己定时长。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

改进YOLOv8_seg实现路边非标停车位实例分割 2026/9/4 7:29:59

改进YOLOv8_seg实现路边非标停车位实例分割

简介:本资源是一套面向智能交通与城市治理领域的计算机视觉实践方案,专为解决印度等地区路边非标准停车位识别难题而设计,适用于具备PyTorch基础的算法工程师、智慧城市项目开发者及高校科研人员。系统基于改进YOLOv8_seg实例分割模型&#x…

阅读更多 →
C++量化交易引擎源码解析与实盘部署指南 2026/9/4 7:29:59

C++量化交易引擎源码解析与实盘部署指南

简介:这是一套面向计算机专业本科生与初学者的C/C量化投资交易平台源码,适用于毕业设计、课程设计及期末大作业等实践场景,聚焦金融工程与系统编程交叉领域,帮助学习者掌握行情接入、策略回测、订单执行等核心模块的底层实现逻辑。…

阅读更多 →
从DMA思想到高效数据管道:异步、缓冲与流控的工程实践 2026/9/4 7:29:59

从DMA思想到高效数据管道:异步、缓冲与流控的工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
MATLAB App Designer组件详解:从基础控件到高级布局与回调编程 2026/9/4 7:29:58

MATLAB App Designer组件详解:从基础控件到高级布局与回调编程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
STC89C52RC驱动0.96寸OLED屏:从SPI通信到图形显示实战 2026/9/4 7:29:58

STC89C52RC驱动0.96寸OLED屏:从SPI通信到图形显示实战

简介:本资源是一套面向嵌入式初学者与51单片机实践者的OLED显示驱动开发套件,聚焦STC98C52RC单片机通过SPI/I2C接口点亮并控制SSD1306等主流OLED屏的核心功能实现。资源提供完整可编译工程,含主控逻辑(main.c)、OLED底…

阅读更多 →
【基于 Swoole+Hyperf 的微服务实战】 第三周·周六综合实战日 2026/9/4 7:26:58

【基于 Swoole+Hyperf 的微服务实战】 第三周·周六综合实战日

【基于 SwooleHyperf 的微服务实战】 第三周周六综合实战日今天我们进入第三周周六综合实战日。这一周我们从微服务拆分、JSON-RPC 通信,到负载均衡、熔断降级,搭建了完整的服务间调用体系。今天我们将通过一个实战项目——“微服务订单系统”——把本周…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞