qt远程通信

qt和其他远程进程通信可以使用直接的tcp udp,也可以使用封装的RPC,比如thrift,buttonpc,不过这些东西在程序需要特别简单处理的时候就显得没有必要。我们可以使用简单的udp通信来代替RPC。我们要设计一种协议方式,避免udp通信过于脆弱。

qt udp 通信

看如下界面

grpc不是tcp吗_tcp/ip


我们启动一个sokit 来启动udp服务

grpc不是tcp吗_grpc不是tcp吗_02


输入信息,点击发送,server端就收到了信息

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    v_sender=new QUdpSocket(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_clicked()
{
    QByteArray dataGram=ui->lineEdit->text().toUtf8();

    QHostAddress address(ui->lineEdit_2->text());
    v_sender->writeDatagram(dataGram.data(),
                               dataGram.size(),
                            address,
                               6666);

}

代码特别简单,不过udp有个特点,就是容易丢包,我们设计一种机制,来避免udp 太过脆弱。我们规定udp 必须收到包必须返回,并且把udp收发放到线程中。

上面发包,那么能否既能发包又能收包?是可以的,这个分为两种情况,一种是局域网,一种是在云端,作为云端服务端记住IP和端口,发送心跳,局域网里面没有问题,在云端不一定能维持链接,断开时可以重新启动udp,再发送心跳。

connect(v_sender, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
void MainWindow::readPendingDatagrams()
{
    while(v_sender->hasPendingDatagrams()){

        QHostAddress srcAddress;
        quint16 nSrcPort;
        QByteArray datagram;
        datagram.resize(v_sender->pendingDatagramSize());
        v_sender->readDatagram(datagram.data(), datagram.size(), &srcAddress, &nSrcPort);
        qDebug()<<datagram.toStdString().data();
    }
}
void MainWindow::closeEvent(QCloseEvent *event)
{
    Q_UNUSED(event)
    v_sender->abort();
    QEventLoop loop;
    QTimer::singleShot(1000, &loop, SLOT(quit()));
    loop.exec();
    this->close();
}

grpc不是tcp吗_udp_03


测试工具中sokit 中的当前链接选中,发送数据,

udp

grpc不是tcp吗_grpc不是tcp吗_04


udp这里收到了数据打印。当然外网和云端方式不这时候最好使用还是tcp来做。

使用tcp

tcp 链接和udp链接类似,没有多大区别,不过,传输层tcp协议拥有传输控制方法,拥塞控制和丢包重传机制,udp是没有的,以下是简单的启动方法

socket = new QTcpSocket();
  
    //连接信号槽
 QObject::connect(socket, &QTcpSocket::readyRead, this, &MainWindow::socket_Read_Data);
 QObject::connect(socket, &QTcpSocket::disconnected, this, &MainWindow::socket_Disconnected);

使用tcp 可以使用上次文章中的movetoThread 方法,把tcp的行为操作放到一个线程中,也可以继承一个QThread, 覆盖run方法,由于异步执行,使用connect将server 的newConnection 直接写入lamba函数之中,然后在lamba线程中启动一个tcp链接线程,应对

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
void close()
{
            myqtread->quit();
            myqtread->wait();
            myqtread->deleteLater();
 } 
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
  
    // 设置端口号
    ui->port->setText("6666");
  
    this->tcp_server = new QTcpServer(this);
    // 检验是否接收客户端的连接
    connect(this->tcp_server, &QTcpServer::newConnection, this, [=]()
    {
        QTcpSocket * tcp = this->m_server->nextPendingConnection();
        MyQThread * myqtread = new MyQThread(tcp);
        myqtread->start();
        // 获取子线程中发来的客户端端口的消息
        connect(myqtread, &MyQThread::ClientDisconnect, this, [=]()
        {
            QMessageBox::warning(this, "警告", "客户端已断开连接...");
        });
        connect(myqtread, &MyQThread::OverRecveid, this, [=]()
        {
            QMessageBox::information(this, "提示", "已接收文客户端发来的数据");
            tcp->close();
            tcp->deleteLater();
           close();

        });
    });
}
  
Widget::~Widget()
{
    delete ui;
}
// 点击监听按钮 进行监听 按钮转到槽的方式
void Widget::on_listenBtn_clicked()
{
    //获取端口号
    unsigned short port = ui->port->text().toUShort();
    //利用this->m_s 调用listen 进行监听
    this->m_server->listen(QHostAddress::Any, port);
}

序列化等其他RPC工作

其他工作我们可以使用二进制来代替json方式的发送,不过自己解析需要花时间,确实没有RPC工具来得方便,不过很多工作,我们可以使用调用脚本 lua 的方式来做。这个等到下一次来讲了。