Hi,今天用一個小例子,陳述一下 Qt 里使用 TCP 通訊的流程。
代碼鏈接:
https://doc.qt.io/qt-5/examples.NETwork.html
運行效果:
Server 端
運行效果:
顯示 IP + 端口,然后靜靜地的等待客戶端的連接。
源碼文件:
msg_server/
├── msg_server.pro
├── main.cpp
├── server.cpp
└── server.h
源碼分析如下。
創建 TCP Server
在構造函數中進行初始化:
// server.cpp
Server::Server(QWidget *parent)
: QDialog(parent)
, statusLabel(new QLabel)
{
// 建立 TCP Server,并監聽
tcpServer = new QTcpServer(this);
tcpServer->listen()
// 獲取 Server 的 IP 地址,并用其初始化 UI
[...]
// 一旦有 TCP 連接,則調用 sendMsg() 發送數據給客戶端
connect(tcpServer, &QTcpServer::newConnection, this, &Server::sendMsg);
}
要點:
1、QTcpServer 是對 TCP-based server 的封裝。
2、QTcpServer::listen() 用于監聽是否有客戶端發起連接。
3、一旦有客戶端訪問,QTcpServer 會發出 newConnection() 信號,我們通過綁定槽函數 sendMsg() 以實現發送消息的功能。
獲取 Server IP
在界面上顯示服務端的 IP 信息:
// server.cpp
Server::Server(QWidget *parent)
: QDialog(parent)
, statusLabel(new QLabel)
{
// 創建 TCP Server
[...]
QString ipAddress;
// 獲得所有的 IP 地址
QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
// 解析出第一個可用的 IPv4 地址
for (int i = 0; i < ipAddressesList.size(); ++i) {
if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
ipAddressesList.at(i).toIPv4Address()) {
ipAddress = ipAddressesList.at(i).toString();
break;
}
}
// 初始化 UI
[...]
}
要點:
1、QNetworkInterface 是對網絡接口 (例如 lo、eth0...) 的封裝。
2、QNetworkInterface::allAddresses() 會返回系統里所有的 IP 地址。
3、QHostAddress 是對 IP 地址(IPv4、IPv6) 的封裝。
4、QHostAddress::toIPv4Address() 會將點分式的 IPv4 地址轉換為數字式,例如 127.0.0.1 會被轉換為 0x7F000001,失敗則返回 0。
給客戶端發送消息
當有客戶端連接到來時,槽函數 sendMsg()會被調用 :
// server.cpp
void Server::sendMsg()
{
// Prepare message
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out << message[QRandomGenerator::global()->bounded(message.size())];
// Get pending connection
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
connect(clientConnection, &QAbstractSocket::disconnected,
clientConnection, &QObject::deleteLater);
// Send message
clientConnection->write(block);
clientConnection->disconnectFromHost();
}
要點:
1、QTcpSocket 是對 TCP Socket 的封裝。
2、為了與主機無關 (字節序等),這里選用 QByteArray 以二進制的格式來存儲數據。使用 QDataStream 可以輕松地將 Message 寫到 QByteArray 里。
3、QDataStream 從各種 IO 設備 (QIODeice 的子類),例如 QByteArray、文件 (QFile) 等讀寫二進制數據。
4、從 QTcpServer::nextPendingConnection() 獲得客戶端的 Socket。
5、用 QTcpSocket::write() 將 Message 通過網絡發給客戶端。
6、最后,通過 QTcpSocket::disconnectFromHost 斷開連接,它會等待直到數據成功被寫出去。
Client 端
運行效果:
每次點擊 "Get Message" 按鈕,客戶端都會從服務端隨機獲取到一條問候信息。
源碼文件:
msg_client/
├── msg_client.pro
├── client.cpp
├── client.h
└── main.cpp
創建 TCP Socket
// client.cpp
Client::Client(QWidget *parent)
: QDialog(parent)
, hostCombo(new QComboBox)
, portLineEdit(new QLineEdit)
, statusLabel(new QLabel(tr("This examples requires that you run then Message Server example as well.")))
, getMsgButton(new QPushButton(tr("Get Message")))
, tcpSocket(new QTcpSocket(this))
{
// Init UI
[...]
// Setup QDataStream's source
in.setDevice(tcpSocket);
in.setVersion(QDataStream::Qt_5_10);
// Setup signal & slot
connect(getMsgButton, &QAbstractButton::clicked,
this, &Client::requestNewMsg);
connect(tcpSocket, &QIODevice::readyRead, this, &Client::readMsg);
}
要點:
1、用 QTcpSocket 創建 TCP Socket。
2、將 QDataStream 數據流的輸入源設置為 Socket。
3、設置信號槽:當 Socket 有數據時,調用 readMsg() 將其讀走。
從服務端讀取消息
當用戶點擊 "Get Message" 按鈕時,requestNewMsg() 會被調用
// client.cpp
void Client::requestNewMsg()
{
getMsgButton->setEnabled(false);
tcpSocket->abort();
tcpSocket->connectToHost(hostCombo->currentText(),
portLineEdit->text().toInt());
}
要點:
1、QTcpSocket::connectToHost() 向服務端發起連接。
2、該函數沒有返回值,是因為當有錯誤發生時,socket 會發出 error() 信號,我們可以通過綁定對應的槽函數進行錯誤處理。
3、成功連接后,服務端會隨機發送一條信息過來,客戶端接收到消息后,readMsg() 會被調用。
// client.cpp
void Client::readMsg()
{
QString ;
in >> nextFortune;
statusLabel->setText(nextFortune);
getMsgButton->setEnabled(true);
}
很簡單的流操作,讀到信息后,將其顯示在界面上。
總結
用一張圖總結一下 Qt TCP 通訊流程:
原文鏈接:
https://mp.weixin.qq.com/s/aCyV4HBwzTUZbCrdKsLtBw文章轉載自:老吳嵌入式
文章來源于:一小例子,了解 TCP 通訊流程 | Qt 示例
原文鏈接:一小例子,了解 TCP 通訊流程 | Qt 示例
版權聲明:本文來源于網絡,免費傳達知識,版權歸原作者所有,如涉及作品版權問題,請聯系我進行刪除