2. QML---TCP消息发送与接收

1. 说明

本文章是接着前篇文章写的,文章中的简单案例是实现了服务端和客户端之间的通信,发送的消息比较单一,可以在此基础上进行扩展,做成聊天的小工具。
效果展示:

TCP简单通信

2. 实现步骤:

第一步:
在服务端加入数据读取函数,当服务端连接到新的客户端后,会触发newConnection()信号,此时应在其槽函数中进行响应,获取客户端的tcpSocket,当这个tcpSocket发送了消息时,会触发***readyRead()信号,此时应再设置槽函数,在这个槽函数中使用tcpSocket->readAll()***函数接收发送过来的数据,具体代码如下:
tcpServer.h:

#ifndef TCPSEVER_H
#define TCPSEVER_H

#include <QObject>
#include <QDebug>
#include <QTcpServer>
#include <QTcpSocket>
#include <QByteArray>

class TcpSever : public QObject
{
    Q_OBJECT
public:
    explicit TcpSever(QObject *parent = nullptr);

    Q_INVOKABLE void startListen();//供QML端调用启动监听
    Q_INVOKABLE void sendMsg();//供QML端调用发送数据

signals:
    void sendConnectSuc(QString mStr);//连接成功时发送该信号,QML端会进行响应
    void sendRecivedMsg(QString mData);//接收到数据时发送该信号,QML端会进行响应

public slots:
    void newConnect();
    void readFromClient();

private:
    QTcpServer *tcpSever;
    QTcpSocket *tcpSocket;
    QByteArray m_data;
};

#endif // TCPSEVER_H

tcpServer.cpp:

#include "tcpsever.h"

TcpSever::TcpSever(QObject *parent) : QObject(parent)
{

}

void TcpSever::startListen()
{
    tcpSever = new QTcpServer(this);
    tcpSever->listen(QHostAddress::Any,6666);
    //有新的连接后,执行newConnect槽函数
    connect(tcpSever,SIGNAL(newConnection()),this,SLOT(newConnect()));

    emit sendConnectSuc("listening....");
}

void TcpSever::sendMsg()
{
	//QML端的按钮调用该函数,发送单一数据
    tcpSocket->write("123");
}

void TcpSever::newConnect()
{
	//获取连接成功的tcpSocket 
    tcpSocket = tcpSever->nextPendingConnection();

    QString ip = tcpSocket->peerAddress().toString();
    quint16 port = tcpSocket->peerPort();
    if(port){
        QString temp = QString::fromLocal8Bit("sucess connected...");
        emit sendConnectSuc(temp);	//发送连接成功信号
    }
    //当检测到有新数据传输过来时,执行readFromClient槽函数
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readFromClient()));

}

void TcpSever::readFromClient()
{
	//读取客户端发送过来的数据
    QString array = QString(tcpSocket->readAll());

    emit sendRecivedMsg(array);	//读取到数据后,发送该信号,将数据发送的QML端进行使用
}

第二步:
在客户端加入数据读取函数,当客户端与服务端连接成功之后,即可使用***tcpSocket.write()函数向服务端发送数据。同样,服务端也能向客户端发送数据,当客户端监测到有新数据传输过来时,会触发readyRead()信号,此时应再设置槽函数,在这个槽函数中使用tcpSocket->readAll()***函数接收发送过来的数据,具体代码如下:
tcpSocket.h:

#ifndef TCPSOCKET_H
#define TCPSOCKET_H

#include <QObject>
#include <QTcpSocket>
#include <QHostAddress>

class TcpSocket : public QObject
{
    Q_OBJECT
public:
    explicit TcpSocket(QObject *parent = nullptr);

    Q_INVOKABLE void askConnect();//请求连接服务端,供QML调用
    Q_INVOKABLE void sendMsg();//向服务端发送数据,供QML调用

signals:
    void sendAskCommand(QString mStr);//请求发送后,发射此信号
    void sendReceivedMsg(QString arr);//接收到服务端传输过来的数据后,发送此信号,将接收的数据发送到QML中进行使用

public slots:
    void receiveMsg();//客户端检测到有数据从服务端传输过来时,触发readyRead()信号,并执行receiveMsg()槽函数

private:
    QTcpSocket *tcpSocket;

};
#endif // TCPSOCKET_H

tcpSocket.cpp:

#include "Tcpsocket.h"

TcpSocket::TcpSocket(QObject *parent) : QObject(parent)
{
    tcpSocket = new QTcpSocket(this);
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(receiveMsg()));//当有数据发送过来时,执行槽函数receiveMsg()
}

void TcpSocket::askConnect()
{
    tcpSocket->connectToHost(QHostAddress("127.0.0.1"),6666);//请求连接服务端

    emit sendAskCommand("asking.....");
}

void TcpSocket::sendMsg()
{
    tcpSocket->write("456");//向服务端发送数据
}

void TcpSocket::receiveMsg()
{
    QString data = QString(tcpSocket->readAll());//读取服务端发送过来的数据

    emit sendReceivedMsg(data);//将数据发送到QML端进行使用
}

第三步:
main.cpp文件中,将上面的自定义类注册到QML的上下文背景中进行使用,代码如下:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include "tcpsever.h"
#include "Tcpsocket.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);
	//注册自定义类到QML中进行使用
    TcpSever tcp;
    engine.rootContext()->setContextProperty("tcp",&tcp);
    TcpSocket tcpsocket;
    engine.rootContext()->setContextProperty("tcpSocket",&tcpsocket);

    return app.exec();
}

第四步:
main.qml文件中调用上面的函数,界面设计相对简单,包含一些按钮和两个ListView,用于显示发送的数据,类似于微信聊天对话的形式,代码如下:

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.1

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Connections{
        target: tcp
        property var i: 1
        onSendConnectSuc:{
            severModel.append({str:mStr})
        }
        onSendRecivedMsg:{
            severModel.append({str:mData})
        }
    }
    Connections{
        target: tcpSocket
        property var j: 1
        onSendAskCommand:{
            socketModel.append({index:j++,str:mStr})
        }
        onSendReceivedMsg:{
            socketModel.append({index:j++,str:arr})
        }
    }


    Button{
        id:listenBtn
        width: 100
        height: 50
        anchors.left: parent.left
        anchors.top: parent.top
        anchors.topMargin: 10
        anchors.leftMargin: 10
        text:"StartListen"
        onClicked: {
            tcp.startListen();
        }
    }
    Button{
        id:sendBtn_server
        width: 100
        height: 50
        anchors.left: parent.left
        anchors.top: listenBtn.bottom
        anchors.topMargin: 10
        anchors.leftMargin: 10
        text:"SendMsg"
        onClicked: {
            tcp.sendMsg()
        }
    }
    Button{
        id:askConnect
        width: 100
        height: 50
        anchors.right: parent.right
        anchors.rightMargin:10
        anchors.top: parent.top
        anchors.topMargin: 10
        text: "AskConnect"
        onClicked: {
            tcpSocket.askConnect()
        }
    }
    Button{
        id:sendBtn_Socket
        width: 100
        height: 50
        anchors.right: parent.right
        anchors.rightMargin:10
        anchors.top: askConnect.bottom
        anchors.topMargin: 10
        text: "SendMsg"
        onClicked: {
            tcpSocket.sendMsg()
        }
    }
    ListView{
        id:severView
        anchors.top: listenBtn.top
        anchors.left: listenBtn.right
        anchors.leftMargin: 5
        anchors.right: parent.horizontalCenter
        height: parent.height
        clip:true
        spacing:20
        model:ListModel{
            id:severModel
        }
        delegate: Rectangle{
            width: parent.width
            height: 10
            Image{
                id:severImg
                width: parent.height*1.5
                height: parent.height*2
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                source: "qrc:/images/boy.jpeg"
            }

            Text{
                id:severcontent
                height: parent.height
                anchors.left: severImg.right
                anchors.leftMargin: 5
                anchors.verticalCenter: severImg.verticalCenter
                text: str
            }
        }
    }
    ListView{
        id:socketView
        anchors.top: askConnect.top
        anchors.right: askConnect.left
        anchors.rightMargin: 5
        width: 200
        height: parent.height
        clip:true
        spacing:20
        model:ListModel{
            id:socketModel
        }
        delegate: Rectangle{
            width: parent.width
            height: 10
            Image{
                id:socketImg
                width: parent.height*1.5
                height: parent.height*2
                anchors.verticalCenter: parent.verticalCenter
                anchors.right: parent.right
                source: "qrc:/images/girl.jpeg"
            }

            Text{
                id:socketcontent
                height: parent.height
                anchors.right: socketImg.left
                anchors.rightMargin: 5
                anchors.verticalCenter: socketImg.verticalCenter
                text: str
                color: "red"
            }
        }
    }
}

持续更新中,请大家多多关注…

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山间点烟雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值