当前位置: 首页 > news >正文

开源 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,测试发送和接受。

http://www.dtcms.com/a/456608.html

相关文章:

  • 计算机毕业设计java共享茶室预约微信小程序 微信小程序中的共享茶室预订平台 茶室共享预约小程序的设计与开发
  • 《投资-104》价值投资者的认知升级与交易规则重构 - 如何从投资的角度选择创业的方向?
  • 网站底部素材临沂市平邑县建设局网站
  • 光通信|OAM-波长可控交叉连接
  • Redshift中锁定表的查询与处理
  • 利用AI+大数据的方式分析恶意样本(四十六)
  • 解码Linux环境搭建
  • 第8章:定时任务与触发器——让 Bot 主动服务
  • Javaweb--Vue
  • 大数据模糊计算
  • 2021年408真题易错知识点整理
  • 【金仓数据库】ksql 指南(二) —— 创建与管理本地数据库
  • 凡科网站是骗子跨境网站开发公司
  • vite性能优化
  • git添加远程仓库报错To add an exception for this directory解决方案-优雅草卓伊凡
  • 手机AIDE使用OpenCV
  • AI智能体(Agent)大模型入门【9】--如何在pycharm等其他编译软件调用ocr工具【只写后端代码不演示】
  • 浅析SpringBoot框架常见未授权访问漏洞
  • 有什么可以做翻译的网站点的排版设计网站
  • 第五十三章 ESP32S3 TCPClient 实验
  • 中国突破柔性电池技术瓶颈:可弯折20000次引领能源存储革命
  • 网站制作公司的宣传海报品牌免费网站建设
  • 基于模板缓冲的矢量贴地显示
  • flink keyby使用与总结 基础片段梳理
  • flink UTDF函数
  • 乐陵网站开发贾汪区建设局网站
  • VS安装EEPlus库及解决[弃用的]CS0618问题
  • 《算法闯关指南:优选算法--滑动窗口》--15.串联所有单词的子串,16.最小覆盖子串
  • 行驶证识别技术通过OCR和AI实现信息自动化采集与处理,涵盖图像预处理、文字识别及结构化校验,提升效率与准确性
  • 第十七篇:数组与链表:结构特性、操作与经典题目