开源 C++ QT QML 开发(十一)通讯--TCP服务器端
文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
开源 C++ QT QML 开发(一)基本介绍
开源 C++ QT QML 开发(二)工程结构
开源 C++ QT QML 开发(三)常用控件
开源 C++ QT QML 开发(四)复杂控件--Listview
开源 C++ QT QML 开发(五)复杂控件--Gridview
开源 C++ QT QML 开发(六)自定义控件--波形图
开源 C++ QT QML 开发(七)自定义控件--仪表盘
开源 C++ QT QML 开发(八)自定义控件--圆环
开源 C++ QT QML 开发(九)文件--文本和二进制
开源 C++ QT QML 开发(十)通讯--串口
开源 C++ QT QML 开发(十一)通讯--TCP服务器端
开源 C++ QT QML 开发(十二)通讯--TCP客户端
推荐链接:
开源 C# 快速开发(一)基础知识
开源 C# 快速开发(二)基础控件
开源 C# 快速开发(三)复杂控件
开源 C# 快速开发(四)自定义控件--波形图
开源 C# 快速开发(五)自定义控件--仪表盘
开源 C# 快速开发(六)自定义控件--圆环
开源 C# 快速开发(七)通讯--串口
开源 C# 快速开发(八)通讯--Tcp服务器端
开源 C# 快速开发(九)通讯--Tcp客户端
开源 C# 快速开发(十)通讯--http客户端
开源 C# 快速开发(十一)线程
开源 C# 快速开发(十二)进程监控
开源 C# 快速开发(十三)进程--管道通讯
开源 C# 快速开发(十四)进程--内存映射
开源 C# 快速开发(十五)进程--windows消息
开源 C# 快速开发(十六)数据库--sqlserver增删改查
本章节主要内容是:介绍qml通讯中的tcp连接服务器端例子,实现ip和port参数可设,数据发送和接收。
1.代码分析
2.所有源码
3.效果演示
一、代码分析
一、C++后端详细分析 (tcp_server.h/cpp)
1.1 头文件分析 (tcp_server.h)
类声明和属性定义
class TcpServer : public QObject
{Q_OBJECT// QML属性声明Q_PROPERTY(bool isListening READ isListening NOTIFY isListeningChanged)Q_PROPERTY(QString statusMessage READ statusMessage NOTIFY statusMessageChanged)
// ... 其他属性
作用:声明6个QML可绑定的属性,每个属性包含READ方法和对应的信号核心方法声明
public:explicit TcpServer(QObject *parent = nullptr);~TcpServer();// QML可调用方法Q_INVOKABLE bool startServer(const QString &ip, quint16 port);Q_INVOKABLE void stopServer();Q_INVOKABLE void sendToAllClients(const QString &message);// ... 其他方法
设计特点:
使用Q_INVOKABLE使C++方法在QML中可用
明确的资源管理(析构函数中调用stopServer)
1.2 实现文件分析 (tcp_server.cpp)
构造函数
TcpServer::TcpServer(QObject *parent): QObject(parent), m_tcpServer(new QTcpServer(this)), m_isListening(false), m_clientCount(0), m_currentPort(0)
{connect(m_tcpServer, &QTcpServer::newConnection, this, &TcpServer::onNewConnection);updateStatusMessage("服务器未启动");
}
关键点:
父子对象关系管理(使用this作为parent)
信号槽连接:新连接→onNewConnection
初始状态设置
IP地址获取函数
QStringList TcpServer::getAvailableIPs()
{QStringList ipAddresses;ipAddresses << "0.0.0.0 (所有网络接口)";ipAddresses << "127.0.0.1 (本地回环)";// 获取所有IPv4地址并过滤QList<QHostAddress> allAddresses = QNetworkInterface::allAddresses();for (const QHostAddress &address : allAddresses) {if (address.protocol() == QAbstractSocket::IPv4Protocol &&address != QHostAddress::LocalHost) {// 复杂的网卡过滤逻辑if (!interfaceName.contains("Virtual", Qt::CaseInsensitive) &&!interfaceName.contains("VPN", Qt::CaseInsensitive)) {ipAddresses.append(QString("%1 (%2)").arg(ip).arg(interfaceName));}}}return ipAddresses;
}
算法分析:
添加特殊地址选项
遍历所有网络接口
过滤IPv4和非回环地址
排除虚拟网卡和VPN
格式化为"IP (网卡名)"的友好格式服务器启动函数
bool TcpServer::startServer(const QString &ip, quint16 port)
{if (m_tcpServer->isListening()) {m_tcpServer->close(); // 先关闭已有服务}QHostAddress hostAddress;// IP地址解析逻辑if (ip.startsWith("0.0.0.0")) {hostAddress = QHostAddress::Any;m_currentIP = "0.0.0.0";} else if (ip.contains("127.0.0.1")) {// ... 处理回环地址} else {// 提取纯IP地址(去除描述文本)QString cleanIP = ip;int spaceIndex = ip.indexOf(' ');if (spaceIndex > 0) {cleanIP = ip.left(spaceIndex);}// IP格式验证if (!hostAddress.setAddress(cleanIP)) {updateStatusMessage("IP地址格式错误: " + cleanIP);return false;}m_currentIP = cleanIP;}m_currentPort = port;// 启动监听if (!m_tcpServer->listen(hostAddress, port)) {updateStatusMessage("启动服务器失败: " + m_tcpServer->errorString());return false;}// 更新状态并发射信号m_isListening = true;updateStatusMessage(QString("服务器已启动 - IP: %1 端口: %2").arg(m_currentIP).arg(m_currentPort));emit isListeningChanged();emit currentIPChanged();emit currentPortChanged();return true;
}
关键处理:
IP地址智能解析(支持带描述的格式)
错误处理和用户反馈
状态同步和信号发射新连接处理
void TcpServer::onNewConnection()
{QTcpSocket *clientSocket = m_tcpServer->nextPendingConnection();// 连接客户端信号connect(clientSocket, &QTcpSocket::disconnected, this, &TcpServer::onClientDisconnected);connect(clientSocket, &QTcpSocket::readyRead, this, &TcpServer::onClientReadyRead);// 更新客户端列表m_clients.append(clientSocket);m_clientCount = m_clients.size();// 日志记录QString clientInfo = QString("%1:%2").arg(clientSocket->peerAddress().toString()).arg(clientSocket->peerPort());appendReceivedData(QString("[新客户端连接]: %1").arg(clientInfo));updateStatusMessage(QString("服务器运行中 - IP: %1 端口: %2 - 客户端数量: %3").arg(m_currentIP).arg(m_currentPort).arg(m_clientCount));emit clientCountChanged();
}
连接管理:
自动信号槽连接(断开、数据可读)
客户端信息记录(IP:Port格式)
实时状态更新数据接收处理
void TcpServer::onClientReadyRead()
{QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());if (!client) return;// 读取所有可用数据,不依赖换行符QByteArray data = client->readAll();if (!data.isEmpty()) {QString message = QString::fromUtf8(data);QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");QString clientInfo = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());// 移除可能的多余换行符message = message.trimmed();appendReceivedData(QString("[%1] %2: %3").arg(timestamp).arg(clientInfo).arg(message));}
}
数据处理特点:
使用qobject_cast安全转换sender
按行读取数据(readLine())
添加时间戳和客户端信息
支持UTF-8编码
二、QML前端详细分析
2.1 主窗口结构 (ApplicationWindow)
整体布局
ApplicationWindow {id: windowwidth: 800height: 600// 三个主要区域Rectangle { id: controlPanel } // 顶部控制面板Rectangle { id: clientPanel } // 左侧客户端列表Rectangle { /* 消息显示区域 */ } // 右侧消息显示
}
2.2 控制面板详细分析
IP地址选择组件
ComboBox {id: ipComboBoxLayout.preferredWidth: 250editable: truemodel: server.getAvailableIPs() // 绑定C++方法onAccepted: {if (find(editText) === -1) {model = model.concat(editText) // 动态添加自定义IP}}
}
特性:
可编辑下拉框
动态模型更新
支持自定义IP输入
服务器控制按钮
Button {id: startButtontext: server.isListening ? "停止服务器" : "启动服务器"onClicked: {if (server.isListening) {server.stopServer()} else {var port = parseInt(portField.text)if (port > 0 && port <= 65535) {server.startServer(ipComboBox.currentText, port)}}}background: Rectangle {color: server.isListening ? "#e74c3c" : "#2ecc71" // 状态颜色}
}
状态管理:
文本动态变化
颜色状态指示(绿→启动,红→停止)
端口验证
2.3 客户端列表组件
列表视图和委托
ListView {id: clientListViewmodel: server.clientCount // 直接绑定客户端数量delegate: Rectangle {RowLayout {Label { text: "客户端 " + (index + 1) }Button { // 发送按钮onClicked: {clientMessageDialog.clientIndex = indexclientMessageDialog.open()}}Button { // 断开按钮onClicked: server.disconnectClient(index)}}}
}
设计特点:
简单模型(直接使用clientCount)
每个客户端项包含管理和操作按钮
索引传递用于客户端识别
2.4 消息发送对话框
Dialog {id: clientMessageDialogproperty int clientIndex: -1 // 自定义属性title: "发送消息给客户端 " + (clientIndex + 1)onAccepted: {if (clientMessageField.text.trim() !== "") {server.sendToClient(clientIndex, clientMessageField.text)clientMessageField.clear()}}
}
交互设计:
模态对话框
属性传递客户端索引
输入验证和自动清理
三、前后端交互机制
3.1 注册和暴露接口
int main(int argc, char *argv[])
{TcpServer tcpServer;QQmlApplicationEngine engine;// 关键:将C++对象暴露给QMLengine.rootContext()->setContextProperty("server", &tcpServer);engine.load(QUrl(QStringLiteral("qrc:/main.qml")));return app.exec();
}
3.2 数据绑定流程
属性绑定示例:
// QML中直接绑定C++属性
Label { text: server.statusMessage }
Label { text: "客户端数量: " + server.clientCount }
TextArea { text: server.receivedData }
方法调用示例:
Button {onClicked: server.startServer(ipComboBox.currentText, port)
}
3.3 信号传播机制
C++端信号发射 → QML属性更新 → 界面自动刷新
二、所有源码
TcpServer.h文件源码
#ifndef TCPSERVER_H
#define TCPSERVER_H#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QDateTime>class TcpServer : public QObject
{Q_OBJECTQ_PROPERTY(bool isListening READ isListening NOTIFY isListeningChanged)Q_PROPERTY(QString statusMessage READ statusMessage NOTIFY statusMessageChanged)Q_PROPERTY(QString receivedData READ receivedData NOTIFY receivedDataChanged)Q_PROPERTY(int clientCount READ clientCount NOTIFY clientCountChanged)Q_PROPERTY(QString currentIP READ currentIP NOTIFY currentIPChanged)Q_PROPERTY(quint16 currentPort READ currentPort NOTIFY currentPortChanged)public:explicit TcpServer(QObject *parent = nullptr);~TcpServer();bool isListening() const;QString statusMessage() const;QString receivedData() const;int clientCount() const;QString currentIP() const;quint16 currentPort() const;Q_INVOKABLE bool startServer(const QString &ip, quint16 port);Q_INVOKABLE void stopServer();Q_INVOKABLE void sendToAllClients(const QString &message);Q_INVOKABLE void sendToClient(int index, const QString &message);Q_INVOKABLE void disconnectClient(int index);Q_INVOKABLE QStringList getAvailableIPs();signals:void isListeningChanged();void statusMessageChanged();void receivedDataChanged();void clientCountChanged();void currentIPChanged();void currentPortChanged();private slots:void onNewConnection();void onClientDisconnected();void onClientReadyRead();private:QTcpServer *m_tcpServer;QList<QTcpSocket*> m_clients;bool m_isListening;QString m_statusMessage;QString m_receivedData;int m_clientCount;QString m_currentIP;quint16 m_currentPort;void updateStatusMessage(const QString &message);void appendReceivedData(const QString &data);
};#endif // TCPSERVER_H
TcpServer.cpp文件源码
#include "tcp_server.h"
#include <QNetworkInterface>
#include <QHostInfo>TcpServer::TcpServer(QObject *parent): QObject(parent), m_tcpServer(new QTcpServer(this)), m_isListening(false), m_clientCount(0), m_currentPort(0)
{connect(m_tcpServer, &QTcpServer::newConnection, this, &TcpServer::onNewConnection);updateStatusMessage("服务器未启动");
}TcpServer::~TcpServer()
{stopServer();
}bool TcpServer::isListening() const
{return m_isListening;
}QString TcpServer::statusMessage() const
{return m_statusMessage;
}QString TcpServer::receivedData() const
{return m_receivedData;
}int TcpServer::clientCount() const
{return m_clientCount;
}QString TcpServer::currentIP() const
{return m_currentIP;
}quint16 TcpServer::currentPort() const
{return m_currentPort;
}QStringList TcpServer::getAvailableIPs()
{QStringList ipAddresses;// 添加特殊选项ipAddresses << "0.0.0.0 (所有网络接口)";ipAddresses << "127.0.0.1 (本地回环)";// 获取所有IPv4地址QList<QHostAddress> allAddresses = QNetworkInterface::allAddresses();for (const QHostAddress &address : allAddresses) {if (address.protocol() == QAbstractSocket::IPv4Protocol &&address != QHostAddress::LocalHost) {QNetworkInterface interface = QNetworkInterface::interfaceFromIndex(address.scopeId().toInt());if (interface.isValid() &&(interface.flags() & QNetworkInterface::IsUp) &&(interface.flags() & QNetworkInterface::IsRunning) &&!(interface.flags() & QNetworkInterface::IsLoopBack)) {QString ip = address.toString();QString interfaceName = interface.humanReadableName();// 排除常见的虚拟网卡和VPNif (!interfaceName.contains("Virtual", Qt::CaseInsensitive) &&!interfaceName.contains("VPN", Qt::CaseInsensitive) &&!interfaceName.contains("VMware", Qt::CaseInsensitive) &&!interfaceName.contains("VirtualBox", Qt::CaseInsensitive)) {ipAddresses.append(QString("%1 (%2)").arg(ip).arg(interfaceName));}}}}// 如果没有找到合适的IP,尝试使用主机名解析if (ipAddresses.size() <= 2) { // 只有特殊选项QString hostName = QHostInfo::localHostName();QHostInfo hostInfo = QHostInfo::fromName(hostName);for (const QHostAddress &address : hostInfo.addresses()) {if (address.protocol() == QAbstractSocket::IPv4Protocol &&address != QHostAddress::LocalHost) {ipAddresses.append(address.toString());}}}return ipAddresses;
}bool TcpServer::startServer(const QString &ip, quint16 port)
{if (m_tcpServer->isListening()) {m_tcpServer->close();}QHostAddress hostAddress;// 解析IP地址if (ip.startsWith("0.0.0.0")) {hostAddress = QHostAddress::Any;m_currentIP = "0.0.0.0";} else if (ip.contains("127.0.0.1")) {hostAddress = QHostAddress::LocalHost;m_currentIP = "127.0.0.1";} else {// 提取纯IP地址(去掉括号内的描述)QString cleanIP = ip;int spaceIndex = ip.indexOf(' ');if (spaceIndex > 0) {cleanIP = ip.left(spaceIndex);}if (!hostAddress.setAddress(cleanIP)) {updateStatusMessage("IP地址格式错误: " + cleanIP);return false;}m_currentIP = cleanIP;}m_currentPort = port;if (!m_tcpServer->listen(hostAddress, port)) {updateStatusMessage("启动服务器失败: " + m_tcpServer->errorString());return false;}m_isListening = true;updateStatusMessage(QString("服务器已启动 - IP: %1 端口: %2").arg(m_currentIP).arg(m_currentPort));emit isListeningChanged();emit currentIPChanged();emit currentPortChanged();return true;
}void TcpServer::stopServer()
{if (m_tcpServer->isListening()) {// 断开所有客户端连接for (QTcpSocket *client : m_clients) {client->disconnectFromHost();if (client->state() != QAbstractSocket::UnconnectedState) {client->waitForDisconnected(1000);}client->deleteLater();}m_clients.clear();m_clientCount = 0;emit clientCountChanged();m_tcpServer->close();m_isListening = false;m_currentIP = "";m_currentPort = 0;updateStatusMessage("服务器已停止");emit isListeningChanged();emit currentIPChanged();emit currentPortChanged();}
}void TcpServer::sendToAllClients(const QString &message)
{if (m_clients.isEmpty()) {return;}for (QTcpSocket *client : m_clients) {if (client->state() == QAbstractSocket::ConnectedState) {QByteArray data = (message + "\n").toUtf8();client->write(data);}}appendReceivedData(QString("[发送给所有客户端]: %1").arg(message));
}void TcpServer::sendToClient(int index, const QString &message)
{if (index < 0 || index >= m_clients.size()) {return;}QTcpSocket *client = m_clients.at(index);if (client->state() == QAbstractSocket::ConnectedState) {QByteArray data = (message + "\n").toUtf8();client->write(data);appendReceivedData(QString("[发送给客户端%1]: %2").arg(index).arg(message));}
}void TcpServer::disconnectClient(int index)
{if (index < 0 || index >= m_clients.size()) {return;}QTcpSocket *client = m_clients.at(index);client->disconnectFromHost();
}void TcpServer::onNewConnection()
{QTcpSocket *clientSocket = m_tcpServer->nextPendingConnection();connect(clientSocket, &QTcpSocket::disconnected, this, &TcpServer::onClientDisconnected);connect(clientSocket, &QTcpSocket::readyRead, this, &TcpServer::onClientReadyRead);m_clients.append(clientSocket);m_clientCount = m_clients.size();QString clientInfo = QString("%1:%2").arg(clientSocket->peerAddress().toString()).arg(clientSocket->peerPort());appendReceivedData(QString("[新客户端连接]: %1").arg(clientInfo));updateStatusMessage(QString("服务器运行中 - IP: %1 端口: %2 - 客户端数量: %3").arg(m_currentIP).arg(m_currentPort).arg(m_clientCount));emit clientCountChanged();
}void TcpServer::onClientDisconnected()
{QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());if (!client) {return;}QString clientInfo = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());m_clients.removeOne(client);m_clientCount = m_clients.size();appendReceivedData(QString("[客户端断开]: %1").arg(clientInfo));updateStatusMessage(QString("服务器运行中 - IP: %1 端口: %2 - 客户端数量: %3").arg(m_currentIP).arg(m_currentPort).arg(m_clientCount));client->deleteLater();emit clientCountChanged();
}void TcpServer::onClientReadyRead()
{QTcpSocket *client = qobject_cast<QTcpSocket*>(sender());if (!client) return;// 读取所有可用数据,不依赖换行符QByteArray data = client->readAll();if (!data.isEmpty()) {QString message = QString::fromUtf8(data);QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");QString clientInfo = QString("%1:%2").arg(client->peerAddress().toString()).arg(client->peerPort());// 移除可能的多余换行符message = message.trimmed();appendReceivedData(QString("[%1] %2: %3").arg(timestamp).arg(clientInfo).arg(message));}
}void TcpServer::updateStatusMessage(const QString &message)
{if (m_statusMessage != message) {m_statusMessage = message;emit statusMessageChanged();}
}void TcpServer::appendReceivedData(const QString &data)
{m_receivedData += data + "\n";emit receivedDataChanged();
}
main.qml文件源码
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12ApplicationWindow {id: windowwidth: 800height: 600title: "TCP服务器"visible: true// 服务器控制区域Rectangle {id: controlPanelwidth: parent.widthheight: 150color: "#f0f0f0"border.color: "#cccccc"ColumnLayout {anchors.fill: parentanchors.margins: 10RowLayout {Layout.fillWidth: trueLabel {text: "IP地址:"}ComboBox {id: ipComboBoxLayout.preferredWidth: 250editable: truemodel: server.getAvailableIPs()onAccepted: {if (find(editText) === -1) {model = model.concat(editText)}}}Label {text: "端口号:"}TextField {id: portFieldLayout.preferredWidth: 100text: "8080"validator: IntValidator { bottom: 1; top: 65535 }placeholderText: "输入端口号"}Button {id: startButtontext: server.isListening ? "停止服务器" : "启动服务器"onClicked: {if (server.isListening) {server.stopServer()} else {var port = parseInt(portField.text)if (port > 0 && port <= 65535) {server.startServer(ipComboBox.currentText, port)} else {statusLabel.text = "端口号无效"}}}background: Rectangle {color: server.isListening ? "#e74c3c" : "#2ecc71"radius: 4}contentItem: Text {text: startButton.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}}Button {text: "刷新IP"onClicked: {ipComboBox.model = server.getAvailableIPs()}background: Rectangle {color: "#3498db"radius: 4}contentItem: Text {text: "刷新IP"color: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}}}RowLayout {Layout.fillWidth: trueButton {text: "清空日志"onClicked: {receivedTextArea.clear()}background: Rectangle {color: "#f39c12"radius: 4}contentItem: Text {text: "清空日志"color: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}}Item {Layout.fillWidth: true}Label {text: "当前: " + (server.currentIP ? server.currentIP + ":" + server.currentPort : "未启动")color: "#8e44ad"font.bold: true}}RowLayout {Layout.fillWidth: trueLabel {id: statusLabeltext: server.statusMessageLayout.fillWidth: truecolor: server.isListening ? "#27ae60" : "#e74c3c"font.bold: truewrapMode: Text.Wrap}Label {text: "客户端数量: " + server.clientCountcolor: "#2980b9"font.bold: true}}RowLayout {Layout.fillWidth: trueTextField {id: messageFieldLayout.fillWidth: trueplaceholderText: "输入要发送的消息..."onAccepted: sendButton.clicked()}Button {id: sendButtontext: "发送给所有客户端"enabled: server.isListening && server.clientCount > 0onClicked: {if (messageField.text.trim() !== "") {server.sendToAllClients(messageField.text)messageField.clear()}}background: Rectangle {color: sendButton.enabled ? "#9b59b6" : "#bdc3c7"radius: 4}contentItem: Text {text: sendButton.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}}}}}// 客户端列表区域Rectangle {id: clientPanelwidth: 200anchors {top: controlPanel.bottombottom: parent.bottomleft: parent.left}color: "#f8f9fa"border.color: "#dee2e6"ColumnLayout {anchors.fill: parentanchors.margins: 10Label {text: "客户端列表"font.bold: truefont.pixelSize: 16Layout.alignment: Qt.AlignHCenter}ListView {id: clientListViewLayout.fillWidth: trueLayout.fillHeight: truemodel: server.clientCountclip: truedelegate: Rectangle {width: clientListView.widthheight: 40color: index % 2 === 0 ? "#ffffff" : "#f8f9fa"border.color: "#dee2e6"RowLayout {anchors.fill: parentanchors.margins: 5Label {text: "客户端 " + (index + 1)Layout.fillWidth: trueelide: Text.ElideRight}Button {text: "发送"enabled: server.isListeningonClicked: {clientMessageDialog.clientIndex = indexclientMessageDialog.open()}background: Rectangle {color: "#3498db"radius: 3}contentItem: Text {text: "发送"color: "white"font.pixelSize: 10horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}}Button {text: "断开"enabled: server.isListeningonClicked: server.disconnectClient(index)background: Rectangle {color: "#e74c3c"radius: 3}contentItem: Text {text: "断开"color: "white"font.pixelSize: 10horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenter}}}}ScrollBar.vertical: ScrollBar {}}}}// 消息显示区域Rectangle {anchors {top: controlPanel.bottombottom: parent.bottomleft: clientPanel.rightright: parent.right}color: "white"border.color: "#dee2e6"ScrollView {anchors.fill: parentanchors.margins: 5TextArea {id: receivedTextAreatext: server.receivedDatareadOnly: truewrapMode: TextArea.WrapselectByMouse: truefont.family: "Courier New"font.pixelSize: 12background: null}}}// 发送给特定客户端的对话框Dialog {id: clientMessageDialogproperty int clientIndex: -1title: "发送消息给客户端 " + (clientIndex + 1)modal: truestandardButtons: Dialog.Ok | Dialog.Cancelx: (window.width - width) / 2y: (window.height - height) / 2ColumnLayout {width: parent ? parent.width : 100Label {text: "输入要发送的消息:"}TextField {id: clientMessageFieldLayout.fillWidth: trueplaceholderText: "输入消息..."onAccepted: clientMessageDialog.accept()}}onAccepted: {if (clientMessageField.text.trim() !== "") {server.sendToClient(clientIndex, clientMessageField.text)clientMessageField.clear()}}onRejected: {clientMessageField.clear()}}
}
main.cpp文件源码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "tcp_server.h"int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);// 注册TCP服务器到QMLTcpServer tcpServer;QQmlApplicationEngine engine;// 将TCP服务器实例暴露给QMLengine.rootContext()->setContextProperty("server", &tcpServer);engine.load(QUrl(QStringLiteral("qrc:/main.qml")));if (engine.rootObjects().isEmpty())return -1;return app.exec();
}
三、效果演示
服务器端设置ip和port,测试发送和接受。