注:本文为“TCP 连接状态标识”相关文章合辑。
略作重排。
如有内容异常,请看原文。
TCP 的状态:SYN, FIN, ACK, PSH, RST, URG 简介及 ACK 确认机制
llzhang_fly 于 2020-09-19 05:25:26 发布
1. TCP 的状态 FLAGS 字段状态
在 TCP 层,有个 FLAGS 字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.
对于我们日常的分析有用的就是前面的五个字段,它们的含义是:
- SYN 表示建立连接,
- FIN 表示关闭连接,
- ACK 表示响应,
- PSH 表示有 DATA 数据传输,
- RST 表示连接重置。
其中,ACK 是可能与 SYN,FIN 等同时使用的,例如:SYN 和 ACK 可能同时为 1,它表示的就是建立连接之后的响应;如果只是单个的一个 SYN,它表示的只是建立连接。TCP 的几次握手就是通过这样的 ACK 表现出来的。但 SYN 与 FIN 是不会同时为 1 的,因为前者表示的是建立连接,而后者表示的是断开连接。RST 一般是在 FIN 之后才会出现为 1 的情况,表示的是连接重置。一般地,当出现 FIN 包或 RST 包时,我们便认为客户端与服务器端断开了连接;而当出现 SYN 和 SYN+ACK 包时,我们认为客户端与服务器建立了一个连接。PSH 为 1 的情况,一般只出现在 DATA 内容不为 0 的包中,也就是说 PSH 为 1 表示的是有真正的 TCP 数据包内容被传递。
TCP 的连接建立和连接关闭,都是通过请求-响应的模式完成的。
概念补充 - TCP 三次握手
TCP(Transmission Control Protocol)传输控制协议是一种主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
- 位码 即 TCP 标志位,有 6 种标示:SYN(synchronous 建立联机)、ACK(acknowledgement 确认)、PSH(push 传送)、FIN(finish 结束)、RST(reset 重置)、URG(urgent 紧急)、Sequence number(顺序号码)、Acknowledge number(确认号码)。
第一次握手:主机 A 发送位码为 syn=1,随机产生 seq number=1234567 的数据包到服务器,主机 B 由 SYN=1 知道,A 要求建立联机;
第二次握手:主机 B 收到请求后要确认联机信息,向 A 发送 ack number=(主机 A 的 seq+1),syn=1,ack=1,随机产生 seq=7654321 的包;
第三次握手:主机 A 收到后检查 ack number 是否正确,即第一次发送的 seq number+1,以及位码 ack 是否为 1,若正确,主机 A 会再发送 ack number=(主机 B 的 seq+1),ack=1,主机 B 收到后确认 seq 值与 ack=1 则连接建立成功。完成三次握手,主机 A 与主机 B 开始传送数据。
2. Wireshark TCP 报文到达 ACK 确认机制
TCP 数据包中的序列号(Sequence Number) 不是以报文段来进行编号的,而是将连接生存周期内传输的所有数据当作一个字节流,序列号就是整个字节流中每个字节的编号。一个 TCP 数据包中包含多个字节流的数据(即数据段),而且每个 TCP 数据包中的数据大小不一定相同。在建立 TCP 连接的三次握手过程中,通信双方各自已确定了初始的序号 (x) 和 (y),TCP 每次传送的报文段中的序号字段值表示所要传送本报文中的第一个字节的序号。
TCP 的报文到达确认(ACK),是对接收到的数据的最高序列号的确认,表示这前面的数据已经接收到,并向发送端返回一个下次接收时期望的 TCP 数据包的序列号(Ack Number)。例如,主机 A 发送的当前数据序号是 400,数据长度是 100,则接收端收到后会返回一个确认号是 500 的确认号给主机 A。
TCP 提供的确认机制,可以在通信过程中不对每一个 TCP 数据包发出单独的确认包(Delayed ACK 机制),而是在传送数据时,顺便把确认信息传出,这样可以大大提高网络的利用率和传输效率。同时,TCP 的确认机制也可以一次确认多个数据报,例如,接收方收到了 201,301,401 的数据报,则只需要对 401 的数据包进行确认即可,对 401 的数据包的确认也意味着 401 之前的所有数据包都已经确认,这样也可以提高系统的效率。
若发送方在规定时间内没有收到接收方的确认信息,就要将未被确认的数据包重新发送。接收方如果收到一个有差错的报文,则丢弃此报文,并不向发送方发送确认信息。因此,TCP 报文的重传机制是由设置的超时定时器来决定的,在定时的时间内没有收到确认信息,则进行重传。 这个定时的时间值的设定非常重要,太大会使包重传的延时比较大,太小则可能没有来得及收到对方的确认包发送方就再次重传,会使网络陷入无休止的重传过程中。接收方如果收到了重复的报文,将会丢弃重复的报文,但是必须发回确认信息,否则对方会再次发送。 丢包重传直到收到 ACK 报文或发送方达到配置的最大重传次数,最大重传次数取决于发送操作系统的配置值。默认情况下,Windows 主机默认重传 5 次。大多数 Linux 系统默认最大 15 次。两种操作系统都可配置。
TCP 协议应当保证数据报按序到达接收方。如果接收方收到的数据报文没有错误,只是未按序号,这种现象如何处理呢?TCP 协议本身没有规定,而是由 TCP 协议的实现者自己去确定。通常有两种方法进行处理:一是对没有按序号到达的报文直接丢弃,二是将未按序号到达的数据包先放于缓冲区内,等待它前面的序号包到达后,再将它交给应用进程。后一种方法将会提高系统的效率。例如,发送方连续发送了每个报文中 100 个字节的 TCP 数据报,其序号分别是 1,101,201,…,701。假如其它数据报都收到了,而 201 这个数据报没有收到,则接收端应当对 1 和 101 这两个数据报进行确认,并将数据递交给相关的应用进程,301 至 701 这 5 个数据报则应当放于缓冲区,等到 201 这个数据报到达后,然后按序将 201 至 701 这些数据报递交给相关应用进程,并对 701 数据报进行确认,确保了应用进程级的 TCP 数据的按序到达。
3. TCP 抓包分析
总结
1. ACK 包可以和其他包合在一起,例如 ACK 包可以携带数据。
2. 可以接收多个数据包后,一次性给一个应答,不用每个数据包一一对应给应答。
3. 在通信过程中,通过接收到的包的 ack 值可以判断是否是上一个本机发送包的应答包(ack 值与上一个本机发送包的 seq 有关),seq 值和 ack 值的确定规则如下:
三次握手
- 第一次握手(发送):seq 为 (x)((x) 为任意值),无视 ack(因为是第一个包,不需要给其他包应答)。
- 第二次握手(发送):seq 为 (y)((y) 为任意值),ack 等于接收包 seq+1(即 (x+1))。
- 第三次握手(发送):seq 等于上一个本机发送包 seq+1(即 (x+1)),也就是 1,ack 等于接收包 seq+1(即 (y+1))。
数据传输
某主机发送的 seq 和 ack 是根据上一个接收包的 seq、ack 和 len 得到,具体为:seq=ack,ack=seq+len。
提醒:如果某一主机连续发了 4 个包,后三个包的 seq 和 ack 和第一个包的一样。
提醒:seq 会单调增大。
特别:如果握手完第一个数据包是客户端发送,第一个数据包的 seq 和 ack 和第三次握手的一样。
四次挥手
-
如果是服务器发起的挥手,挥手前最后一个包是服务器发送:
- 第一次挥手(发送):seq 为上一个本机发送包 seq+len,ack 为上一个本机发送包 ack。
- 第二个挥手(发送):seq 为本次接收包 ack,ack 为本次接收包 seq+1。
- 第三次挥手(发送):和第二次挥手一样。
- 第四次挥手(发送):seq 为本次接收包 ack,ack 为本次接收包 seq+1。
-
如果是客户端发起的挥手,挥手前最后一个包是客户端发送:
- 第一次挥手(发送):seq 为本次接收包 ack,ack 为本次接收包 seq+len。
- 第二个挥手(发送):seq 为本次接收包 ack,ack 为本次接收包 seq+1。
- 第三次挥手(发送):和第二次挥手一样。
- 第四次挥手(发送):seq 为本次接收包 ack,ack 为本次接收包 seq+1。
4. TCP 包的 seq 和 ack 号计算方法
https://blog.csdn.net/huaishu/article/details/93739446
5. 使用 Wireshark 出现很多 TCP Retransmission 信息
http://blog.jues.org.cn/post/shi-yong-wireshark-chu-xian-hen-duo-tcp-retransmission-xin-xi.html
出现 TCP Retransmission 多数是因为目标主机的端口没有开放监听,很少出现是网络不好导致的。
如果在某个时间段(RTT 的倍数)内没有确认发送的数据,则将数据重新传输到远程主机。重传超时从 RTT 开始,并随着每次重传而增加一倍。重传超时总是受限于 CFGZ-MNRTO 和 CFGYMax RTO。如果自从第一次传输数据以来,CFGY-ReTrExtTMO 时间就过去了,连接被关闭,即状态被设置为关闭。注意,当一个套接字被关闭时,将响应于接收到的端口所发送的任何数据包来发送重置。
当超时发生时,将重新发送输出窗口中的所有未确认数据。数据被重新打包,因此,包将不与原始包相同。例如,如果以 10 字节的数据发送分组,则发送具有 30 字节数据的分组,并且第一分组丢失,40 字节的未确认数据将在输出窗口中。当超时发生时,所有 40 个字节将在一个分组中发送(假设 MSS 大于或等于 40)。
如果接收到三个重复的确认,则快速重传算法无需等待超时即可重传 TCP 数据。RTIP32 还实现了 RFC 2582 中定义的 NeReNeO 快速恢复算法。
- tcp retransmission原因-CSDN博客
https://blog.csdn.net/lemontree1945/article/details/88581516
类似场景:client 连接服务器时,因 TLS 证书设置错误,所以会导致连接服务器后,没有收到应答;即发送 SYN 报文,无响应。
TCP 的连接状态标识(SYN, FIN, ACK, PSH, RST, URG)
m0_37989944 于 2021-09-03 18:47:59 发布
在 TCP 层,有个 FLAGS 字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.
其中,对于我们日常的分析有用的就是前面的五个字段。它们的含义是:
- SYN 表示建立连接,
- FIN 表示关闭连接,
- ACK 表示响应,
- PSH 表示有 DATA 数据传输,
- RST 表示连接重置。
其中,ACK 是可能与 SYN,FIN 等同时使用的,例如 SYN 和 ACK 可能同时为 1,它表示的就是建立连接之后的响应;如果只是单个的一个 SYN,它表示的只是建立连接。TCP 的几次握手就是通过这样的 ACK 表现出来的。但 SYN 与 FIN 是不会同时为 1 的,因为前者表示的是建立连接,而后者表示的是断开连接。RST 一般是在 FIN 之后才会出现为 1 的情况,表示的是连接重置。一般地,当出现 FIN 包或 RST 包时,我们便认为客户端与服务器端断开了连接;而当出现 SYN 和 SYN+ACK 包时,我们认为客户端与服务器建立了一个连接。PSH 为 1 的情况,一般只出现在 DATA 内容不为 0 的包中,也就是说 PSH 为 1 表示的是有真正的 TCP 数据包内容被传递。
TCP 的连接建立和连接关闭,都是通过请求-响应的模式完成的。
概念补充 - TCP 三次握手
TCP(Transmission Control Protocol)传输控制协议是一种主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
-
位码 即 TCP 标志位,有 6 种标示:SYN(synchronous 建立联机)、ACK(acknowledgement 确认)、PSH(push 传送)、FIN(finish 结束)、RST(reset 重置)、URG(urgent 紧急)、Sequence number(顺序号码)、Acknowledge number(确认号码)。
-
第一次握手:建立连接时,客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;
-
第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;
-
第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。完成三次握手,客户端与服务器开始传送数据。
【注意】中断连接端可以是 Client 端,也可以是 Server 端。
假设 Client 端发起中断连接请求,也就是发送 FIN 报文。Server 端接到 FIN 报文后,意思是说 “我 Client 端没有数据要发给你了”,但是如果你还有数据没有发送完成,则不必急着关闭 Socket,可以继续发送数据。所以你先发送 ACK,“告诉 Client 端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候 Client 端就进入 FIN_WAIT 状态,继续等待 Server 端的 FIN 报文。当 Server 端确定数据已发送完成,则向 Client 端发送 FIN 报文,“告诉 Client 端,好了,我这边数据发完了,准备好关闭连接了”。Client 端收到 FIN 报文后,“就知道可以关闭连接了,但是他还是不相信网络,怕 Server 端不知道要关闭,所以发送 ACK 后进入 TIME_WAIT 状态,如果 Server 端没有收到 ACK 则可以重传。” Server 端收到 ACK 后,“就知道可以断开连接了”。Client 端等待了 2MSL 后依然没有收到回复,则证明 Server 端已正常关闭,那好,我 Client 端也可以关闭连接了。Ok,TCP 连接就这样关闭了!
整个过程 Client 端所经历的状态如下:
而 Server 端所经历的过程如下:
【注意】 在 TIME_WAIT 状态中,如果 TCP client 端最后一次发送的 ACK 丢失了,它将重新发送。TIME_WAIT 状态中所需要的时间是依赖于实现方法的。典型的值为 30 秒、1 分钟和 2 分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。
【问题 1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当 Server 端收到 Client 端的 SYN 连接请求报文后,可以直接发送 SYN+ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。但是关闭连接时,当 Server 端收到 FIN 报文时,很可能并不会立即关闭 SOCKET,所以只能先回复一个 ACK 报文,告诉 Client 端,“你发的 FIN 报文我收到了”。只有等到我 Server 端所有的报文都发送完了,我才能发送 FIN 报文,因此不能一起发送。故需要四步握手。
【问题 2】为什么 TIME_WAIT 状态需要经过 2MSL(最大报文段生存时间)才能返回到 CLOSE 状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入 CLOSE 状态了,但是我们必须假象网络是不可靠的,有可能最后一个 ACK 丢失。所以 TIME_WAIT 状态就是用来重发可能丢失的 ACK 报文。
10054 错误码
有人问:select 写 socket 时,建立连接立马发送数据有时候收不到数据,还提示 10054,大多数情况下还是能收到的。一般收不到数据的时候,第二次再连接发送数据就能收到。请问是怎么回事呢?
一般来说是连接被对方重置。一个建立的连接被远程主机强行关闭,若远程主机上的进程异常终止运行(由于内存冲突或硬件故障),或者针对套接字执行了一次强行关闭,便会产生 10054 错误。针对强行关闭的情况,可用 SO_LINGER 套接字选项和 setsockopt 来配置一个套接字。
而对于描述的那个问题,我回忆了 TCP/IP 协议,认为会有如下两种情况:
- Client 在与 Server 进行三次握手时,Client 调用 connect 函数完成了连接的建立(客户端自以为建立了),而实际 Server 那边却没有收到最后一次握手 ack 的回复。这时触发了 Server 的 syn 触发器,重发 SYN+ACK 包,一般默认是重发 5 次,时间分别是 1 秒、2 秒、4 秒、8 秒、16 秒;而此时 Client 马上发送数据的话,Server 会发 RST 回复给 Client,那么此时 Client 就会提示 10054。
- 而另一种情况就是 Server 这边的 accept 队列满了(一般有 syn_recv 队列,accept 队列),那么此时 Server 会直接回复 RST(最新的 Linux 内核是这么实现的),当然也有可能会拒绝 Client 的请求,让 Client 自己自动断开请求。
群里的一位大神(冒泡)说实现第二种方式,也就是拒绝 Client 发过来的请求,原因如下:
- 服务器已经处理不过来了,再发 RST 加剧压力。
- 客户端如果丢包,会等到超时再重试,而收到 RST 的话有可能立刻重试,服务器压力会更大。
Linux 网络编程中 socket 常见错误码分析
- EINTR(4):阻塞的操作被取消阻塞的调用打断。如设置了发送接收超时,就会遇到这种错误。只能针对阻塞模式的 socket。读、写阻塞的 socket 时,-1 返回,错误号为 INTR。另外,如果出现 EINTR 即 errno 为 4,错误描述 Interrupted system call,操作也应该继续。如果 recv 的返回值为 0,那表明连接已经断开,接收操作也应该结束。
- ETIMEDOUT(110):
- 操作超时。一般设置了发送接收超时,遇到网络繁忙的情况,就会遇到这种错误。
- 服务器做了读数据做了超时限制,读时发生了超时。
- 错误被描述为 “connect time out”,即 “连接超时”,这种情况一般发生在服务器主机崩溃。此时客户 TCP 将在一定时间内(依具体实现)持续重发数据分节,试图从服务 TCP 获得一个 ACK 分节。当最终放弃尝试后(此时服务器未重新启动),内核将会向客户进程返回 ETIMEDOUT 错误。如果某个中间路由器判定该服务器主机已经不可达,则一般会响应 “destination unreachable”-“目的地不可达” 的 ICMP 消息,相应的客户进程返回的错误是 EHOSTUNREACH 或 ENETUNREACH。当服务器重新启动后,由于 TCP 状态丢失,之前所有的连接信息也不存在了,此时对于客户端发来请求将回应 RST。如果客户进程对检测服务器主机是否崩溃很有必要,要求即使客户进程不主动发送数据也能检测出来,那么需要使用其他技术,如配置 SO_KEEPALIVE Socket 选项,或实现某些心跳函数。
- EAGAIN:
- Send 返回值小于要发送的数据数目,会返回 EAGAIN 和 EINTR。
- recv 返回值小于请求的长度时说明缓冲区已经没有可读数据,但再读不一定会触发 EAGAIN,有可能返回 0 表示 TCP 连接已被关闭。
- 当 socket 是非阻塞时,如返回此错误,表示写缓冲队列已满,可以做延时后再重试。
- 在 Linux 进行非阻塞的 socket 接收数据时经常出现 Resource temporarily unavailable,errno 代码为 11(EAGAIN),表明在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏 socket 的同步,不用管它,下次循环接着 recv 就可以。对非阻塞 socket 而言,EAGAIN 不是一种错误。
- EPIPE:
- Socket 关闭,但是 socket 号并没有置 -1。继续在此 socket 上进行 send 和 recv,就会返回这种错误。这个错误会引发 SIGPIPE 信号,系统会将产生此 EPIPE 错误的进程杀死。所以,一般在网络程序中,首先屏蔽此消息,以免发生不及时设置 socket 进程被杀死的情况。
- write(…) on a socket that has been closed at the other end will cause a SIGPIPE.
- 错误被描述为 “broken pipe”,即 “管道破裂”,这种情况一般发生在客户进程不理会(或未及时处理)Socket 错误,继续向服务 TCP 写入更多数据时,内核将向客户进程发送 SIGPIPE 信号,该信号默认会使进程终止(此时该前台进程未进行 core dump)。结合上边的 ECONNRESET 错误可知,向一个 FIN_WAIT2 状态的服务 TCP(已 ACK 响应 FIN 分节)写入数据不成问题,但是写一个已接收了 RST 的 Socket 则是一个错误。
- EBADF:read(…) or write(…) on a locally closed socket will return EBADF.
- EFAULT:地址错误。
- EBUSY:
- ECONNREFUSED:
- 拒绝连接。一般发生在连接建立时。
- 拔服务器端网线测试,客户端设置 keep alive 时,recv 较快返回 0,先收到 ECONNREFUSED(Connection refused)错误码,其后都是 ETIMEOUT。
- 该错误是 connect() 返回的,所以它只能发生在客户端(如果客户端被定义为发起连接的一方)。
- ECONNRESET:
- 在客户端服务器程序中,客户端异常退出,并没有回收关闭相关的资源,服务器端会先收到 ECONNRESET 错误,然后收到 EPIPE 错误。
- 连接被远程主机关闭。有以下几种原因:
- 远程主机停止服务,重新启动。
- 当在执行某些操作时遇到失败,因为设置了 “keep alive” 选项,连接被关闭,一般与 ENETRESET 一起出现。
- 远程端执行了一个 “hard” 或者 “abortive” 的关闭。应用程序应该关闭 socket,因为它不再可用。当执行在一个 UDP socket 上时,这个错误表明前一个 send 操作返回一个 ICMP “port unreachable” 信息。
- 如果 client 关闭连接,server 端的 select 并不出错(不返回 -1,使用 select 对唯一一个 socket 进行 non-blocking 检测),但是写该 socket 就会出错,用的是 send。错误号:ECONNRESET. 读(recv)socket 并没有返回错误。
- 该错误被描述为 “connection reset by peer”,即 “对方复位连接”,这种情况一般发生在服务进程较客户进程提前终止。当服务进程终止时会向客户 TCP 发送 FIN 分节,客户 TCP 回应 ACK,服务 TCP 将转入 FIN_WAIT2 状态。此时如果客户进程没有处理该 FIN(如阻塞在其他调用上而没有关闭 Socket 时),则客户 TCP 将处于 CLOSE_WAIT 状态。当客户进程再次向 FIN_WAIT2 状态的服务 TCP 发送数据时,则服务 TCP 将立刻响应 RST。一般来说,这种情况还会引发另外的应用程序异常,客户进程在发送完数据后,往往会等待从网络 IO 接收数据,很典型的如 read 或 readline 调用,此时由于执行时序的原因,如果该调用发生在 RST 分节收到前执行的话,那么结果是客户进程会得到一个非预期的 EOF 错误。此时一般会输出 “server terminated prematurely”-“服务器过早终止” 错误。
- EINVAL:无效参数。提供的参数非法。有时也会与 socket 的当前状态相关,如一个 socket 并没有进入 listening 状态,此时调用 accept,就会产生 EINVAL 错误。
- EMFILE:打开了太多的 socket。对进程或者线程而言,每种实现方法都有一个最大的可用 socket 数目处理,或者是全局的,或者是局部的。
- EWOULDBLOCK(EAGAIN):资源暂时不可用。这个错误是从对非阻塞 socket 进行的不能立即结束的操作返回的,如当没有数据在队列中可以读时,调用 recv。并不是 fatal 错误,稍后操作可以被重复。调用在一个非阻塞的 SOCK_STREAM socket 上调用 connect 时会产生这个错误,因为有时连接建立必须消耗一定的时间。
- ENOTCONN:在一个没有建立连接的 socket 上,进行 read,write 操作会返回这个错误。出错的原因是 socket 没有标识地址。Setsoc 也可能会出错。
- ECONNRESET:Connection reset by peer. 连接被远程主机关闭。有以下几种原因:
- 远程主机停止服务,重新启动。
- 当在执行某些操作时遇到失败,因为设置了 “keep alive” 选项,连接被关闭,一般与 ENETRESET 一起出现。
- ECONNABORTED:
- 软件导致的连接取消。一个已经建立的连接被 host 方的软件取消,原因可能是数据传输超时或者是协议错误。
- 该错误被描述为 “software caused connection abort”,即 “软件引起的连接中止”。原因在于当服务和客户进程在完成用于 TCP 连接的 “三次握手” 后,客户 TCP 却发送了一个 RST(复位)分节,在服务进程看来,就在该连接已由 TCP 排队,等着服务进程调用 accept 的时候 RST 却到达了。POSIX 规定此时的 errno 值必须 ECONNABORTED。源自 Berkeley 的实现完全在内核中处理中止的连接,服务进程将永远不知道该中止的发生。服务器进程一般可以忽略该错误,直接再次调用 accept。
- ENETUNREACH:网络不可达。Socket 试图操作一个不可达的网络。这意味着 local 的软件知道没有路由到达远程的 host。
- ENETRESET:网络重置时丢失连接。由于设置了 “keep-alive” 选项,探测到一个错误,连接被中断。在一个已经失败的连接上试图使用 setsockopt 操作,也会返回这个错误。
- EINPROGRESS:操作正在进行中。一个阻塞的操作正在执行。
- ENOTSOCK:在非 socket 上执行 socket 操作。
- EDESTADDRREQ:需要提供目的地址。在一个 socket 上的操作需要提供地址。如往一个 ADDR_ANY 地址上进行 sendto 操作会返回这个错误。
- EMSGSIZE:消息体太长。发送到 socket 上的一个数据包大小比内部的消息缓冲区大,或者超过别的网络限制,或是用来接收数据包的缓冲区比数据包本身小。
- EPROTOTYPE:协议类型错误。标识了协议的 Socket 函数在不支持的 socket 上进行操作。如 ARPA Internet UDP 协议不能被标识为 SOCK_STREAM socket 类型。
- ENOPROTOOPT:该错误不是一个 Socket 连接相关的错误。errno 给出该值可能由于,通过 getsockopt 系统调用来获得一个套接字的当前选项状态时,如果发现了系统不支持的选项参数就会引发该错误。
- EPROTONOSUPPORT:不支持的协议。系统中没有安装标识的协议,或者是没有实现。如函数需要 SOCK_DGRAM socket,但是标识了 stream protocol。
- ESOCKTNOSUPPORT:Socket 类型不支持。指定的 socket 类型在其 address family 中不支持。如可选选中选项 SOCK_RAW,但实现并不支持 SOCK_RAW sockets。
连接过程可能出现的错误情况
- 如果客户机 TCP 协议没有接收到对它的 SYN 数据段的确认,函数以错误返回,错误类型为 ETIMEOUT。通常 TCP 协议在发送 SYN 数据段失败之后,会多次发送 SYN 数据段,在所有的发送都失败之后,函数以错误返回。
- 注:SYN(synchronize)位:请求连接。TCP 用这种数据段向对方 TCP 协议请求建立连接。在这个数据段中,TCP 协议将它选择的初始序列号通知对方,并且与对方协议协商最大数据段大小。SYN 数据段的序列号为初始序列号,这个 SYN 数据段能够被确认。当协议接收到对这个数据段的确认之后,建立 TCP 连接。
- 如果远程 TCP 协议返回一个 RST 数据段,函数立即以错误返回,错误类型为 ECONNREFUSED。当远程机器在 SYN 数据段指定的目的端口号处没有服务进程在等待连接时,远程机器的 TCP 协议将发送一个 RST 数据段,向客户机报告这个错误。客户机的 TCP 协议在接收到 RST 数据段后不再继续发送 SYN 数据段,函数立即以错误返回。
- 注:RST(reset)位:表示请求重置连接。当 TCP 协议接收到一个不能处理的数据段时,向对方 TCP 协议发送这种数据段,表示这个数据段所标识的连接出现了某种错误,请求 TCP 协议将这个连接清除。有 3 种情况可能导致 TCP 协议发送 RST 数据段:
- SYN 数据段指定的目的端口处没有接收进程在等待。
- TCP 协议想放弃一个已经存在的连接。
- TCP 接收到一个数据段,但是这个数据段所标识的连接不存在。接收到 RST 数据段的 TCP 协议立即将这条连接非正常地断开,并向应用程序报告错误。
- 注:RST(reset)位:表示请求重置连接。当 TCP 协议接收到一个不能处理的数据段时,向对方 TCP 协议发送这种数据段,表示这个数据段所标识的连接出现了某种错误,请求 TCP 协议将这个连接清除。有 3 种情况可能导致 TCP 协议发送 RST 数据段:
- 如果客户机的 SYN 数据段导致某个路由器产生 “目的地不可到达” 类型的 ICMP 消息,函数以错误返回,错误类型为 EHOSTUNREACH 或 ENETUNREACH。通常 TCP 协议在接收到这个 ICMP 消息之后,记录这个消息,然后继续几次发送 SYN 数据段,在所有的发送都告失败之后,TCP 协议检查这个 ICMP 消息,函数以错误返回。
- 注:ICMP(Internet 消息控制协议):Internet 的运行主要是由 Internet 的路由器来控制,路由器完成 IP 数据包的发送和接收,如果发送数据包时发生错误,路由器使用 ICMP 协议来报告这些错误。ICMP 数据包是封装在 IP 数据包的数据部分中进行传输的,其格式如下:
- ICMP 数据包主要有以下类型:
- 目的地不可到达:
- A. 目的主机未运行。
- B. 目的地址不存在。
- C. 路由表中没有目的地址对应的条目,因而路由器无法找到去往目的主机的路由。
- 超时:路由器将接收到的 IP 数据包的生存时间(TTL)域减 1,如果这个域的值变为 0,路由器丢弃这个 IP 数据包,并且发送这种 ICMP 消息。
- 参数出错:当 IP 数据包中有无效域时发送。
- 重定向:将一条新的路径通知主机。
- ECHO 请求、ECHO 回答:这两条消息用于测试目的主机是否可以到达。请求者向目的主机发送 ECHO 请求 ICMP 数据包,目的主机在接收到这个 ICMP 数据包之后,返回 ECHO 回答 ICMP 数据包。
- 时戳请求、时戳回答:ICMP 协议使用这两种消息从其他机器处获得其时钟的当前时间。
- 目的地不可到达:
- ICMP 数据包主要有以下类型:
- 注:ICMP(Internet 消息控制协议):Internet 的运行主要是由 Internet 的路由器来控制,路由器完成 IP 数据包的发送和接收,如果发送数据包时发生错误,路由器使用 ICMP 协议来报告这些错误。ICMP 数据包是封装在 IP 数据包的数据部分中进行传输的,其格式如下:
调用函数 connect 的过程中
当客户机 TCP 协议发送了 SYN 数据段的确认之后,TCP 状态由 CLOSED 状态转为 SYN_SENT 状态,在接收到对 SYN 数据段的确认之后,TCP 状态转换成 ESTABLISHED 状态,函数成功返回。如果调用函数 connect 失败,应该用 close 关闭这个套接字描述符,不能再次使用这个套接字描述符来调用函数 connect。
connect 函数的出错处理
- ETIMEOUT:connection timed out。目的主机不存在,没有返回任何响应,例如主机关闭。
- ECONNREFUSED:connection refused(硬错)。到达目的主机后,由于各种原因建立不了连接,主机返回 RST(复位)响应,例如主机监听进程未启用,TCP 取消连接等。
- EHOSTUNREACH:no route to host(软错)。路由上引发了一个目的地不可达的 ICMP 错误。
其中 (1) (3),客户端会进行定时多次重试,一定次数后才返回错误。另外,当 connect 连接失败时,sockfd 套接口不可用,必须关闭后重新 socket 分配才行。
via:
-
TCP 的状态:SYN, FIN, ACK, PSH, RST, URG 简介及 ACK 确认机制_psh ack-CSDN 博客
https://blog.csdn.net/llzhang_fly/article/details/108676070 -
TCP 的连接状态标识(SYN, FIN, ACK, PSH, RST, URG)_fin ack-CSDN 博客
https://blog.csdn.net/m0_37989944/article/details/120086446