
面向连接的概念:在自己监听的端口接收到连接的请求,然后经过“三次握手”,维护一定的数据结构和对方的信息,确认了该信息:我发的内容对方会接收,对方发的内容我也会接收,直到连接断开。
断开:经过“四次挥手”确保双方都知道且同意对方断开连接,然后在remove为对方维护的数据结构和信息,对方之后发送的包也不会接收,直到 再次连接。
TCP的连接不是建立了一座桥(或者虚拟的专用通道),TCP更好的比喻是在码头上增加了记录人员,核查人员和督导人员,至于IP层和数据链路层,它没有任何改造。
TCP 三次握手的过程如下(以下同时举例一个生活场景帮助理解):
过程解释:
- 第一次握手:客户端向服务器发送一个带有SYN(同步)标志位的数据包(就好像客户端向服务器说“你好,我想和你建立连接” ),同时还会选择一个初始序列号(seq),假设为x,此时客户端进入SYN_SENT(已发送同步状态)。
- 第二次握手:服务器收到客户端的SYN数据包后,知道客户端要建立连接。服务器向客户端返回一个带有SYN和ACK(确认)标志位的数据包(意思是“你好,我收到你建立连接请求了,我也同意建立连接” )。其中ACK的值为客户端发送的seq + 1(即x + 1)表示确认收到客户端的信息,服务器也选择一个自己的初始序列号(假设为y),此时服务器进入SYN_RCVD(同步收到状态)。
- 第三次握手:客户端收到服务器的SYN + ACK数据包后,检查ACK是否正确(确认是x + 1),如果正确则向服务器发送一个带有ACK标志位的数据包(意思是“好的,我知道你收到我的请求并且同意了,那我们连接建立吧” ),ACK的值为服务器的序列号y + 1 ,此时客户端和服务器都进入ESTABLISHED(已建立连接状态),连接建立成功,可以开始传输数据了。
举例(生活场景类比):
把客户端比作小明,服务器比作一个公司的前台。
- 第一次握手:小明打电话给公司(类比客户端发送SYN包)说“我想找你们公司谈个业务合作”(这是表达建立联系的意图),同时自己心里默默记了个数字(初始序列号x ,比如是100 ,就好像是自己这次沟通的一个标识)。
- 第二次握手:前台接到电话(服务器收到SYN),然后跟小明说(回复SYN + ACK包) “好的先生,我知道您要谈业务合作(确认收到请求),我们可以合作,我是前台(类比服务器的初始序列号y ,假设是200),您的请求编号我记好了是到您了(确认号是x + 1 = 101)”。
- 第三次握手:小明听到前台回复(收到SYN + ACK包),确认了前台确实知道自己的请求(检查ACK是101),然后说(发送ACK包)“好的,那我现在过来(表示确认建立连接),我知道你是前台(确认序列号y + 1 = 201)” ,然后双方都知道接下来可以进行业务沟通(数据传输)了。
以下是TCP中常见标志位的作用:
1. URG(紧急标志):
- 当设置为1时表明发送端向另一端使用紧急方式发送数据,即此报文段中有紧急数据。
- 紧急数据不按常规的排队顺序来传送(绕过接收方缓存机制),而是系统会尽快传送。发送方的TCP会把紧急数据放到本报文段数据的最前面,紧急指针指向数据段中的某个字节(从第一个字节到指针所指的字节就是紧急数据)。
2. ACK(确认标志):
- 当ACK = 1时表示确认号字段有效。
- 大多数情况下该标志位是置位的,除了最初建立连接时的SYN包之外,第一次请求建立连接时ack为0 (在三次握手过程中,第二次握手ACK标志置1 ),在连接建立之后传输数据等情况下,TCP规定该位必须设置为1。
- 接收端用确认号来给发送端反馈已经成功接收到的数据信息,其值为希望接收的下一个数据包起始序号,即意味着确认号之前的序号数据已经被接收。
3. PSH(推标志):
- 当设置为1时,指示接收方应该尽快将这个报文交给应用层。
- 表示需要将收到的数据立刻传给上层应用协议而无需等待缓冲区满等情况。在两个应用进程进行交互式通信时比较常用,例如客户发一个请求给服务器时希望立即能够收到对方的响应,客户应用程序可以通知TCP使用推送(push)操作。
4. RST(复位标志):
- 当设置为1时表示连接复位。
- 用于复位那些产生错误的连接(如因为主机崩溃或其他原因导致的错误情况);也被用来拒绝错误和非法的数据包。
- 比如当一个没有被使用的端口即使发来了连接请求,也可以返回一个RST设置为1的包;程序宕掉或切断电源等原因导致主机重启的情况下,如果对方还在通信,原有的TCP通信也无法继续进行,若对方发过来数据则可以用RST = 1强制断开连接。
5. SYN(同步标志):
- 在建立连接时使用,用于同步连接双方的序列号和确认应答号等信息。
- 最初建立连接时,客户端发送SYN = 1,ACK = 0的包请求连接,服务端收到后回复SYN = 1,ACK = 1的包表示响应连接请求,在三次握手过程中SYN标志总共会出现两次(双方各一次)。
6. FIN(结束标志):
- 当设置为1 时表示发送方完成任务,今后不会有数据发送,希望断开连接。
- 可以由通信双方的任一方发出,表示此方向的数据传输结束。当通信结束希望断开连接,通信双方的主机之间就可以相互交换FIN位置为1的TCP段,每个主机又对对方的FIN包进行确认应答以后就可以断开连接。
TCP 流量控制和拥塞控制
- 序号:解决乱序的问题,确保先来后到
- 确认序号:发出去的包应该有确认,要不然我怎么知道对方有没有收到呢?如果没有收到就应该重新发送,直到送达。这个可以解决不丢包的问题。
- 窗口大小(滑动窗口):TCP 要做流量控制,通信双方各声明一个窗口,标识自己当前能够的处理能力,别发送的太快,撑死我,也别发的太慢,饿死我。(针对接收方的缓存)
- 窗口大小(拥塞窗口):拥塞控制(控制网络中发送的包)TCP 发送包常被比喻为往一个水管里面灌水,而 TCP 的拥塞控制就是在不堵塞,不丢包的情况下,尽量发挥带宽。水管有粗细,网络有带宽,也即每秒钟能够发送多少数据;水管有长度,端到端有时延。在理想状态下,水管里面水的量 = 水管粗细 x 水管长度。对于到网络上,通道的容量 = 带宽 × 往返延迟。


TCP 建立连接采用三次握手而不是两次握手主要有以下几个关键原因:
一、防止已失效的连接请求报文突然又传送到服务器端而产生错误(针对特殊状况)
- 假设采用两次握手建立连接:
- 客户端发送一个连接请求报文(SYN)给服务器。
- 服务器收到这个请求后,返回一个确认报文(ACK/SYN)表示同意建立连接。此时两次握手完成,连接建立。
- 但是如果客户端发出的第一个连接请求报文在网络中延迟、阻塞,导致它在某个网络节点中滞留了很长时间。
- 客户端在一段时间后没有收到服务器的确认,于是重新发送一个新的连接请求报文,这次新的请求正常到达服务器,经过两次握手后连接建立并完成数据传输,最后关闭连接。
- 而此时,那个延迟的旧连接请求报文突然到达了服务器。如果是两次握手,服务器会认为这是一个新的连接请求,然后向客户端发送确认报文并建立连接,而客户端此时并不会理会这个旧的确认报文,也不会向服务器发送数据,导致服务器一直等待客户端发送数据,浪费服务器的资源。
二、三次握手才能让双方都确认自己和对方的收发能力正常
- 第一次握手:客户端发送 SYN 报文给服务器,服务器知道了客户端的发送能力正常,自己的接收能力正常。
- 第二次握手:服务器回应 SYN + ACK 报文给客户端,客户端收到后知道了自己的发送和接收能力正常,也知道了服务器的接收和发送能力正常。
- 第三次握手:客户端回应 ACK 报文给服务器,服务器收到后知道了自己的发送和接收能力正常,也确认了客户端的接收能力正常。
只有经过这三次信息的交互和确认,双方才能确定彼此的连接参数是可靠的,连接才能正式建立,从而为后续的数据传输提供稳定可靠的基础。因此,TCP 采用三次握手来建立连接是为了保证连接建立的可靠性和准确性,避免出现上述可能的异常情况。
TCP 协议的四次挥手过程如下(以下假设客户端主动发起连接关闭请求):
第一次挥手:
- 客户端打算关闭连接,此时客户端向服务器发送一个带有FIN(Finish,完成)标志位的TCP报文段,表示客户端不再发送数据了 。
- 发送后客户端进入FIN_WAIT_1(终止等待1)状态 。
第二次挥手:
- 当服务器收到客户端的FIN报文后,服务器会向客户端发送一个ACK(Acknowledge,确认)报文段,表明服务器已经收到客户端的关闭请求。
- 此时服务器进入CLOSE_WAIT(关闭等待)状态 ,客户端收到这个ACK后进入FIN_WAIT_2(终止等待2)状态。在这个阶段,从客户端到服务器这个方向的连接已经关闭(客户端不能再发数据过来),但从服务器到客户端方向的连接还未关闭(服务器如果还有数据可以继续发向客户端)。
第三次挥手:
- 当服务器完成数据发送(如果还有的话),准备关闭连接时,服务器向客户端发送FIN报文段,告知客户端服务器这边也要关闭连接了。
- 然后服务器进入LAST_ACK(最后确认)状态,等待客户端的确认应答。
第四次挥手:
- 客户端收到服务器的FIN报文后,必须对这个FIN进行确认,向服务器发送ACK报文段。
- 发送完成后,客户端进入TIME_WAIT(时间等待)状态。经过2倍的MSL(Maximum Segment Lifetime,报文最大生存时间)时长后,如果期间没有收到服务器重发的FIN报文,客户端就进入CLOSED(关闭)状态 。
- 当服务器收到客户端的ACK报文后,服务器也进入CLOSED状态,至此双方的连接完全关闭。
一些相关要点和原因解释:
为什么需要四次挥手而不是三次(类似三次握手那样):
主要是因为TCP是全双工通信,在关闭连接时,双方都需要单独关闭各自的发送通道和接收通道。第二次和第三次挥手之间存在服务器可能还需要发送数据给客户端的情况,不能合并成一步。
客户端为什么要等待2MSL时长(在TIME_WAIT状态):
- 确保最后一个ACK报文能被服务器收到,如果ACK丢失,服务器会重发FIN报文,在2MSL时间内客户端可以重新发送ACK 。
- 经过2MSL时长可以让本次连接中在网络中残留的报文段都消失,防止新连接中收到旧连接的残留报文而导致异常。
状态变化总结(客户端和服务器):
- 客户端:ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED
- 服务器:ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED
TCP 流量控制

在 TCP 连接中,存在“半打开”和“半关闭”的状态概念:
一、半打开(Half-Open)
- 定义:
- 半打开状态是指在 TCP 连接的一端异常关闭(例如主机崩溃、网络故障等),而另一端却不知道的情况。此时,正常的一端还认为连接是正常的,仍然可以向异常的一端发送数据,但异常的一端无法响应。
- 产生场景和示例:
- 例如,客户端和服务器建立了 TCP 连接,在通信过程中,服务器突然发生故障(如硬件故障导致系统崩溃),但客户端并不知道服务器已经出问题。此时,从客户端的角度来看,连接还是正常的,但实际上服务器已经无法接收或处理数据,连接处于半打开状态。
- 在这种情况下,如果客户端继续向服务器发送数据,由于服务器无法响应,这些数据可能会丢失,并且客户端可能需要经过一段时间后才能检测到连接出现问题(例如通过超时机制等)。
二、半关闭(Half-Close)
- 定义:
- 半关闭是指在一个 TCP 连接中,一端已经关闭了它的输出信道(发送数据的能力),但仍然可以接收来自另一端的数据。也就是说,连接的一个方向已经关闭,而另一个方向仍然保持打开。
- 产生场景和示例:
- 比如,客户端向服务器发送了一个关闭输出信道的请求(执行
shutdown(socket, SHUT_WR)这样的操作),这意味着客户端不再向服务器发送数据,但它仍然可以接收服务器发送的数据。此时,连接处于半关闭状态。 - 一个实际的例子是,客户端已经完成了数据的发送,但还需要接收服务器的一些响应或最后的确认信息,这时就可以使用半关闭状态。客户端关闭输出信道后,服务器可能继续发送剩余的数据,直到完成它的任务后再关闭连接。
- 比如,客户端向服务器发送了一个关闭输出信道的请求(执行
TCP 拥塞控制 (cnnd 为拥塞窗口)

慢启动算法:一开始设置为1个MSS(最大报文段)拥塞窗口,收到确认后为发送2个MSS,收到确认后发送2的3次方个MSS ,之后一直扩大。
拥塞避免算法:当到达 16个MSS 大小(上图),按照+1个MSS大小一直到发生计时器超时,这里拥塞窗口回到慢启动阶段再来,这里新的拥塞阀值为超时时拥塞窗口的一半(如上图发生拥塞是为24那么新阀值为12)。到达阀值后再进入拥塞避免算法,依次增加1个MSS大小。
TCP 拥塞控制是一种用于管理网络拥塞的机制,目的是避免网络因为过多的数据传输而发生拥塞崩溃,同时保证网络资源的公平共享和高效利用。
一、拥塞窗口(Congestion Window,cwnd)
- 这是 TCP 用于进行拥塞控制的关键参数。
- 它限制了发送方在未收到确认之前可以发送的数据量。
- 初始时,拥塞窗口通常较小,然后随着数据的成功传输和确认,逐渐增大。
二、慢启动(Slow Start)
- 阶段特点:
- 当一个新的 TCP 连接建立或者在长时间的空闲后重新开始传输时,拥塞窗口初始化为一个较小的值(通常是 1 个或几个 MSS,Maximum Segment Size,最大报文段长度)。
- 每收到一个 ACK 确认,拥塞窗口就会增加一个 MSS 的大小。
- 例如,开始时 cwnd = 1,发送一个报文段,收到确认后 cwnd = 2,再发送两个报文段,收到相应确认后 cwnd = 4,以此类推,呈指数增长。
- 阈值(Threshold)的引入:
- 当拥塞窗口增长到一个特定的阈值(ssthresh,初始值通常是 65535 字节或其他根据网络状况设定的值)时,慢启动阶段结束,进入拥塞避免阶段。
三、拥塞避免(Congestion Avoidance)
- 增长方式变化:
- 在拥塞避免阶段,拥塞窗口不再呈指数增长,而是每经过一个往返时间(RTT),拥塞窗口只增加 1 个 MSS(即线性增长)。
- 这样可以更加稳定地增加数据传输量,同时降低网络拥塞的风险。
- 网络拥塞的检测与处理:
- 如果发生超时重传(即发送方在一定时间内没有收到 ACK 确认),TCP 认为网络发生了拥塞。
- 此时,将阈值 ssthresh 设为当前拥塞窗口 cwnd 的一半,拥塞窗口 cwnd 重新初始化为 1 个 MSS,然后重新进入慢启动阶段。
- 除了超时重传,还有快速重传和快速恢复机制来处理可能的拥塞情况。
四、快速重传(Fast Retransmit)和快速恢复(Fast Recovery)
- 快速重传:
- 当接收方收到一个失序的报文段时,会立即发送重复的 ACK 给发送方,表明期望收到下一个按序的报文段。
- 当发送方连续收到三个重复的 ACK 时,就认为有报文段丢失,立即重传丢失的报文段,而不必等待超时定时器到期。
- 快速恢复:
- 在快速重传之后,不执行慢启动,而是将拥塞窗口 cwnd 设为新的 ssthresh 值(即当前拥塞窗口的一半),然后线性增加 cwnd(每收到一个重复的 ACK,cwnd 增加 1 个 MSS)。
- 当收到新的 ACK 确认新数据时,将 cwnd 恢复到拥塞避免阶段的增长方式。
例如,在一个网络中,TCP 连接开始建立,拥塞窗口初始为 1 个 MSS,经过慢启动指数增长到阈值,然后进入拥塞避免线性增长。如果出现超时重传,就重新调整阈值和拥塞窗口大小,重新开始慢启动;如果是快速重传,则进入快速恢复阶段进行相应处理。
通过超时重传和快速重传机制进行调整
- 超时重传:
- 当发送方在一定时间内没有收到确认(ACK)时,认为发生了超时。
- 此时,TCP 认为网络可能出现了拥塞。
- 处理方式是将阈值 ssthresh 设为当前拥塞窗口 cwnd 的一半,拥塞窗口 cwnd 重新初始化为 1 个 MSS,然后重新进入慢启动阶段。
- 这种大幅度的调整可以迅速减少发送的数据量,给网络一定的时间来缓解拥塞状况。
- 快速重传:
- 当接收方收到一个失序的报文段时,会立即发送重复的 ACK 给发送方,表明期望收到下一个按序的报文段。
- 当发送方连续收到三个重复的 ACK 时,就认为有报文段丢失,立即重传丢失的报文段,而不必等待超时定时器到期。
- 随后进入快速恢复阶段,将拥塞窗口 cwnd 设为新的 ssthresh 值(即当前拥塞窗口的一半),并线性增加 cwnd(每收到一个重复的 ACK,cwnd 增加 1 个 MSS),避免了因等待超时而长时间停止数据传输,同时也能较快地恢复到正常的数据传输状态,减少了网络拥塞的可能性。
超时重传的原理
超时重传机制是 TCP 保证数据可靠传输的一个重要机制,其原理如下:
一、定时器设置
- 当发送方发送一个数据报文段时,它会同时启动一个定时器。
- 定时器的时长是根据网络的往返时间(RTT)以及一些其他因素动态计算出来的。通常,初始的定时器时长会基于一个经验值或者通过一些测量和估算方法来确定。
二、超时判断
- 发送方在定时器计时期间会等待接收方返回的确认(ACK)。
- 如果在定时器超时之前收到了确认,说明数据报文段已经成功到达接收方,发送方就会取消定时器,并继续发送下一个数据报文段。
- 如果定时器超时了还没有收到确认,发送方就认为该数据报文段在网络中丢失或者出现了其他传输问题。
三、重传操作
- 一旦发生超时,发送方会重新发送该数据报文段。
- 同时,发送方可能还会调整定时器的时长。例如,根据网络的拥塞状况和之前的往返时间测量结果,增加或减小定时器的时间间隔,以适应网络的变化。
- 重传后,再次启动定时器,继续等待确认。如果再次超时,可能会继续重传,并且可能会进一步调整定时器和拥塞窗口等参数,以应对可能的网络拥塞或其他问题。
四、RTT 测量与更新
- 在数据传输过程中,发送方会持续测量每个数据报文段的往返时间(RTT)。
- 即从发送数据报文段的时刻到收到相应确认的时间间隔。
- 发送方会根据多个 RTT 的测量值,采用一些算法(如指数加权移动平均等)来更新定时器的时长,以使其更准确地反映当前网络的实际状况。
- 例如,如果最近几次的 RTT 变长,说明网络状况可能变差,定时器时长可能会相应增加,以减少不必要的重传;反之,如果 RTT 变短,定时器时长可能会适当减小,以提高数据传输的效率。
练习题
TCP使用慢启动拥塞避免机制进行拥塞控制。当拥塞窗口大小为16时,发送节点出现超时未收到确认现象时,将采取的措施是(1),再经过5轮后的拥塞窗口大小为(2),则上述空格内容为() 请解答
在TCP的拥塞控制算法中,当发送节点出现超时未收到确认的现象时,它假定网络中发生了拥塞,因此需要采取措施减少发送到网络中的数据量以避免进一步的拥塞。具体措施如下:
- 慢启动阈值(ssthresh)的更新:当检测到超时时,慢启动阈值ssthresh会被设置为当前拥塞窗口cwnd的一半(为了避免除以奇数出现的小数一般会向下取整),但最少要大于2。如果当前拥塞窗口大小为16,则新的慢启动阈值ssthresh应该是8(16的一半)。
- 拥塞窗口(cwnd)的重置:超时发生后,拥塞窗口cwnd会被重置为1个MSS(Maximum Segment Size,最大报文段长度)。这是为了重新开始慢启动过程,逐步增加发送速率直到再次达到ssthresh或遇到网络拥塞。
- 慢启动与拥塞避免:当cwnd等于ssthresh时,从慢启动阶段转换到拥塞避免阶段。在慢启动阶段,每收到一个ACK,cwnd增加一个MSS;而在拥塞避免阶段,每一轮次(每次接收到与cwnd数量相等的ACK)增加一个MSS。
根据上述规则,当拥塞窗口大小为16时,出现超时后,采取的措施是将cwnd减少为1(即1个MSS),并将ssthresh设置为8。
接下来,经过5轮次(假设每轮成功收到ACK并且没有发生进一步的超时)后的拥塞窗口大小计算如下:
- 第1轮后:cwnd = 1 (慢启动阶段)
- 第2轮后:cwnd = 2 (慢启动阶段)
- 第3轮后:cwnd = 8 (拥塞避免阶段,每次增加1 MSS)达到阈值大小后cwnd增加一个MSS
- 第4轮后:cwnd = 9
- 第5轮后:cwnd = 10
因此,再经过5轮后的拥塞窗口大小为10。