实用TCP协议(1):TCP 协议简介
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 协议假设下层协议可以提供简单的不可靠数据报, 并在此基础上构建可靠的端到端字节流服务。TCP 协议通常工作在 IP 协议上,依赖 IP 协议提供的地址和路由机制。
本文将介绍 TCP 协议的握手、挥手、流量控制、拥塞控制等基本机制。
TCP 包结构

- 发送方端口
- 接收方端口
- 序列号(SEQ)
- 确认号码(Acknowledge Number):设置了 ACK 标志位后有效,表示期待要收到下一个数据包的 SEQ
- 资料偏移(offset): 表示数据段开始位置相对于 TCP 数据包开头的偏移量,也是 TCP Header 的长度
- 保留位: 目前不使用
- 标志位(Flag): 一共有 9bit, 对应位置1表示标志位有效
- ACK: 表示确认收到了发送方发送的数据, ACK=1 时 TCP Header 中的 ACK Number 字段有效。
- PSH: 优先推送。接收方 TCP 应该尽快推送给接收应用程序,而不用等到 TCP 缓存填满后再交付
- RST: 重置连接。表示 TCP 连接中出现严重错误,需要释放并重新建立连接。
- SYN: 表示请求建立连接,SYN 意为同步(synchronize), 即请求同步序列号。
- FIN: 表示此报文段的发送方的数据已经发送完毕,并要求释放 TCP 连接
- 校验和: 根据 TCP 包的头部和数据段计算的校验和,用于保证传输完整无误
IP 协议的数据包大小有限、不保证送达也不保证送达的顺序,如果需要发送大量数据就必须分为多个数据包。发送方 会为自己的每个 TCP 数据包分配一个序列号sequence number,SEQ)。
接收方收到数据包后会按照 SEQ 将数据包去重并排序,然后接收方对已成功收到的包发回一个相应的确认包(ACK)。如果发送方在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失并进行重传。
acknowledge number 表示期望收到的下一个数据包的序列号,换句话说 acknowledge number 之前的数据包已经全部收到。这种确认方法称为累积确认。累积确认在丢包时效率很低,假设通过10个分组发出了1万个字节的数据。如果第一个分组丢失,在纯粹的累计确认协议下,接收方不能说它成功收到了1,000到9,999字节,但未收到包含0到999字节的第一个分组。因而,发送方可能必须重传所有1万个字节。
因此,RFC 2018 中引入了选择确认机制(selective acknowledgment,SACK),允许接收方向发送方返回多个 SACK block, 每个 SACK block 表示一段已经成功收到的连续范围的开始与结束字节序号。
三次握手
TCP 连接建立过程需要发送三个 TCP 包,这个过程被称为三次握手:
- 服务端 bind 端口并开始 listen
- 客户端调用 connect 开始建立连接:客户端发送 SYN 包并带上初始序列号, 并进入 SYN_SENT 状态
- 服务端发送 ACK 确认收到了客户端的SYN, 并且发送自己的 SYN 以及自己的初始序列号,并进入 SYN_RCVD 状态。 这里的ACK 和 SYN 是在同一个 TCP 包中发送的
- 客户端确认服务端的SYN。 至此双方都获得了对方的序列号,连接成功建立

四次挥手
TCP 连接断开过程需要发送四个 TCP 包, 这个过程被称为四次挥手:
- 主动方调用 close 开始关闭连接(客户端和服务端都可以主动断开连接, 下图以客户端主动断开为例): 主动方发送 FIN 包并进入 FIN_WAIT_1,表示己方数据发送完成。此后主动方还可以继续接收数据,但是无法继续发送数据。
- 被动方对 FIN 包发送 ACK 并进入 CLOSE_WAIT 状态。此状态下,被动方可以继续发送数据。
- 被动方数据发送完成,调用 close 发送 FIN 包, 并进入 LAST_ACK 状态等待对 FIN 包的 ACK.
- 主动方对 FIN 包发送 ACK 并进入 TIME_WAIT 状态, 在此状态等待 2 MSL 后连接关闭
- 被动方收到 ACK 后连接关闭

在握手过程中服务端可以将 ACK 和 SYN 在同一个包中发送, 因此握手过程中的两对 SYN-ACK 只需要三次传输即可。在挥手过程中,被动方收到主动方的 FIN 包后可能仍有数据需要发送,所以不能将 FIN 和 ACK 在同一个包中发出使得挥手过程必须要经过四次传输。
TIME WAIT
上文中提到的 MSL 是指 Max Segment Lifetime,它是一个 TCP 包在网络中最大的生存时间超过 MSL 的 TCP 包会被丢弃,MSL 的推荐值为两分钟。
被动方在收到 LAST ACK 会一直尝试重传 FIN 包直到到达最大重试次数。 若主动方在 TIME WAIT 状态等待时间过短, 在收到重传的 FIN 包时连接已经关闭,则主动方会向被动方返回 RST,此时被动方会认为遇到了错误,因此无法正常关闭连接。
TIME_WAIT至少需要持续 2MSL 时长,这2个MSL中的第一个MSL是为了等主动方发出去的 LAST ACK从网络中消失,而第二MSL是为了等在被动方收到ACK之前的一刹那可能重传的FIN报文从网络中消失。
2MSL 并不能绝对保证属于本连接的 TCP 包在网络中消失,比如我们利用防火墙拦截主动方发送的所有 LAST ACK 包,那么被动方会一直重传 FIN 包。最后一个 FIN 包在网络中消失的时间只取决于被动方何时停止重传,与主动方 TIME WAIT 状态持续时间无关。
Linux 系统中 TIME_WAIT 的时间为固定的 60 秒,由内核代码里的 TCP_TIMEWAIT_LEN 宏定义, 只有重新编译内核才可以修改。
#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT state, about 60 seconds */
TIME WAIT 状态的连接会占用端口, 导致系统无法建立新的 TCP 连接。在本系列的后续文章中我们将介绍如何避免出现过多 TIME WAIT 状态的连接。
拥塞控制
发送数据时当然是越快越好,但是发送速度超过了网络的最大承载能力就会发生丢包。TCP 的目标是尽可能的利用网络承载能力,一方面不浪费带宽,另一方面尽量避免丢包。TCP 协议中控制如何合理利用网络的机制被称为拥塞控制,接下来我们来了解一下拥塞控制所涉及的四个算法:慢开始、拥塞避免、快重传和快恢复。
慢开始 - 拥塞避免
发送方维持一个叫做拥塞窗口 CWND(congestion window)的状态变量,当在网络中传输的数据量(未ACK的数据量)到达 CWND 时就暂停发送。
慢开始算法(SlowStart)将 CWND 的初始值设置的非常小,每一轮成功发送-确认都会使得 CWND 加倍,直到 CWND 达到慢开始算法的阈值 SSThresh 后转为拥塞避免。
慢开始算法的慢是指初始传输速度很慢,但是传输速度会以指数快速增长。
在达到 SSThresh 之后转为使用拥塞避免算法使 CWND 线性增长(加法增大), 避免继续快速增长导致网络拥塞。
无论是在慢开始阶段还是在拥塞避免阶段,只要发送方没有及时收到 ACK 都会判断为出现了网络拥塞。遇到网络拥塞后,发送方会把 SSThresh 设为当前 CWND 的一半, 把 CWND 设为初始值重新执行慢开始算法,这个操作称为“乘法减少”。
乘法减少做的目的就是要迅速减少发送到网络中的数据,使得发生拥塞的路由器有足够时间把队列中积压的数据处理完毕。

快重传
快重传(Fast Retransmit)
- 要求接收方每收到一个失序的报文段后就立即发出重复确认而不是等待自己发送数据时才捎带确认
- 发送方只要一连收到三个重复确认就立即重传对方尚未收到的报文段,而不必等待设置的重传计时器到期
快重传使得发送方迅速重传丢失的数据包减少等待时间。

快恢复
当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把 ssthresh 减半(为了预防网络发生拥塞), 但是接下来并不执行慢开始算法。
考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将 CWND 设置为ssthresh减半后的值,然后执行拥塞避免算法,使 CWND 缓慢增大。

TCP Reno 版本引入了快恢复与快重传机制
流量控制
若发送过快导致超出了接收方处理能力同样会导致丢包重传,因此我们需要控制发送方的发送速率避免发送速率超过接收方处理能力。对发送方发送速率的控制,我们称之为流量控制。
接收方会在返回的 ACK 包的 WIN 字段中告知自己接收窗口(Receiver Window, RWND) 大小, 发送方会取接收窗口 RWND 和拥塞窗口 CWND 中的最小值(min(CWND, RWND))作为自己的发送窗口,当未确认的数据量到达发送窗口规定的上限时便暂停发送。
当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果这个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。
为了避免流量控制引发的死锁,TCP使用了持续计时器。每当发送者收到一个零窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生。
实用TCP协议(1):TCP 协议简介的更多相关文章
- 计算机网络通信TCP/IP协议浅析 网络发展简介(二)
本文对计算机网络通信的原理进行简单的介绍 首先从网络协议分层的概念进行介绍,然后对TCP.IP协议族进行了概念讲解,然后对操作系统关于通信抽象模型进行了简单介绍,最后简单描述了socket 分层的 ...
- 软件开发架构,网络编程简介,OSI七层协议,TCP和UDP协议
软件开发架构 什么是软件开发架构 1.软件架构是一个系统的草图. 2.软件架构描述的对象是直接构成系统的抽象组件. 3.各个组件之间的连接则明确和相对细致地描述组件之间的通讯. 4.在实现阶段,这些抽 ...
- TCP/IP协议族(一) HTTP简介、请求方法与响应状态码
接下来想系统的回顾一下TCP/IP协议族的相关东西,当然这些东西大部分是在大学的时候学过的,但是那句话,基础的东西还是要不时的回顾回顾的.接下来的几篇博客都是关于TCP/IP协议族的,本篇博客就先简单 ...
- 网络编程简介(OSI七层协议,TCP协议原理,三次握手与四次挥手)
目录 网络编程 软件开发架构 C/S架构 B/S架构 网络编程的发展史 互联网协议 1.物理连接层 2.数据链路层 3.网络层 4.传输层 5.应用层 三次握手四次挥手 三次握手建链接 数据传输 四次 ...
- iOS网络协议 HTTP/TCP/IP浅析
一.TCP/IP协议 话说两台电脑要通讯就必须遵守共同的规则,就好比两个人要沟通就必须使用共同的语言一样.一个只懂英语的人,和一个只懂中文的人由于没有共同的语言(规则)就没办法沟通.两台电 ...
- 【TCP/IP 协议】 TCP/IP 基础
总结 : 通过学习 TCP/IP 基础, 并总结相关笔记 和 绘制思维导图 到博客上, 对 TCP/IP 框架有了大致了解, 之后开始详细学习数据链路层的各种细节协议, 并作出笔记; 博客地址 : h ...
- TCP协议和UDP协议基础介绍
TCP协议和UDP协议区别 标签(空格分隔): TCP,udp TCP的三次握手 TCP被称为可靠的数据传输协议,主要是通过许多机制来实现的其中最主要的就是三次握手的功能,当然,TCP传送数据的机制非 ...
- HTTP、TCP、IP、协议
HTTP(HyperText Transfer Protocol) 即超文本传输协议,现在基本上所有web项目都遵从HTTP协议(协议就是一种人为的规范). 目前绝大部分使用的都是HTTP/1.1版本 ...
- 网络协议之TCP
前言 近年来,随着信息技术的不断发展,各行各业也掀起了信息化浪潮,为了留住用户和吸引用户,各个企业力求为用户提供更好的信息服务,这也导致WEB性能优化成为了一个热点.据分析,网站速度越快,用户的黏性. ...
- TCP/IP协议与HTTP协议(二)
TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据. 1.TCP连接 手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过 ...
随机推荐
- 南屿 带你 走进 vue
### Vue > Vue是一个前端js框架,由尤雨溪开发,是个人项目 Vue近几年来特别的受关注,三年前的时候angularJS霸占前端JS框架市场很长时间,接着react框架横空出世,因为它 ...
- 【Vulnhub靶场】EMPIRE: BREAKOUT
环境准备 下载靶机,导入到vmware里面,这应该不用教了吧 开机可以看到,他已经给出了靶机的IP地址,就不用我们自己去探测了 攻击机IP地址为:192.168.2.15 靶机IP地址为:192.16 ...
- python02day
回顾 1.编译型和解释型 编译型:一次性编译成二进制,再执行 执行效率高,但不能跨平台,开发效率低 代表语言:C 解释型:逐行解释成二进制,再执行 可以跨平台,开发效率高,但执行效率低 代表语言:py ...
- SpringCloud整合Hystrix
1.Hystrix简介 Hystrix是由Nefflix开源的一个延迟和容错库,用于隔离访问远程系统.服务或第三方库,防止级联失败,从而提升系统的可用性.容错性与局部应用的弹性,是一个实现了超时机制和 ...
- Transformer可解释性:注意力机制注意到了什么?
原创作者 | FLPPED 论文: Self-Attention Attribution: Interpreting Information Interactions Inside Transform ...
- JDBC 操作预编译语句中LIKE模糊匹配怎么用
问题描述 在使用JDBC 预编译执行语句时,遇到一个问题,那就是在含有LIKE的查询语句时,我到底怎么使用匹配符%._呢. 如: SELECT * FROM "+LQ_USERS+" ...
- LAMP以及各组件的编译安装
LAMP以及各组件的编译安装 目录 LAMP以及各组件的编译安装 一.LAMP 1. LAMP概述 2. 各组件的主要作用 3. 平台环境的安装顺序 二.编译安装apache httpd 1. 关闭防 ...
- 一个好用的多方隐私求交算法库JasonCeng/MultipartyPSI-Pro
Github链接传送:JasonCeng/MultipartyPSI-Pro 大家好,我是阿创,这是我的第29篇原创文章. 今天是一篇纯技术性文章,希望对工程狮们有所帮助. 向大家推荐一个我最近改造的 ...
- VNCTF 2022 cm cm1 RE复现
cm1 安卓逆向 JEB 直接跟进主函数找到 ASSERT里面拿到ooo文件 直接脚本解密 k = "vn2022" with open('ooo', 'rb') as f: c ...
- PHP面试常考内容之面向对象(1)
PHP中面向对象常考的知识点有以下几点,我将会从以下几点进行详细介绍说明,帮助你更好的应对PHP面试常考的面向对象相关的知识点和考题. 整个面向对象文章的结构涉及的内容模块有: 一.面向对象与面向过程 ...