一、使用TcpServer和TcpSocket实现通讯,C++实现逻辑,QML实现UI界面。

二、服务器端实现
1、qml代码
css
import QtQuick 2.15
import QtQuick.Controls 2.15
import com.example 1.0
Rectangle {
id:fuwuqi
width: 400
height: 600
property string str: ""
property string editcontext: ""
Text{
id:xianshi
anchors.top: parent.top
x:5
y:0
width: 390
height: 400
Rectangle{
color: 'pink'
opacity: 0.25
anchors.fill: parent
}
}
TextEdit{
id:shuru
height: 180
width: 390
y:415
x:5
text:""
Rectangle{
color: 'pink'
anchors.fill: parent
opacity:0.5
}
onTextChanged: {
console.log('改变了'+shuru.text)
}
}
Button{
id:fasong
text:'发送'
height: 15
width: 60
x:340
y:400
onClicked: {
console.log("数据已发送")
xianshi.text=xianshi.text+"服务器:\n "+shuru.text+"\n"
myLogciControl.sendDataToClient("172.22.122.113", shuru.text);
shuru.text=''
}
}
Button{
id:qingpin
text:'清屏'
height: 15
width: 60
x:0
y:400
onClicked: {
console.log("屏幕已清空")
xianshi.text=''
}
}
//注册TCP服务器
MyTcpServer{
id:myLogciControl
onMsgChanged: {
xianshi.text=xianshi.text+msg+"\n";
}
onReceivedStringChanged:{
xianshi.text=xianshi.text+"客户端:\n "+receivedString+"\n";
}
}
Component.onCompleted: {
myLogciControl.startServer("172.22.122.113",1234);
}
}
2、.mytcpserver.h代码
cpp
#ifndef MYTCPCLIENT_H
#define MYTCPCLIENT_H
#include <QTcpSocket>
#include <QTcpServer>
#include <QHostAddress>
// 1. 添加QML注册宏(必须在类定义前)
// 命名空间:com.example,版本:1.0,QML中类型名:MyTcpServer
#define QML_REGISTER_TCPSERVER qmlRegisterType<MyTcpServer>("com.example", 1, 0, "MyTcpServer")
class MyTcpServer : public QTcpServer
{
Q_OBJECT
Q_PROPERTY(bool isListening READ isListening NOTIFY isListeningChanged)
Q_PROPERTY(QString msg READ msg WRITE setMsg NOTIFY msgChanged)
Q_PROPERTY(QString receivedString READ receivedString WRITE setReceivedString NOTIFY receivedStringChanged)
public:
explicit MyTcpServer(QTcpServer *parent = nullptr);
~MyTcpServer() override;
Q_INVOKABLE bool startServer(const QString &address = "0.0.0.0", quint16 port = 8888); // 启动服务器
Q_INVOKABLE void stopServer(); // 停止服务器
Q_INVOKABLE bool sendDataToClient(const QString &clientAddr, QString data); // 给指定客户端发数据
// Q_INVOKABLE void broadcastData(const QByteArray &data); // 广播数据给所有客户端
// 获取服务器状态(匹配Q_PROPERTY)
bool isListening();
QString msg();
void setMsg(QString msg);
QString receivedString();
void setReceivedString(QString str);
signals:
// 通知QML的核心信号
void isListeningChanged(); // 监听状态变化
void serverStarted(); // 服务器启动成功
void serverStopped(); // 服务器停止
void newClientConnected(const QString &clientAddr, quint16 clientPort); // 新客户端连接
void clientDisconnected(const QString &clientAddr, quint16 clientPort); // 客户端断开
void errorOccurred(const QString &errorMsg); // 错误通知
void msgChanged();
void receivedStringChanged();
private slots:
void onNewConnection(); // 处理新连接
void onClientDisconnected(); // 处理客户端断开
void onClientReadyRead(); // 处理客户端数据接收
//void onServerError(QAbstractSocket::SocketError socketError); // 服务器错误处理
private:
QTcpServer *m_tcpServer = nullptr; // TCP服务器核心对象
QList<QTcpSocket*> m_clientSockets; // 管理所有客户端Socket
// // 辅助方法:通过地址+端口查找客户端Socket
// QTcpSocket* findClientSocket(const QString &clientAddr, quint16 clientPort) const;
// // 重载:仅通过地址(简化QML调用)
QTcpSocket* findClientSocket(const QString &clientAddr);
QString m_msg;
QString m_receivedString;
};
#endif // MYTCPCLIENT_H
3、mytcpserver.cpp代码
cpp
#include "mytcpserver.h"
MyTcpServer::MyTcpServer(QTcpServer *parent)
: QTcpServer{parent}
{
m_tcpServer = new QTcpServer(this);
// 绑定服务器核心信号
connect(m_tcpServer, &QTcpServer::newConnection, this, &MyTcpServer::onNewConnection);
// connect(m_tcpServer, &QTcpServer::acceptError, this, &MyTcpServer::onServerError);
qDebug()<<"服务器初始化开始";
}
MyTcpServer::~MyTcpServer(){
m_clientSockets.clear();
}
bool MyTcpServer::startServer(const QString &address, quint16 port)
{
// 若已监听,先停止
if (m_tcpServer->isListening()) {
stopServer();
}
// 解析监听地址(0.0.0.0表示监听所有网卡)
QHostAddress listenAddr = QHostAddress::Any;
if (!address.isEmpty() && address != "0.0.0.0") {
listenAddr = QHostAddress(address);
if (listenAddr.isNull()) { // 地址解析失败
emit errorOccurred(QString("无效的监听地址:%1").arg(address));
return false;
}
}
// 开始监听
bool success = m_tcpServer->listen(listenAddr, port);
if (success) {
emit serverStarted();
emit isListeningChanged();
qDebug() << "服务器启动成功,监听地址:" << listenAddr.toString() << " 端口:" << port;
} else {
emit errorOccurred(QString("服务器启动失败:%1").arg(m_tcpServer->errorString()));
}
return success;
}
void MyTcpServer::stopServer(){
if(!m_tcpServer->isListening()){
return;
}
m_tcpServer->close();
// 断开所有客户端(触发disconnected信号)
for (QTcpSocket *socket : m_clientSockets){
if(socket->state()!=QTcpSocket::UnconnectedState){
socket->disconnectFromHost();
socket->waitForDisconnected(1000);
}
}
emit serverStopped();
emit isListeningChanged();
qDebug() << "服务器已停止";
}
void MyTcpServer::onNewConnection(){
// 取出所有待处理的新连接(防止漏接)
while (m_tcpServer->hasPendingConnections()){
QTcpSocket *clientSocket = m_tcpServer->nextPendingConnection();
if(!clientSocket){
continue;
qDebug()<<"满了???";
}
connect(clientSocket, &QTcpSocket::disconnected, this, &MyTcpServer::onClientDisconnected);
connect(clientSocket, &QTcpSocket::readyRead, this, &MyTcpServer::onClientReadyRead);
// 添加到客户端列表
m_clientSockets.append(clientSocket);
// 通知QML新客户端连接
QString clientAddr = clientSocket->peerAddress().toString();
quint16 clientPort = clientSocket->peerPort();
emit newClientConnected(clientAddr, clientPort);
qDebug() << "新客户端连接:" << clientAddr << ":" << clientPort;
QString str="客户端新连接:" + clientAddr + " : " + QString::number(clientPort);
setMsg(str);
emit msgChanged();
}
}
void MyTcpServer::onClientDisconnected(){
// 获取断开的客户端Socket
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (!clientSocket) {
return;
}
// 记录客户端信息(断开前获取)
QString clientAddr = clientSocket->peerAddress().toString();
quint16 clientPort = clientSocket->peerPort();
// 从列表移除并释放
m_clientSockets.removeOne(clientSocket);
clientSocket->deleteLater(); // 安全释放
// 通知QML客户端断开
emit clientDisconnected(clientAddr, clientPort);
QString str="客户端断开连接:" + clientAddr + ":" + clientPort;
setMsg(str);
qDebug() << str;
emit msgChanged();
}
void MyTcpServer::onClientReadyRead(){
// 获取发送数据的客户端Socket
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (!clientSocket) {
return;
}
// 读取所有可用数据
QByteArray data = clientSocket->readAll();
QString clientAddr = clientSocket->peerAddress().toString();
// 通知QML收到数据
m_receivedString=data;
emit receivedStringChanged();
qDebug() << "收到客户端" << clientAddr << "数据:" << data;
}
QTcpSocket* MyTcpServer::findClientSocket(const QString &clientAddr){
// 简化版:仅匹配地址(若同一客户端多端口连接,取第一个)
for (QTcpSocket *socket : m_clientSockets) {
if (socket->peerAddress().toString() == clientAddr) {
return socket;
}
}
return nullptr;
}
bool MyTcpServer::sendDataToClient(const QString &clientAddr, QString str){
QByteArray data= str.toUtf8();
QTcpSocket* clientSocket = findClientSocket(clientAddr);
if(!clientSocket||clientSocket->state() != QTcpSocket::ConnectedState){
emit errorOccurred(QString("客户端未连接:%1").arg(clientAddr));
return false;
}
qDebug()<<"发送事件";
qint64 bytesWritten=clientSocket->write(data);
if (bytesWritten == -1) {
emit errorOccurred(QString("发送数据失败:%1").arg(clientSocket->errorString()));
return false;
}
// 强制刷新缓冲区(确保数据立即发送)
clientSocket->flush();
qDebug() << "向客户端" << clientAddr << "发送数据:" << data;
return true;
}
bool MyTcpServer::isListening(){
return m_tcpServer->isListening();
}
QString MyTcpServer::msg(){
return m_msg;
}
void MyTcpServer::setMsg(QString msg){
m_msg=msg;
}
QString MyTcpServer::receivedString(){
return m_receivedString;
}
void MyTcpServer::setReceivedString(QString str){
m_receivedString=str;
}
三、客户端代码
1、qml代码
css
import QtQuick 2.15
import QtQuick.Controls 2.15
import com.example 1.0
Rectangle {
id:fuwuqi
width: 400
height: 600
property string str: ""
property string editcontext: ""
Text{
id:xianshi
anchors.top: parent.top
x:5
y:0
width: 390
height: 400
Rectangle{
color: 'pink'
opacity: 0.25
anchors.fill: parent
}
}
TextEdit{
id:shuru
height: 180
width: 390
y:415
x:5
text:""
Rectangle{
color: 'pink'
anchors.fill: parent
opacity:0.5
}
onTextChanged: {
console.log('改变了'+shuru.text)
}
}
Button{
id:fasong
text:'发送'
height: 15
width: 60
x:340
y:400
onClicked: {
console.log("数据已发送")
xianshi.text=xianshi.text+"客户端:\n "+shuru.text+"\n"
myLogciControl.sendData(shuru.text)
shuru.text=''
}
}
Button{
id:qingpin
text:'清屏'
height: 15
width: 60
x:0
y:400
onClicked: {
console.log("屏幕已清空")
xianshi.text=''
}
}
//注册TCP服务器
TcpClientControl{
id:myLogciControl
onMsgChanged: {
xianshi.text=xianshi.text+ msg +"\n"
}
onReceivedStringChanged: {
xianshi.text=xianshi.text+ "服务器\n "+receivedString +"\n"
}
}
Component.onCompleted: {
myLogciControl.connectToServer("172.22.122.113",1234)
}
}
2、tcpclientcontrol.h代码
cpp
#ifndef TCPCLIENTCONTROL_H
#define TCPCLIENTCONTROL_H
#include <QObject>
#include <QTcpSocket>
#define QML_REGISTER_TCPSCLIENTCONTROL qmlRegisterType<TcpClientControl>("com.example", 1, 0, "TcpClientControl")
class TcpClientControl : public QObject
{
Q_OBJECT
// 暴露连接状态给QML(是否已连接服务器)
Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedChanged);
// 暴露当前连接的服务器地址+端口(可选)
Q_PROPERTY(QString serverAddress READ serverAddress NOTIFY serverAddressChanged);
Q_PROPERTY(quint16 serverPort READ serverPort NOTIFY serverPortChanged);
Q_PROPERTY(QString msg READ msg WRITE setMsg NOTIFY msgChanged)
Q_PROPERTY(QString receivedString READ receivedString WRITE setReceivedString NOTIFY receivedStringChanged)
public:
explicit TcpClientControl(QObject *parent = nullptr);
// 供QML调用的核心方法
Q_INVOKABLE bool connectToServer(const QString &address, quint16 port); // 连接服务器
Q_INVOKABLE void disconnectFromServer(); // 断开连接
Q_INVOKABLE bool sendString(const QString &dataStr); // 发送字符串(适配QML)
Q_INVOKABLE bool sendData(const QByteArray &data); // 发送二进制数据(底层)
Q_INVOKABLE bool reconnect(); // 重连服务器(可选)
// 属性读取方法
bool isConnected() const;
QString serverAddress() const;
quint16 serverPort() const;
void setMsg(QString msg);
QString msg();
QString receivedString();
void setReceivedString(QString str);
signals:
// 通知QML的核心信号
void isConnectedChanged(); // 连接状态变化
void serverAddressChanged();
void serverPortChanged();
void connectedToServer(); // 连接成功
void disconnectedFromServer(); // 断开连接
void dataReceived(const QByteArray &data); // 收到二进制数据
void errorOccurred(const QString &errorMsg); // 错误通知
void msgChanged();
void receivedStringChanged();
private slots:
// 内部信号槽(处理Socket事件)
void onSocketConnected(); // 连接成功
void onSocketDisconnected(); // 断开连接
void onSocketReadyRead(); // 收到数据
void onSocketError(QAbstractSocket::SocketError socketError); // 错误处理
private:
QTcpSocket *m_tcpSocket = nullptr; // 核心Socket对象
QString m_serverAddress; // 记录当前连接的服务器地址
quint16 m_serverPort = 0; // 记录当前连接的服务器端口
QString m_msg;
QString m_receivedString;
};
#endif // TCPCLIENTCONTROL_H
3、tcpclientcontrol.cpp代码
cpp
#include "tcpclientcontrol.h"
#include <QDebug>
TcpClientControl::TcpClientControl(QObject *parent)
: QObject(parent)
{
// 初始化Socket
m_tcpSocket = new QTcpSocket(this);
// 绑定Socket核心信号
connect(m_tcpSocket, &QTcpSocket::connected, this, &TcpClientControl::onSocketConnected);
connect(m_tcpSocket, &QTcpSocket::disconnected, this, &TcpClientControl::onSocketDisconnected);
connect(m_tcpSocket, &QTcpSocket::readyRead, this, &TcpClientControl::onSocketReadyRead);
connect(m_tcpSocket, &QTcpSocket::errorOccurred, this, &TcpClientControl::onSocketError);
}
bool TcpClientControl::connectToServer(const QString &address, quint16 port)
{
// 若已连接,先断开
if (m_tcpSocket->state() == QTcpSocket::ConnectedState) {
disconnectFromServer();
}
// 记录服务器地址和端口
m_serverAddress = address;
m_serverPort = port;
emit serverAddressChanged();
emit serverPortChanged();
// 连接服务器(异步,结果通过connected/disconnected/error信号通知)
m_tcpSocket->connectToHost(QHostAddress(address), port);
// 无需等待连接(避免阻塞QML主线程),状态通过信号通知
return true;
}
void TcpClientControl::disconnectFromServer()
{
if (m_tcpSocket->state() != QTcpSocket::UnconnectedState) {
m_tcpSocket->disconnectFromHost();
// 若处于连接中状态,强制关闭
if (m_tcpSocket->state() == QTcpSocket::ConnectingState) {
m_tcpSocket->abort();
}
}
}
bool TcpClientControl::sendString(const QString &dataStr)
{
// QML字符串转QByteArray(UTF-8编码,避免中文乱码)
QByteArray data = dataStr.toUtf8();
return sendData(data);
}
bool TcpClientControl::sendData(const QByteArray &data)
{
// 检查连接状态
if (m_tcpSocket->state() != QTcpSocket::ConnectedState) {
emit errorOccurred("未连接到服务器,发送失败");
return false;
}
// 发送数据(异步,返回是否写入缓冲区成功)
qint64 bytesWritten = m_tcpSocket->write(data);
if (bytesWritten == -1) {
emit errorOccurred(QString("发送数据失败:%1").arg(m_tcpSocket->errorString()));
return false;
}
// 强制刷新缓冲区(确保数据立即发送)
m_tcpSocket->flush();
qDebug() << "客户端发送数据:" << data;
return true;
}
bool TcpClientControl::reconnect()
{
// 重连上次连接的服务器
if (m_serverAddress.isEmpty() || m_serverPort == 0) {
emit errorOccurred("未记录服务器地址和端口,重连失败");
return false;
}
return connectToServer(m_serverAddress, m_serverPort);
}
bool TcpClientControl::isConnected() const
{
return m_tcpSocket->state() == QTcpSocket::ConnectedState;
}
QString TcpClientControl::serverAddress() const
{
return m_serverAddress;
}
quint16 TcpClientControl::serverPort() const
{
return m_serverPort;
}
void TcpClientControl::onSocketConnected()
{
emit isConnectedChanged();
emit connectedToServer();
QString str= "客户端与服务器的连接:" + m_serverAddress + " : " + QString::number(m_serverPort);
setMsg(str);
emit msgChanged();
qDebug() << str;
}
void TcpClientControl::onSocketDisconnected()
{
emit isConnectedChanged();
emit disconnectedFromServer();
QString str= "客户端断开与服务器的连接:" + m_serverAddress + ":" + m_serverPort;
setMsg(str);
emit msgChanged();
}
void TcpClientControl::onSocketReadyRead()
{
// 读取所有可用数据
QByteArray data = m_tcpSocket->readAll();
if (data.isEmpty()) {
return;
}
// 发送二进制数据信号(供需要原始字节的场景)
emit dataReceived(data);
// 转换为字符串信号(适配QML显示)
QString dataStr = QString::fromUtf8(data);
m_receivedString=dataStr;
emit receivedStringChanged();
qDebug() << "客户端收到服务器数据:" << data << "(字符串:" << dataStr << ")";
}
void TcpClientControl::onSocketError(QAbstractSocket::SocketError socketError)
{
Q_UNUSED(socketError);
QString errorMsg = QString("客户端错误:%1").arg(m_tcpSocket->errorString());
emit errorOccurred(errorMsg);
emit isConnectedChanged(); // 错误会导致连接状态变化
qDebug() << errorMsg;
}
QString TcpClientControl::msg(){
return m_msg;
}
void TcpClientControl::setMsg(QString msg){
m_msg=msg;
}
QString TcpClientControl::receivedString(){
return m_receivedString;
}
void TcpClientControl::setReceivedString(QString str){
m_receivedString=str;
}