鱼弦:CSDN内容合伙人、CSDN新星导师、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen)
Qt QTcpSocket是一个用于实现TCP协议的客户端和服务器端的类,它可以连接到指定的主机和端口,提供了丰富的方法和信号来发送和接收数据。QTcpSocket的底层架构由以下几个部分组成:
- QTcpSocketPrivate
QTcpSocketPrivate是QTcpSocket的私有类,它负责QTcpSocket的底层实现。QTcpSocketPrivate包含了一个QAbstractSocketEngine类型的成员变量,用于管理底层的套接字通信。在QTcpSocket的构造函数中,会创建一个QTcpSocketPrivate对象,并将其作为QTcpSocket的私有数据。
- QAbstractSocketEngine
QAbstractSocketEngine是一个抽象类,它定义了QTcpSocket底层套接字通信的接口。具体的套接字通信实现由各个平台的QAbstractSocketEngine子类来实现。在QTcpSocketPrivate中,会创建一个具体的QAbstractSocketEngine子类对象,用于实现QTcpSocket的底层套接字通信。
- QSocketNotifier
QSocketNotifier是一个Qt中用于通知文件描述符状态变化的类,它可以监视文件描述符的读写状态,并在状态发生变化时发送信号通知应用程序。在QTcpSocketPrivate中,会使用QSocketNotifier来监听底层套接字通信的读写状态,并在状态发生变化时发送信号通知QTcpSocket。
下面是 QTcpSocket 的基本实现架构图:
+-------------------+
| QTcpSocket |
+-------------------+
| - socketDescriptor|
+-------------------+
/_\
|
| 继承
|
+-------------------+
| QAbstractSocket |
+-------------------+
| - socketState |
| - socketError |
| - readBuffer |
| - writeBuffer |
| - readNotifier |
| - writeNotifier |
| - exceptionNotifier|
+-------------------+
/_\
|
| 继承
|
+-------------------+
| QObject |
+-------------------+
可以看出,QTcpSocket 继承自 QAbstractSocket 和 QObject 两个类,其中 QAbstractSocket 提供了基本的套接字操作(如连接、断开连接、发送数据等),而 QObject 则提供了对象的基本功能(如信号和槽、对象名称等)。
在 QTcpSocket 类中,socketDescriptor 属性表示底层使用的套接字描述符,它是一个整数值。QTcpSocket 的构造函数中会通过 socket() 函数创建一个新的套接字,并将其与 socketDescriptor 属性关联起来。QTcpSocket 的 connectToHost() 函数会调用底层的 connect() 函数来建立 TCP 连接。QTcpSocket 的 bind() 函数会调用底层的 bind() 函数来将套接字绑定到指定的本地地址和端口上。QTcpSocket 的 listen() 函数会调用底层的 listen() 函数来开始监听连接请求。QTcpSocket 的 accept() 函数会调用底层的 accept() 函数来接受连接请求并创建一个新的套接字。
QTcpSocket 通过 readNotifier 和 writeNotifier 成员变量来监听套接字的可读和可写事件,当套接字有数据可读或可以写入数据时,就会触发相应的信号(如 readyRead 和 bytesWritten 信号)来通知应用程序。当套接字出现错误时,socketError 信号会被触发,应用程序可以通过该信号来处理错误。
在QTcpSocket中,我们可以使用以下方法来实现TCP客户端和服务器端的通信:
- connectToHost()
connectToHost()方法用于连接到指定的主机和端口号,它接受两个参数,第一个参数是一个字符串,表示要连接的主机名或IP地址,第二个参数是一个整数,表示要连接的端口号。例如:
QTcpSocket socket;
socket.connectToHost("www.example.com", 80);
- disconnectFromHost()
disconnectFromHost()方法用于断开与主机的连接,它不接受任何参数。例如:
QTcpSocket socket;
socket.disconnectFromHost();
- write()
write()方法用于向连接的主机发送数据,它接受一个字节数组作为参数,表示要发送的数据。例如:
QTcpSocket socket;
socket.write("Hello, world!");
- flush()
flush()方法用于刷新缓冲区,将缓冲区中的数据发送出去,它不接受任何参数。例如:
QTcpSocket socket;
socket.write("Hello, world!");
socket.flush();
- read()
read()方法用于从连接的主机读取数据,它接受一个整数作为参数,表示要读取的数据长度。例如:
QTcpSocket socket;
socket.connectToHost("www.example.com", 80);
socket.write("GET / HTTP/1.0\r\n\r\n");
socket.flush();
QByteArray data = socket.readAll();
- close()
close()方法用于关闭套接字,它不接受任何参数。例如:
QTcpSocket socket;
socket.close();
- waitForConnected()
waitForConnected()方法用于等待连接完成,它接受一个整数作为参数,表示等待的超时时间(毫秒)。例如:
QTcpSocket socket;
socket.connectToHost("www.example.com", 80);
if (socket.waitForConnected(5000)) {
qDebug() << "Connected";
} else {
qDebug() << "Connection timed out";
}
- waitForReadyRead()
waitForReadyRead()方法用于等待套接字有数据可读,它接受一个整数作为参数,表示等待的超时时间(毫秒)。例如:
QTcpSocket socket;
socket.connectToHost("www.example.com", 80);
socket.write("GET / HTTP/1.0\r\n\r\n");
socket.flush();
if(socket.waitForReadyRead(5000))) {
QByteArray data = socket.readAll();
qDebug() << data;
} else {
qDebug() << "No data received";
}
- error()
error()方法用于获取套接字的错误代码,它返回一个QAbstractSocket::SocketError枚举类型的值。例如:
QTcpSocket socket;
socket.connectToHost("www.example.com", 80);
if (socket.waitForConnected(5000)) {
qDebug() << "Connected";
} else {
qDebug() << "Connection error:" << socket.error();
}
以上是QTcpSocket类的主要方法,除此之外还有许多其他方法和信号,可以根据实际需求来选择使用。以下是一个简单的QTcpSocket示例代码,演示了如何实现TCP客户端的通信:
#include <QTcpSocket>
#include <QDebug>
int main()
{
QTcpSocket socket;
socket.connectToHost("www.example.com", 80);
if (socket.waitForConnected(5000)) {
qDebug() << "Connected";
socket.write("GET / HTTP/1.0\r\n\r\n");
socket.flush();
if (socket.waitForReadyRead(5000)) {
QByteArray data = socket.readAll();
qDebug() << data;
} else {
qDebug() << "No data received";
}
} else {
qDebug() << "Connection error:" << socket.error();
}
socket.close();
return 0;
}
这个示例代码实现了连接到www.example.com的80端口,发送一个HTTP GET请求,并读取服务器返回的数据。实际使用中,可以根据需要对代码进行修改和扩展。