TCP 协议简介

Qt的网络模块主要集中在QtNetwork模块中,提供了对TCP/IP、UDP、HTTP等多种网络协议的支持。通过这些类,开发者可以轻松实现客户端、服务器端的通信,处理网络数据传输,进行HTTP请求等操作。

注意QT 网络需要在pro中包含network库

QT += network

TCP 协议简介

TCP(Transmission Control Protocol)是一种面向连接、可靠、基于字节流的传输层协议。它确保数据包按顺序到达并进行错误检查。

三次握手

为了保证客户端和服务器端的可靠连接,TCP建立连接时必须要进行三次会话,也叫TCP三次握手,进行三次握手的目的是为了确认双方的接收能力和发送能力是否正常。

举个栗子
班主任 和 王同学打电话

班主任:你好!小王同学,听得到吗?(一次会话)
王同学:听到了,老师,你能听到吗 (二次会话)
班主任:听到了,你过来上课吧 (三次会话)

通过这个例子我们可以知道三次会话的目的就是为了确保双方的连接正常,同理,TCP三次握手也是这个过程,下面用图文形式来解释一下TCP三次握手。

TCP建立连接过程

最开始的时候客户端和服务器都是处于CLOSED关闭状态。主动打开连接的为客户端,被动打开连接的是服务器。

TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了 LISTEN 监听状态

第一次握手 TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT 同步已发送状态

第二次握手 TCP服务器收到请求报文后,如果同意连接,则会向客户端发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了 SYN-RCVD 同步收到状态

第三次握手 TCP客户端收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED已建立连接状态 触发三次握手

有人可能会很疑惑为什么要进行第三次握手?
主要原因:防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误

TCP四次挥手

建立TCP连接需要三次握手,终止TCP连接需要四次挥手

举个例子
张三和李四的对话

张三:好的,那我先走了
李四:好的,那你走吧
李四:那我也走了?
张三:好的,你走吧

数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。

第一次挥手 客户端发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态

第二次挥手 服务器端接收到连接释放报文后,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT 关闭等待状态

第三次挥手 客户端接收到服务器端的确认请求后,客户端就会进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文,服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

第四次挥手 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态,但此时TCP连接还未终止,必须要经过2MSL后(最长报文寿命),当客户端撤销相应的TCB后,客户端才会进入CLOSED关闭状态,服务器端接收到确认报文后,会立即进入CLOSED关闭状态,到这里TCP连接就断开了,四次挥手完成

TIME_WAIT状态的作用

在第四次挥手中,主动关闭的一方(通常是客户端)进入TIME_WAIT状态。这一状态的存在有两个主要目的:

  1. 确保最后的ACK包能够被对方接收:如果最后的ACK包由于网络问题丢失,TIME_WAIT状态允许主动关闭方在等待期间重传ACK包,确保被动方能够收到并正确关闭连接。
  • 如果被动关闭方没有收到ACK包(因为ACK在传输过程中丢失),它会重新发送FIN包。
  • 主动关闭方在TIME_WAIT状态下监听到这个重新发送的FIN包后,会重新发送ACK包,确保被动关闭方最终收到了确认。

  1. 防止旧的重复数据包干扰新的连接:感谢有限的端口资源和序列号重复使用,通过TIME_WAIT状态,确保网络上旧的包完全消失,避免干扰未来使用相同端口和序列号的新连接。

为什么需要四次挥手

确保双向关闭

由于TCP是全双工的,双方需要分别确认发送和接收的结束。如果只使用两次挥手(类似于建立连接的三次握手),可能无法确保双方都已完成数据传输,并且确认了对方的关闭请求。

防止数据丢失

在连接终止过程中,网络延迟、丢包等问题可能导致某些包丢失。通过四次挥手,每一步的确认确保双方都已经安全地收到了对方的关闭请求,避免数据丢失或连接状态不一致。

保证资源释放

四次挥手确保双方都完成了收尾工作,包括缓冲区中的数据已全部发送和接收,资源能够被正确释放。如果过早关闭连接,可能会导致数据丢失或资源泄漏。

编码流程

客户端和服务器通信,客户端需要执行的流程包括如下:

1 客户端创建QTcpSocket对象,调用connect连接到服务器。

2 获取连接状态,如果连接失败则提示或重连

3 读写收发数据

服务器的通信流程包括:

1 服务器创建QTcpServer对象

2 调用listen方法监听新的连接

3 通过捕获newConnection信号编写处理新连接的槽函数。或者重写incomingConnection槽函数处理新连接

4 基于新连接返回的QTcpSocket对象,连接读信号,并实现读处理的槽函数即可。

5 基于新连接返回的QTcpSocket对象,连接断开信号,并实现断开连接处理的槽函数。

使用TCP需要包含必要头文件

数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。

第一次挥手 客户端发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态

第二次挥手 服务器端接收到连接释放报文后,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT 关闭等待状态

第三次挥手 客户端接收到服务器端的确认请求后,客户端就会进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文,服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

第四次挥手 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态,但此时TCP连接还未终止,必须要经过2MSL后(最长报文寿命),当客户端撤销相应的TCB后,客户端才会进入CLOSED关闭状态,服务器端接收到确认报文后,会立即进入CLOSED关闭状态,到这里TCP连接就断开了,四次挥手完成

TIME_WAIT状态的作用

在第四次挥手中,主动关闭的一方(通常是客户端)进入TIME_WAIT状态。这一状态的存在有两个主要目的:

  1. 确保最后的ACK包能够被对方接收:如果最后的ACK包由于网络问题丢失,TIME_WAIT状态允许主动关闭方在等待期间重传ACK包,确保被动方能够收到并正确关闭连接。
  • 如果被动关闭方没有收到ACK包(因为ACK在传输过程中丢失),它会重新发送FIN包。
  • 主动关闭方在TIME_WAIT状态下监听到这个重新发送的FIN包后,会重新发送ACK包,确保被动关闭方最终收到了确认。

  1. 防止旧的重复数据包干扰新的连接:感谢有限的端口资源和序列号重复使用,通过TIME_WAIT状态,确保网络上旧的包完全消失,避免干扰未来使用相同端口和序列号的新连接。

为什么需要四次挥手

确保双向关闭

由于TCP是全双工的,双方需要分别确认发送和接收的结束。如果只使用两次挥手(类似于建立连接的三次握手),可能无法确保双方都已完成数据传输,并且确认了对方的关闭请求。

防止数据丢失

在连接终止过程中,网络延迟、丢包等问题可能导致某些包丢失。通过四次挥手,每一步的确认确保双方都已经安全地收到了对方的关闭请求,避免数据丢失或连接状态不一致。

保证资源释放

四次挥手确保双方都完成了收尾工作,包括缓冲区中的数据已全部发送和接收,资源能够被正确释放。如果过早关闭连接,可能会导致数据丢失或资源泄漏。

编码流程

客户端和服务器通信,客户端需要执行的流程包括如下:

1 客户端创建QTcpSocket对象,调用connect连接到服务器。

2 获取连接状态,如果连接失败则提示或重连

3 读写收发数据

服务器的通信流程包括:

1 服务器创建QTcpServer对象

2 调用listen方法监听新的连接

3 通过捕获newConnection信号编写处理新连接的槽函数。或者重写incomingConnection槽函数处理新连接

4 基于新连接返回的QTcpSocket对象,连接读信号,并实现读处理的槽函数即可。

5 基于新连接返回的QTcpSocket对象,连接断开信号,并实现断开连接处理的槽函数。

使用TCP需要包含必要头文件

#include <QTcpSocket>
#include <QTcpServer>
#include <QHostAddress>

QTcpSocket 类

QTcpSocket 是 Qt 提供的用于TCP客户端通信的类。主要功能包括:

  • 连接到服务器(connectToHost
  • 读取与写入数据(read, write
  • 处理连接状态(connected, disconnected 等信号)

常用信号:

  • connected(): 成功连接到服务器时发出。
  • disconnected(): 断开连接时发出。
  • readyRead(): 有新数据可读时发出。
  • errorOccurred(QAbstractSocket::SocketError): 出现错误时发出。

QTcpServer 类

QTcpServer 是 Qt 提供的用于TCP服务器管理的类。主要功能包括:

  • 监听端口(listen
  • 等待客户端连接(newConnection 信号)
  • 管理多个客户端连接

常用方法:

  • listen(QHostAddress, quint16): 开始监听指定地址和端口。
  • nextPendingConnection(): 获取下一个等待连接的QTcpSocket对象。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐