【问题标题】:How to use standardised request response transactions using QTcp?如何使用 QTcp 使用标准化的请求响应事务?
【发布时间】:2019-11-12 08:42:33
【问题描述】:

我正在为一个放置在循环中的客户端创建一个服务器-客户端系统,以不断地从服务器查询一组预定的信息。我已经根据我对 Qt 框架的 TCP 实现如何工作的理解编写了一些代码,但我不确定我所写的是否是正确的方法。


首先,我在客户端 QTcp 类中创建了一个带有一堆 QByteArray 变量的枚举,以作为查询从客户端传递到服务器,如下所示

enum datamap
{
    QByteArray data1 = 1;
    QByteArray data2 = 2;
    QByteArray data3 = 3;
    // and so on...
};

然后我创建一个函数,该函数接受一个枚举datamap 变量传递给服务器和一个变量来保存当前请求(以避免为错误请求接收到的数据之间的混淆),如下所示

datamap current_request = 0;
int client::setDataToGet(datamap& data)
{
    if(socket->state() == QAbstractSocket::ConnectedState)
    {
        current_request = data;
        socket->write(data);
        return socket->waitForBytesWritten();
    }
    else
        return -1;
} 

在此之后,我创建了连接到readyread 信号的readyRead() 插槽,以处理来自服务器的响应并发送到函数,这些函数将根据current_request 变量将接收到的数据显示在相应的文本框中如下图

void client::readyRead()
{
    QTcpSocket *m_socket = static_cast<QTcpSocket*>(sender());
    while(m_socket->bytesAvailable() > 0)
    {
        QByteArray buf = socket->readAll();
    }
    switch(current_request):
    case 1:
        dispToTextBox1(buf);
    case 2:
        dispToTextBox2(buf);
    case 3:
        dispToTextBox3(buf);
    // and so on....
}

现在,对于服务器端,我创建了一个readyRead() 插槽,该插槽连接到来自newConnection() 函数中调用的套接字侦听器的readyread() 信号。

这从客户端获取句柄,因此应该返回与句柄关联的数据。插槽的代码如下

void server::readyRead()
{
    QTcpSocket *m_socket = static_cast<QTcpSocket*>(sender());
    while(m_socket->bytesAvailable() > 0)
    {
        QByteArray buf = m_socket->readAll();
    }
    switch(buf):
    case 1:
        data = collectDatafromStream1();
        m_socket->write(data); m_socket->flush();
    case 2:
        data = collectDatafromStream2();
        m_socket->write(data); m_socket->flush();
    case 3:
        data = collectDatafromStream3();
        m_socket->write(data); m_socket->flush();
    //and so on.....    
}

有人可以验证这是否是正确的方法,或者是否有更好的替代方法来处理任务。

【问题讨论】:

  • "QByteArray data1 = 1; QByteArray data2 = 2; QByteArray data3 = 3; // 以此类推..." - 不看其他内容;这显然很疯狂。为什么不使用容器?
  • 如果您的应用有 GUI,请不要使用 waitForXxx 方法。请改用信号和插槽。此外,在 GUI 程序中,您不能让循环连续做某事,您必须使用 QTimer 之类的东西以特定间隔(包括“尽可能快”的间隔 0)来做某事,或者最好让事情发生作为结果发生其他事情(例如对从网络接收到的数据做出反应,或在 GUI 中使用某些操作)。使用信号和槽。
  • 我计划将 QHash 用于功能工作版本。至于现在,为了包含工作概念中可能出现的错误,我使用了一个枚举来表示流程。
  • 好的。然后我使用信号和插槽而不是循环

标签: c++ qt tcp qtcpsocket qtcpserver


【解决方案1】:

对概念模型进行了一些更改。使用this question 中“sashoalm”给出的响应,使用 QdataStream 传递数据,并使用信号和槽循环遍历每个请求类型的读写序列。


我的服务器类如下

tcpserver.h

#ifndef TCPSERVER_H
#define TCPSERVER_H

#include<qt5/QtNetwork/QTcpServer>
#include<qt5/QtNetwork/QTcpSocket>
#include<qt5/QtCore/QObject>

class TCPServer : public QObject
{
  Q_OBJECT

public:
  explicit TCPServer(QObject *parent = nullptr);

signals:
    void dataReceived(QByteArray);

private slots:
    void newConnection();
    void disconnected();
    void readyRead();

private:
    QTcpServer *server;
    QTcpSocket *socket;
    QHash<QString, int> reverse_hash;

};

#endif // TCPSERVER_H

tcpserver.cpp

#include <iostream>
#include "tcpserver.h"
#include <qt5/QtCore/QDataStream>
#include <qt5/QtCore/QBuffer>
#include <qt5/QtCore/QString>

class BlockWriter
{
public:
    BlockWriter(QIODevice *io)
    {
        buffer.open(QIODevice::WriteOnly);
        this->io = io;
        _stream.setVersion(QDataStream::Qt_4_8);
        _stream.setDevice(&buffer);
        _stream << quint64(0);
    }

    ~BlockWriter()
    {
        _stream.device()->seek(0);
        _stream << static_cast<quint64>(buffer.size());

        io->write(buffer.buffer());
    }

    QDataStream &stream()
    {
        return _stream;
    }

private:
    QBuffer buffer;
    QDataStream _stream;
    QIODevice *io;
};


class BlockReader
{
public:
    BlockReader(QIODevice *io)
    {
        buffer.open(QIODevice::ReadWrite);
        _stream.setVersion(QDataStream::Qt_4_8);
        _stream.setDevice(&buffer);

        qint64 blockSize;

        readMax(io, sizeof(blockSize));
        buffer.seek(0);
        _stream >> blockSize;

        readMax(io, blockSize);
        buffer.seek(sizeof(blockSize));
    }

    QDataStream& stream()
    {
        return _stream;
    }

private:
    void readMax(QIODevice *io, qint64 n)
    {
        while (buffer.size() < n) {
            buffer.write(io->read(n - buffer.size()));
        }
    }

    QBuffer buffer;
    QDataStream _stream;
};

TCPServer::TCPServer(QObject *parent) : QObject(parent)
{
    server = new QTcpServer(this);
    connect(server, SIGNAL(newConnection()), SLOT(newConnection()));
    qDebug() << "Listening:" << server->listen(QHostAddress::Any, 5404);

    reverse_hash.insert("data1", 1);
    reverse_hash.insert("data2", 2);

}

void TCPServer::newConnection()
{
    while (server->hasPendingConnections())
    {
        qDebug()<<"incoming connection!";
        socket = server->nextPendingConnection();
        connect(socket, SIGNAL(readyRead()), SLOT(readyRead()));
        connect(socket, SIGNAL(disconnected()), SLOT(disconnected()));
    }
}

void TCPServer::disconnected()
{
    qDebug() << "disconnected!";
    disconnect(socket, SIGNAL(readyRead()));
    disconnect(socket, SIGNAL(disconnected()));
    socket->deleteLater();
}

void TCPServer::readyRead()
{
    qDebug() << "Read!";
    QString data;
    BlockReader(socket).stream() >> data;
    qDebug() <<"received data request: " << data;

    switch(reverse_hash.value(data))
    {
    case 1: //call sequence to respond to request.(write to data)
        qDebug() << "responding go data1 request!";
        break;
    case 2://call sequence to respond to request.(write to data)
        qDebug() << "responding go data2 request!";
        break;

    }

    BlockWriter(socket).stream()<<data;
    socket->flush();

}

我的客户端GUI类如下

主窗口.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtNetwork>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QTcpSocket *socket;
    QHash<int, QString> hash;
    int current_slot, starting_slot, ending_slot;

signals:
    void dataSet();

public slots:
    void connectToHost();
    void connected();
    void disconnected();
    void setDatatoGet();
    void getData();
    //bool writeData(QByteArray data);

};

#endif // MAINWINDOW_H

主窗口.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <iostream>

class BlockWriter
{
public:
    BlockWriter(QIODevice *io)
    {
        buffer.open(QIODevice::WriteOnly);
        this->io = io;
        _stream.setVersion(QDataStream::Qt_4_8);
        _stream.setDevice(&buffer);
        _stream << quint64(0);
    }

    ~BlockWriter()
    {
        _stream.device()->seek(0);
        _stream << static_cast<quint64>(buffer.size());
        io->write(buffer.buffer());
    }

    QDataStream &stream()
    {
        return _stream;
    }

private:
    QBuffer buffer;
    QDataStream _stream;
    QIODevice *io;
};


class BlockReader
{
public:
    BlockReader(QIODevice *io)
    {
        buffer.open(QIODevice::ReadWrite);
        _stream.setVersion(QDataStream::Qt_4_8);
        _stream.setDevice(&buffer);

        qint64 blockSize;
        readMax(io, sizeof(blockSize));
        buffer.seek(0);
        _stream >> blockSize;
        readMax(io, blockSize);
        buffer.seek(sizeof(blockSize));
    }

    QDataStream& stream()
    {
        return _stream;
    }

private:
    void readMax(QIODevice *io, qint64 n)
    {
        while (buffer.size() < n) {
            buffer.write(io->read(n - buffer.size()));
        }
    }
    QBuffer buffer;
    QDataStream _stream;
};


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    starting_slot = 1;
    current_slot = starting_slot;
    ending_slot = 2;


    ui->setupUi(this);

    ui->status_label->setStyleSheet("background-color:red;");
    ui->receive_btn->setEnabled(false);

    socket = new QTcpSocket(this);
    connectToHost();

    connect(ui->conn_btn, SIGNAL(clicked()), this, SLOT(connectToHost()));
    connect(ui->receive_btn, SIGNAL(clicked()), this, SLOT(setDatatoGet()));
    connect(this, SIGNAL(dataSet()), this , SLOT(setDatatoGet()));


}

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

void MainWindow::connectToHost()
{
    socket->deleteLater();
    socket = new QTcpSocket(this);
    socket->connectToHost(QHostAddress("192.168.0.127"), 5404);
    connect(socket, SIGNAL(connected()), this, SLOT(connected()));
    connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));

    //ADD to hash table
    hash.insert(1, "data1");
    hash.insert(2, "data2");

}

void MainWindow::connected()
{
    ui->status_label->setStyleSheet("background-color:green;");
    ui->receive_btn->setEnabled(true);
    connect(socket, SIGNAL(readyRead()),this, SLOT(getData()));

}

void MainWindow::disconnected()
{
    ui->status_label->setStyleSheet("background-color:red;");
    ui->receive_btn->setEnabled(false);
    disconnect(socket, SIGNAL(readyRead()),this, SLOT(getData()));

}

void MainWindow::setDatatoGet()
{

    if(current_slot == ending_slot + 1)
    {
        current_slot = starting_slot;
    }

    qDebug() <<"calling request data slot " << current_slot;


    BlockWriter(socket).stream() << hash.value(current_slot);

    socket->flush();
    current_slot++;

}

void MainWindow::getData()
{
    QString data;
    BlockReader(socket).stream() >> data;
    //qDebug() <<"received response, current received data is for slot "<< data <<"and current number is" << current_slot;
    switch (current_slot - 1)
    {
    case 1:
        //display in respective label
        qDebug() << "display data1 to label!";
        break;
    case 2:
        //display in respective label
        qDebug() << "display data2 to label!";
        break;
    }

    emit dataSet();
}


【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 2020-01-09
    • 2015-09-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多