【QT】Qt 网络

YoungMLet 2024-08-06 09:07:10 阅读 79

Qt 网络

Qt 网络1. UDP Socket(1)核心 API 概览(2)回显服务器(3)回显客户端

2. TCP Socket(1)核心 API 概览(2)回显服务器(3)回显客户端

Qt 网络

和多线程类似,Qt 为了⽀持跨平台,对网络编程的 API 也进行了重新封装。

在进行网络编程之前,需要在项目中的 .pro 文件中添加 network 模块。添加之后要手动编译⼀下项目,使 Qt Creator 能够加载对应模块的头文件。

1. UDP Socket

(1)核心 API 概览

主要的类有两个:QUdpSocketQNetworkDatagram

QUdpSocket 表示⼀个 UDPsocket 文件。

在这里插入图片描述

QNetworkDatagram 表示⼀个 UDP 数据报

在这里插入图片描述

(2)回显服务器

1、创建界面,包含⼀个 QListWidget 用来显示信息

2、 创建 QUdpSocket 成员,修改 widget.h:

<code>class Widget : public QWidget

{

Q_OBJECT

public:

Widget(QWidget *parent = nullptr);

~Widget();

private:

Ui::Widget *ui;

QUdpSocket *socket;

};

修改 widget.cpp,完成 socket 后续的初始化。⼀般来说, 要先连接信号槽, 再绑定端⼝。如果顺序反过来, 可能会出现端⼝绑定好了之后, 请求就过来了. 此时还没来得及连接信号槽. 那么这个请求就有可能错过了.

Widget::Widget(QWidget *parent)

: QWidget(parent), ui(new Ui::Widget)

{

ui->setupUi(this);

// 1. 设置窗⼝标题

this->setWindowTitle("服务器");

// 2. 实例化 socket

socket = new QUdpSocket(this);

// 3. 连接信号槽, 处理收到的请求

connect(socket, &QUdpSocket::readyRead, this, &Widget::processRequest);

// 4. 绑定端⼝

bool ret = socket->bind(QHostAddress::Any, 9090);

if (!ret)

{

QMessageBox::critical(nullptr, "服务器启动出错", socket->errorString());

return;

}

}

3、实现 processRequest , 完成处理请求的过程

读取请求并解析

根据请求计算响应

把响应写回到客户端

void Widget::processRequest()

{

// 1. 读取请求

const QNetworkDatagram &requestDatagram = socket->receiveDatagram();

QString request = requestDatagram.data();

// 2. 根据请求计算响应

const QString &response = process(request);

// 3. 把响应写回到客⼾端

QNetworkDatagram responseDatagram(response.toUtf8(),

requestDatagram.senderAddress(), requestDatagram.senderPort());

socket->writeDatagram(responseDatagram);

// 显⽰打印⽇志

QString log = "[" + requestDatagram.senderAddress().toString() + ":" +

QString::number(requestDatagram.senderPort()) + "] req: " + request + ", resp: " + response;

ui->listWidget->addItem(log);

}

4、实现 process 函数

由于我们此处是实现回显服务器. 所以 process ⽅法中并没有包含实质性的内容.

QString Widget::process(const QString& request)

{

return request;

}

(3)回显客户端

1、创建界面,包含⼀个 QLineEdit , QPushButton , QListWidget;

先使用⽔平布局把 QLineEditQPushButton 放好, 并设置这两个控件的垂直方向的 sizePolicy 为 Expanding

• 再使用垂直布局把 QListWidget 和上面的水平布局放好.

• 设置垂直布局的 layoutStretch 为 5, 1 (当然这个尺寸比例根据个人喜好微调).

在这里插入图片描述

2、在 widget.cpp 中, 先创建两个全局常量, 表⽰服务器的 IP 和 端⼝

<code>// 提前定义好服务器的 IP 和 端⼝

const QString& SERVER_IP = "127.0.0.1";

const quint16 SERVER_PORT = 9090;

3、创建 QUdpSocket 成员

修改 widget.h, 定义成员

class Widget : public QWidget

{

Q_OBJECT

public:

Widget(QWidget *parent = nullptr);

~Widget();

private:

Ui::Widget *ui;

// 创建 socket 成员

QUdpSocket *socket;

};

修改 widget.cpp, 初始化 socket

Widget::Widget(QWidget *parent)

: QWidget(parent), ui(new Ui::Widget)

{

ui->setupUi(this);

// 1. 设置窗⼝名字

this->setWindowTitle("客⼾端");

// 2. 实例化 socket

socket = new QUdpSocket(this);

}

4、给发送按钮 slot 函数, 实现发送请求

void Widget::on_pushButton_clicked()

{

// 1. 获取到输⼊框的内容

const QString &text = ui->lineEdit->text();

// 2. 构造请求数据

QNetworkDatagram requestDatagram(text.toUtf8(), QHostAddress(SERVER_IP),

SERVER_PORT);

// 3. 发送请求

socket->writeDatagram(requestDatagram);

// 4. 消息添加到列表框中

ui->listWidget->addItem("客户端说: " + text);

// 5. 清空输⼊框

ui->lineEdit->setText("");

}

5、再次修改 Widget 的构造函数, 通过信号槽, 来处理服务器的响应.

connect(socket, &QUdpSocket::readyRead, this, [=]()

{

const QNetworkDatagram responseDatagram = socket->receiveDatagram();

QString response = responseDatagram.data();

ui->listWidget->addItem(QString("服务器说: ") + response);

});

2. TCP Socket

(1)核心 API 概览

核⼼类是两个: QTcpServerQTcpSocket

QTcpServer 用于监听端口, 和获取客户端连接。

在这里插入图片描述

QTcpSocket 用户客户端和服务器之间的数据交互。

在这里插入图片描述

QByteArray 用于表示⼀个字节数组. 可以很⽅便的和 QString 进行相互转换.

例如:

使用 QString 的构造函数即可把 QByteArray 转成 QString.使用 QString 的 toUtf8 函数即可把 QString 转成 QByteArray.

(2)回显服务器

1、创建界⾯. 包含⼀个 QListWidget , ⽤于显⽰收到的数据.

在这里插入图片描述

2、创建 QTcpServer 并初始化

修改 widget.h, 添加 QTcpServer 指针成员

<code>class Widget : public QWidget

{

Q_OBJECT

public:

Widget(QWidget *parent = nullptr);

~Widget();

private:

Ui::Widget *ui;

// 创建 QTcpServer

QTcpServer *tcpServer;

};

修改 widget.cpp, 实例化 QTcpServer 并进⾏后续初始化操作.

设置窗⼝标题

实例化 TCP server. (⽗元素设为当前控件, 会在⽗元素销毁时被⼀起销毁).

通过信号槽, 处理客⼾端建⽴的新连接.

监听端口

Widget::Widget(QWidget *parent)

: QWidget(parent), ui(new Ui::Widget)

{

ui->setupUi(this);

// 1. 设置窗⼝标题

this->setWindowTitle("服务器");

// 2. 实例化 TCP server

tcpServer = new QTcpServer(this);

// 3. 通过信号槽, 处理客⼾端建⽴的新连接.

connect(tcpServer, &QTcpServer::newConnection, this,

&Widget::processConnection);

// 4. 监听端⼝

bool ret = tcpServer->listen(QHostAddress::Any, 9090);

if (!ret)

{

QMessageBox::critical(nullptr, "服务器启动失败!", tcpServer - > errorString());

exit(1);

}

}

3、继续修改 widget.cpp, 实现处理连接的具体⽅法 processConnection

获取到新的连接对应的 socket.

通过信号槽, 处理收到请求的情况

通过信号槽, 处理断开连接的情况

void Widget::processConnection()

{

// 1. 获取到新的连接对应的 socket.

QTcpSocket *clientSocket = tcpServer->nextPendingConnection();

QString log = QString("[") + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客⼾端上线!";

ui->listWidget->addItem(log);

// 2. 通过信号槽, 处理收到请求的情况

connect(clientSocket, &QTcpSocket::readyRead, this, [=]()

{

// a) 读取请求

QString request = clientSocket->readAll();

// b) 根据请求处理响应

const QString& response = process(request);

// c) 把响应写回客⼾端

clientSocket->write(response.toUtf8());

QString log = QString("[") + clientSocket->peerAddress().toString()

+ ":" + QString::number(clientSocket->peerPort()) + "] req: " +

request + ", resp: " + response;

ui->listWidget->addItem(log);

});

// 3. 通过信号槽, 处理断开连接的情况

connect(clientSocket, &QTcpSocket::disconnected, this, [=]()

{

QString log = QString("[") + clientSocket->peerAddress().toString()

+ ":" + QString::number(clientSocket->peerPort()) + "] 客户端下线!";

ui->listWidget->addItem(log);

// 删除 clientSocket

clientSocket->deleteLater();

});

}

4、实现 process 方法, 实现根据请求处理响应

由于我们此处是实现回显服务器. 所以 process ⽅法中并没有包含实质性的内容

QString Widget::process(const QString &request)

{

return request;

}

(3)回显客户端

1、创建界⾯;包含⼀个 QLineEdit , QPushButton , QListWidget

先使用⽔平布局把 QLineEditQPushButton 放好, 并设置这两个控件的垂直方向的 sizePolicyExpanding再使用垂直布局把 QListWidget 和上面的水平布局放好.设置垂直布局的 layoutStretch 为 5, 1 (当然这个尺寸比例根据个⼈喜好微调).

在这里插入图片描述

2、创建 QTcpSocket 并实例化

修改 widget.h, 创建成员.

<code>class Widget : public QWidget

{

Q_OBJECT

public:

Widget(QWidget *parent = nullptr);

~Widget();

private:

Ui::Widget *ui;

// 新增 QTcpSocket

QTcpSocket *socket;

};

修改 widget.cpp, 对 QTcpSocket 进行实例化

设置窗⼝标题

实例化 socket 对象 (⽗元素设为当前控件, 会在⽗元素销毁时被⼀起销毁).

和服务器建⽴连接.

等待并确认连接是否出错.

Widget::Widget(QWidget *parent)

: QWidget(parent), ui(new Ui::Widget)

{

ui->setupUi(this);

// 1. 设置窗⼝标题.

this->setWindowTitle("客⼾端");

// 2. 实例化 socket 对象.

socket = new QTcpSocket(this);

// 3. 和服务器建⽴连接.

socket->connectToHost("127.0.0.1", 9090);

// 4. 等待并确认连接是否出错.

if (!socket->waitForConnected())

{

QMessageBox::critical(nullptr, "连接服务器出错!", socket->errorString());

exit(1);

}

}

3、修改 widget.cpp, 给按钮增加点击的 slot 函数, 实现发送请求给服务器.

void Widget::on_pushButton_clicked()

{

// 获取输⼊框的内容

const QString &text = ui->lineEdit->text();

// 清空输⼊框内容

ui->lineEdit->setText("");

// 把消息显⽰到界⾯上

ui->listWidget->addItem(QString("客⼾端说: ") + text);

// 发送消息给服务器

socket->write(text.toUtf8());

}

4、修改 widget.cpp 中的 Widget 构造函数, 通过信号槽, 处理收到的服务器的响应.

// 处理服务器返回的响应.

connect(socket, &QTcpSocket::readyRead, this, [=]()

{

QString response = socket->readAll();

qDebug() << response;

ui->listWidget->addItem(QString("服务器说: ") + response);

});



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。