TCP(一)、虐你千百遍的 “TCP三次握手、四次挥手” 大讲解

一、TCP基本认识

01、瞧瞧TCP格式

  • 序列号: 在建立连接时由计算机生成的随机数作为其初始值,通过SYN包传给接收端主机,每发送一次数据,就【累加】一次该【数据字节数】的大小。用来解决网络包乱序问题
  • 确认应答号: 指下一次【期望】收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号之前的数据都已经被正常接收。用来解决不丢包问题
  • 控制位:
    ACK:该位为 1 时,【确认应答】的字段变为有效,TCP规定除了最初建立连接时的 SYN包之外该位必须设置为 1
    RST:该位为 1 时,表示TCP连接中出现异常必强制断开连接。
    SYN:该位为 1 时,表示希望建立连接,并在其【序列号】的字段进行序列号初始值的设定。
    FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束,希望断开时,通信双发的主机就可以相互交换FIN位置为1的TCP段。

02、为什么需要TCP协议?工作在哪一层?

IP层是【不可靠】的,它不保证网络包的交付、不保证网络包的按序支付、也不保证网络包中的数据的完整性。
如果需要保障网路数据包的可靠性,那么就需要由上层(传输层)的TCP协议来负责。
因为TCP 是一个工作在传输层可靠数据传输的服务,它能保证接收端接收的网络包是无损坏、无间隔、非冗余和按序的


03、什么是TCP?

TCP面向连接的、可靠的、基于字节流的传输层通信协议。

  • 面向连接:一定是【一对一】才能连接,不能像UDP协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
  • 可靠的:无论网络链路中出现了怎样的链路变化,TCP都可以保证一个报文一定能够到达接收端。
  • 字节流:消息是【没有边界的】,所以无论我们消息有多大都可以进行传输。并且消息是【有序的】,当【前一个】消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对【重复】的报文会自动丢弃。

04、什么是TCP连接?

简单的来说,就是用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接
所以我们可以知道,建立一个TCP连接是需要客户端与服务端达成下面三个信息的共识:

  • Socket:由 IP 地址和端口号组成
  • 序列号:用来解决乱序问题等
  • 窗口大小:用来做流量控制

05、如何唯一确定一个TCP连接呢?

TCP四元组可以唯一确定一个连接,四元组包括如下:

  • 源地址
  • 源端口
  • 目的地址
  • 目的端口
    源地址和目的地址的字段(32位)是在IP头部中,作用是通过 IP 协议发送报文给对方主机。
    源端口和目的端口的字段(16位)是在TCP头部中,作用是告诉 TCP 协议应该把报文发送给哪个进程。

06、有一个 IP 的服务器监听了一个端口,它的TCP的最大连接数是多少?

服务器通常固定在某个本地端口上监听,等待客户端的连接请求。因此,客户端 IP 和 端口是可变的,其理论值计算公式如下:

最大TCP连接数 = 客户端的IP数 * 客户端的端口数

对IPv4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口号最多为 2 的 16 次方,也就是服务端单机最大TCP 连接数,约为 2 的 48 次方。
当然,服务端最大并发 TCP 连接数远不能达到理论上限。

  • 首先主要是文件描述符限制,Socket 都是文件,所以首先要通过 ulimit配置文件描述符的数目。
  • 另一个是内存限制,,每个TCP连接都要占用一定内存,操作系统是由限的。

07、UDP 和 TCP 有什么区别呢?分别的应用场景是?

UDP 不提供复杂的控制机制,利用 IP 提供面向【无连接】的通信服务。
UDP 协议真的非常简,头部只有 8 个字节(64位),UDP 的头部格式仅仅包括:

  • 目标和源端口号:主要是告诉 UDP 协议应该把报文发给哪个进程
  • 包长度:该字段保存了 UDP 首部的长度和数据的长度之和。
  • 校验和:校验和是为了提供可靠的UDP首部和数据而设计。

TCP 和 UDP 区别

  1. 连接:
    ①:TCP是面向连接的传输层协议,传输数据前首先要建立连接
    ②:UDP是不需要连接,即刻传输数据。
  2. 服务对象:
    ①: TCP是一对一的两点服务,即一条连接只有两个端点。
    ②:UDP支持一对一、一对多、多对多的交互通信。
  3. 可靠性:
    ①:TCP是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
    ②:UDP 是尽最大努力交付,不保证可靠交付数据。
  4. 拥塞控制、流量控制:
    ①:TCP有拥塞控制和流量控制机制,保证数据传输的安全性。
    ②:UDP则没有,即使网络非常拥堵了,也不会影响UDP的发送速率。
  5. 首部开销:
    ①:TCP首部长度较长,会有一定的开销,首部在没有使用【选项】字段时是 20 个字节,如果使用了【选项】字段则会变长的。
    ②:UDP 首部只有 8 个字节,并且是固定不变的,开销较小。

TCP 和 UDP应用场景
由于TCP是面向连接的,能保证数据的可靠性支付,因此常用于:

  • FTP文件传输
  • HTTP/ HTTPS

由于 UDP 面向无连接,它可以随时发送数据,再加上UDP 本身的处理既简单又高效,因此常用于:

  • 包总量较少的通信,如 DNS、SNMP
  • 视频、音频等多媒体通信
  • 广播通信

08、为什么UDP头部没有【首部长度】字段,而TCP头部有【首部长度】字段呢?

原因是TCP有TCP有可变长的【选项】字段,而UDP头部长度则是不会变化的,无需多一个字段去记录UDP 的首部长度。


09、为什么 UDP 头部有【包长度】字段,而TCP头部没有【包长度】字段呢?

先说说TCP是如何计算负载长度的:

TCP数据的长度 = IP 总长度 — IP首部长度 —  TCP首部长度

其中 IP 总长度 和 IP 首部长度,在 IP 首部格式是已知的。TCP 首部长度,则是在 TCP 首部格式已知的,所以就可以求得TCP数据的长度。
大家这时就奇怪的问:“UDP 也是基于IP 层的啊 ,那UDP 的数据长度也可以通过这个公式计算啊 ? 为何还要有【包长度】呢?”
因为为了网络设别硬件设计和处理方便,首部长度需要是 4 字节的整数倍


二、TCP 连接建立

01、TCP三次握手和状态变迁

TCP是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手而进行的

  • 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。
  • 客户端会把随机初始化序号(client_isn),将此序号置于TCP首部的【序号】字段中,同时把 SYN 标志为 1, 表示 SYN报文。接着把第一个SYN报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于SYN-SENT状态。
  • 服务端收到客户端的SYN报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入TCP首部的【序号】字段中,其次把TCP 首部的【确认应答号】字段填入 client_isn + 1,接着把SYNACK标志位置为 1 。最后把该报文发送给客户端,该报文也不包含应用层数据,之后服务器处于 SYN_RCVD 状态。
  • 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置标为 1,其次【确认应答号】字段填入 server_isn + 1,最后把报文发送给服务端,这次报文可以携带客户到服务器的数据,之后客户端处于ESTABLISHED
  • 服务器收到客户端的应答报文后,也进入ESTABLISHED状态。

从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的,这也是面试常见的问题。
一旦完成三次握手,双发都处于ESTABLISHED 状态,此连接就已经建立完成,客户端和服务端就可以相互发送数据了。


02、如何在Linux 系统中查看TCP状态?

TCP的连接状态查看,在Linux 可以通过 netstat -napt 命令查看。


03、为什么是三次握手?不是两次、四次?

在前面我们知道了什么是 TCP连接

  • 用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。

所以,重要的是为什么三次握手才可以初始化Socket、序列号和窗口大小并建立TCP连接
接下来在三个方面分析三次握手的原因:

  • 三次握手才可以阻止历史重复连接初始化(主要原因)
  • 三次握手才可以同步双方的初始序列号
  • 三次握手才可以避免资源浪费

原因一:避免历史连接
简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。 网络环境是错综复杂的,往往并不是我们期望的一样,先发送的数据包,就先到达目标主机,反而它很骚,可能会由于网络拥堵等乱七八糟的原因,会使得旧的数据包,先到达目标主机,那么这种情况下 TCP 三次握手是如何避免的呢?
客户端连续发送多次 SYN 建立连接的报文,在网络拥堵等情况下:

  • 一个【旧SYN报文】比【最新的SYN】报文早到达了服务端
  • 那么此时服务端就会回一个 SYN + ACK报文给客户端;
  • 客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送RST 报文给服务器,表示中止这一次连接。

如果是两次握手连接,就不能判断当前连接是否是历史连接,三次握手则可以在客户端(发送方)准备发第三次报文时,客户端因有足够的上下文来判断当前连接是否时历史连接:

  • 如果时历史连接(序列号过期或者超时),则第三次握手发送的时 RST 报文,以此中止历史连接;
  • 如果不是历史连接,则第三方发送的报文时ACK 报文,通信双发就会成功建立连接;

所以,TCP使用三次握手建立连接的最主要原因是防止历史连接初始化了连接

原因二:同步双方初始序列号
TCP协议的通信双方,都必须维护一个【序列号】,序列号是可靠传输的一个关键因素,它的作用:

  • 接收方可以去除重复的数据
  • 接收方可以根据数据包的序列号按序接收
  • 可以标识发送出去的数据包中,哪些是已经被对方收到的;

可见,序列号在TCP连接中占据着非常重要的作用,所以当客户端发送携带【初始序列号】的SYN报文的时候,需要服务端回一个 ACK应答报文,表示客户端的SYN报文已被服务端成功接收,那当服务端发送【初始序列号】给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步
四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了【三次握手】。而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。

原因三:避免资源浪费
如果只有【两次握手】,当客户端的SYN请求连接在网络中阻塞,客户端没有接收到ACK 就会重复发送SYN,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的ACK确认信号,所以每收到一个SYN就只能先主动建立一个连接,这会造成什么情况呢?
如果客户端的SYN阻塞了,重复发送多次SYN,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费
即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求SYN,而造成重新分配资源。

小结:

TCP建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按需传输。
不使用【两次握手】和【四次握手】的原因:

  • 【两次握手】:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号。
  • 【四次握手】:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。

04、为什么客户端和服务端的初始序列号ISN 是不相同的?

因为网络中的报文会延迟、会复制重发、也有可能丢失,这样会造成不同连接之间产生互相影响,所以为了避免相互影响,客户端可服务端的初始序列号是随机且互不相同的。


05、初始化序列号ISN 是如何产生的?

起始 ISN 是基于时钟的,每4毫秒 + 1,转一圈要4.55个小时。RFC1948中提出了一个较好的初始化序列号ISN随机生成算法。

ISN = M + F
  • M 是一个计时器,这个计时器每隔4毫秒加1.
  • F 是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。要保证Hash算法不能呗外部推算得出,用MD5算法是一个比较好的选择。

06、既然 IP 层会分片,为什么 TCP 层还需要 MSS呢?

在这里插入图片描述

  • MTU:一个网络包的最大长度,以太网中一般为1500字节;
  • MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的TCP 数据的最大长度;

如果TCP的整个报文(头部+ 数据)交给 IP 层进行分片,会有什么异常呢?
当IP 层有一个超过 MTU大小的数据(TCP头部 + TCP数据)要发送,那么IP 层就要进行分片,把数据分片成若干片,保证每一个分片都小于 MTU。把每一份 IP 数据报进行分片以后,由目标主机的 IP 层来进行重新组装后,再交给上一层 TCP 传输层。

这看起来既然有序,但这存在隐患的,那么当如果一个IP分片丢失,整个 IP 报文的所有分片都得重传
因为 IP 层本身没有超时重传机制,它由传输层的TCP来负责超时和重传。

当接收方发现 TCP 报文(头部 + 数据)的某一片丢失后,则不会响应 ACK 给对方,那么发送方的 TCP 在超时后,就会重发【整个TCP报文(头部 + 数据)】。
因此,可以得知由 IP 层进行分片传输,是非常没有效率的。
所以,为了达到最佳的传输效能 TCP 协议在建立连接的时候通常要协商双方的 MSS 值,当TCP层发现数据超过MSS时,则就先会进行分片,当然由它形成的IP包的长度也就不会大于 MTU ,自然也就不用 IP 分片了。
经过 TCP 层分片后,如果一个 TCP 分片丢失后,进行重发时也是以MSS为单位,而不用重传所有的分片,大大增加了重传的效率。


07、什么时SYN攻击?如何避免 SYN 攻击?

我们都知道TCP连接建立时需要三次握手,假设攻击者短时间伪造不同 IP 地址的 SYN报文,服务端每接收到一个SYN报文,就进入SYN_RCVD 状态,但服务端发送出去的ACK + SYN报文,无法得知未知IP主机的ACK应答,久而久之就会占满服务端的SYN接收队列(未连接队列),使得服务器不能为正常用户服务。
避免SYN攻击方式:其中一种解决方式是通过修改 Linux 内核参数,控制队列大小和当队列满时应做些什么处理。

  • 当网卡接收到数据包的速度大于内核处理的速度时,会有一个队列保存这些数据包。控制该队列的最大值如下参数:
net.core.netdev_max_backlog
  • SYN_RCVD 状态连接的最大个数:
net.ipv4.tcp_max_syn_backlog
  • 超出处理能力时,对新的 SYN 直接返回RST,丢弃连接:
net.ipv4.tcp_abort_on_overflow

三、TCP连接断开

01、四次挥手过程和状态变迁

天下没有不散的宴席,对于TCP连接也是这样,TCP断开连接是通过 四次挥手方式,双方都可以主动断开连接,断开连接后主机的【资源】将被释放。
在这里插入图片描述

  • 客户端打算关闭连接,此时会发送一个TCP首部FIN标志位 被置为 1的报文,也即FIN报文,之后客户端进入FIN_WAIT_1状态。
  • 服务端收到该报文后,就向客户端发送ACK应答报文,接着服务端进入CLOSED_WAIT状态。
  • 客户端收到服务端的ACK应答报文之后,之后进入FIN_WAIT_2状态。
  • 等待服务端处理完数据后,也向客户端发送FIN报文,之后服务端进入LAST_ACK 状态。
  • 客户端收到服务端的FIN 报文后,回一个ACK 应答报文,之后进入 TIME_WAIT 状态。
  • 服务器收到了ACK 应答报文后,就进入了 CLOSE 状态,至此服务器已经完成连接的关闭。
  • 客户端再经过** 2MSL**一段时间后,自动进入 CLOSE状态,至此客户端也完成连接的关闭。

可以看到,每个方向都需要一个FIN 和 一个ACK,因此通常被称为四次挥手
这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT状态


02、为什么挥手需要四次?

再来回顾下四次挥手双方发 FIN包的过程,就能理解为什么需要四次了。

  • 关闭连接时,客户端向服务端发送FIN时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务器收到客户端的FIN报文时,先回一个ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务器不再发送数据时,才发送FIN报文给客户端来表示同意现在关闭连接。

从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的ACKFIN一般都会分开发送,从而导致比三次握手多了一次。


03、为什么 TIME_WAIT 等待的时间时 2MSL?

MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为TCP报文是基于 IP 协议的,而 IP 头中有一个 TTL字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值位0时,则数据报被丢弃,同时发送 ICMP 报文通知源主机。
MSL 与 TTL 的区别:MSL 的单位是时间,TTL 是经过路由跳数。所以 MSL应该要大于等于TTL 消耗为0的时间,以确保报文已被自然消亡。
TIME_WAIT等待 2 倍 的MSL,比较合理的解释是:网络中可能存在来自对方的数据包,当这些对方的数据包被我方处理后又向对方发送响应,所以一来一回需要等待 2 倍的时间


04、为什么需要 TIME_WAIT 状态?

主动发起关闭的一方,才会有 TIME_WAIT 状态。
需要 TIME_WAIT 状态,主要是两个原因:

  • 防止具有相同【四元组】的【旧】数据包被收到
  • 保证【被动关闭连接】的一方能被正确的关闭,即保证最后的ACK 能让被动关闭方接收到,从而帮助其正常关闭。

05、TIME_WAIT 过多有什么危害?

如果服务器有处于 TIME_WAIT 状态的TCP,则说明是由服务器方主动发起的断开请求。
过多的 TIME_WAIT 状态的危害有两种:

  • 第一是内存资源占用
  • 第二是对端口资源的占用,一个TCP连接至少消耗一个本地端口;
    第二个危害会造成严重的后果的,要知道,端口资源也是有限的,一般可以开启的端口为 32768 ~ 61000 ,也可以通过如下参数设置指定。
net.ipv4.ip_local_port_range

如果服务端TIME_WAIT 状态过多,沾满了所有端口资源,则会导致无法创建新连接


06、如何优化TIME_WAIT ?
  • 打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项
  • net.ipv4.tcp_max_tw_buckets
  • 程序中使用 SO_LINGER ,应用强制使用 RST关闭。

07、如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP有一个机制是保送机制,这个机制的原理是这样的:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间时段,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
在Linux内核可以有对应的参数可以设置保活时间、保活探测的时间间隔、保活探测的次数,以下为默认值:

net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
  • net.ipv4.tcp_keepalive_time = 7200 : 表示保活时间是7200秒(2小时),也就2小时内如果没有任何连接相关的活动,则会启动保活机制
  • net.ipv4.tcp_keepalive_intvl = 75 :表示每次检测间隔 75秒
  • net.ipv4.tcp_keepalive_probes = 9 :表示检测9从无响应,认为对方是不可达的,从而中断本次的连接。
    也就是说在Linux 系统中,最少需要经过 2 小时 11分 15秒才可以发现一个【死亡】连接。
7200 + 75 * 9 = 7875秒 (2小时 11分 15秒)

如果开启了 TCP 保活,需要考虑以下几种情况:

  • 第一种,对端程序是正常工作的。当TCP保活的探测报文发送给对端,对端会正常响应,这样TCP保活时间会被重置,等待下一个TCP保活时间的到来。
  • 第二种,对端程序崩溃并重启。当TCP保活的探测报文发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,会产生一个RST报文,这样很快就会发现TCP 连接已经被重置。
  • 第三种,是对端程序崩溃,或对端由于其他原因导致报文不可达。当TCP保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活次数后,TCP会报告该TCP连接已经死亡

四、Socket编程

01、针对TCP应该如何 Socket编程?

在这里插入图片描述

  • 服务端和客户端初始化socket,得到文件描述符;
  • 服务端调用bind IP 地址 和 端口;
  • 服务端调用listen ,进行监听;
  • 服务端调用accept, 等待客户端连接;
  • 客户端调用connect,向服务端的地址和端口发起连接请求;
  • 服务端调用write写入数据;服务端调用read读取数据;
  • 客户端断开连接时,会调用close,那么服务端read读取数据的时候,就会读取到了EOF,待处理完数据后,服务端调用close,表示连接关闭。

这里需要注意的是,服务端调用accept 时,连接成功了会返回一个已完成连接的socket,后续用来传输数据。
所以,监听的socket和真正用来传送数据的socket,是【两个】socket,一个叫作监听socket,一个叫作已完成连接socket
成功连接建立后,双方开始通过read 和 write 函数来读写数据,就像往一个文件流里写东西一样。


02、listen时候 参数backlog的意义?

Linux内核中会维护两个队列:

  • 未完成连接队列(SYN队列):接收到一个SYN 建立连接请求,处于 SYN_RCVD状态;
  • 已完成连接队列(Accept 队列):已完成TCP三次握手,处于 ESTABLISHED 状态;
int listen(lint socketed, int backlog)
  • 参数一:socketfd 为 socketfd 文件描述符
  • 参数二:backlog,这参数在历史有一定的变化
    在早期 linux 内核 2.2之后,backlog 是 SYN队列大小,也就是未完成的队列大小。
    在Linux 内核2.2之后,backlog变成accep队列,也就是已完成连接建立的队列长度,所以现在通常认为backlog 是 accept队列

03、accept 发送在三次握手的哪一步?

我们先看看客户端连接服务器时,发送了什么?
在这里插入图片描述

  • 客户端的协议栈向服务器发送了SYN包,并告诉服务器当前发送序列号为 client_isn,客户端进入 SYNC_SENT 状态;
  • 服务器端的协议栈收到这个包之后,和客户端进行ACK应答,应答的值为client_isn + 1,表示对SYN包 client_isn 的确认,同时服务器也发送一个 SYN包,告诉客户端当前我的发送序列为server_isn,服务器进入 SYNC_RCVD 状态;
  • 客户端协议栈收到 ACK之后,使得应用程序从connect调用返回,表示客户端到服务端的单向连接建立成功,客户端的状态为 ESTABLISHED,同时客户端协议栈也会对服务端的SYN 包进行应答,应答数据为 sercer_isn + 1;
  • 应答包到达服务器后,服务器协议栈使得 accept阻塞调用返回,这个时候服务端到客户端的单向连接也建立成功,服务器端也进入 ESTABLISHED状态

从上面的描述过程,我们可以得知客户端 connect 成功返回是在第二次握手,服务端accept成功返回是在三次握手成功之后。


04、客户端调用了close,断开的流程时什么?

在这里插入图片描述

  • 客户端调用close,表明客户端没有数据需要发送了,则此时会向服务器端发送 FIN 报文,进入FIN_WAIT_1 状态;
  • 服务端接收到了FIN报文,TCP协议栈会为FIN包插入一个文件结束符EOF到接收缓冲区中,应用程序可以通过read调用来感知这个FIN包。这个EOF会被放到已排队等候的其他已接受的数据之后,这就意味着服务端需要处理这种异常情况,因为EOF表示在该连接上再无额外的数据到达。此时,服务端进入CLOSE_WAIT状态;
  • 接着,当处理完数据后,自然就会读到EOF,于是也调用close关闭它的套接字,这会发出一个 FIN 包,之后处于LAST_ACK状态;
  • 服务端收到ACK确认包后,就进入了最后的CLOSE 状态;
  • 客户端在进入TIME_WAIT状态后经过2MSL时间之后,也进入CLOSED状态。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值