首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >并发处理多个Qtcp连接以显示在大理石小部件上

问并发处理多个Qtcp连接以显示在大理石小部件上
EN

Stack Overflow用户
提问于 2019-12-11 21:16:25
回答 1查看 181关注 0票数 0

我有一个使用qt创建者的应用程序,它获取地理坐标数据并在map.it上显示它,只需一个连接就可以工作,但是当涉及到拥有不止一次时,它就成了一个谜。我希望从几个具有不同地址和端口的qtcp连接中获取一个循环中的数据,然后将数据放入分离的列表中,并在读取每个连接地理数据列表之后,在不同线程中在映射上添加单独的层,该线程每3秒更新一次,而所有这些线程都同时从网络数据包中获取数据。假设我有许多不同的gps接收器来收集他们的位置数据,我想把它整合到地图上。下面是我的代码示例:

1.定义qtcp客户端应该连接的服务器列表:

globals.cpp

代码语言:javascript
复制
#define PACKET 50
struct Connects{
    static QMap<QString,int> list()
        {
          QMap<QString,int> m;
          m["sender1.com"] = 4456;
          m["sender2.com"] = 4457;
          m["sender3.com"] = 4458;
          return m;
        }
    static const QMap<QString,int> myMap;

};
QMap<QString, int> const Connects::myMap = list();

2.发射地图的主要窗口:

main.cpp

代码语言:javascript
复制
void add_layer(MarbleWidget *mapWidget,MainWindow *window,Client *client,QTimer *timer ){
        //get data and append to a list every 3 sec
        timer = new QTimer;
        QObject::connect(timer,SIGNAL(timeout()),window,SLOT(next_client()));
        QObject::connect(client,SIGNAL(Client_Connected()),window,SLOT(online_slot()));
        timer->start(3000);
        // Add New Layer based on positions in the list
        MPaintLayer* layer = new MPaintLayer(mapWidget);
        for(int j = 0; j < client->data.count(); ++j){
                layer->setPosition(client->data.at(j).Lat,client->data.at(j).Lon);
        }
        mapWidget->addLayer(layer);
}

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    // Load Marble
    MarbleWidget *mapWidget = new MarbleWidget;
    mapWidget->show();
    //iterate over connections address and port list
    QMapIterator<QString,int> i(Connects::myMap);
    while (i.hasNext()) {
        i.next();
        client = new Client;
        client->ConnectToServer(i.key(),i.value());
        //make thread pool and create new thread for adding each layer
        QThreadPool pool;
        QFuture<void> tr;
        tr = QtConcurrent::run(&pool,add_layer,mapWidget,this,client,timer);
    }
}
void MainWindow::online_slot()
{
     //fetch data from the buffer and separate each entity based on size
     while (client->GetLanBufferSize() >= PACKET) {
          client->ReadData((char*)buffer,PACKET);
          double lat,lon;
          memcpy(&lat,&buffer[8],sizeof(double));
          memcpy(&lon,&buffer[16],sizeof(double));
          //put buffer data to target list which is defined in client
          Target trg;
          client->data.append(trg);
     }
    //remove has-readed data
    for (int var = 0; var < client->data.count(); ++var) {
        client->data.removeAt(var);
        var--;
    }
}

3.根据地图上收到的数据,声明用于添加或更新新层的类:

paintlayer.cpp

代码语言:javascript
复制
MPaintLayer::MPaintLayer(MarbleWidget* widget) 
{
    S_N = QPixmap(":/mode-s.png");
}
bool MPaintLayer::render( GeoPainter *painter,ViewportParams *viewport,
                          const QString& renderPos, GeoSceneLayer * layer )
{
    //throw a tiny png file on map based on retrieved locations
    for (int var = 0; var < pos.count(); ++var) {
        painter->drawPixmap(pos[var],S_N));
    }
    return true;
}
void MPaintLayer::setPosition(double lat,double lon)
{
  pos.append(GeoDataCoordinates(lat,lon,0,GeoDataCoordinates::Degree));

}

5.为接收到的数据格式定义一个类:

client.h

代码语言:javascript
复制
class Target
{
public:
    Target()
    {
        Lat = Lon = 0;
    }
    double Lat,Lon;
    GeoDataCoordinates Position;
    void update(double lat,double lon)
    {
        Lat = lat;
        Lon = lon;
        Position = GeoDataCoordinates(Lon,Lat,0,GeoDataCoordinates::Degree);
    }
};

6.声明用于建立新的qtcp连接和接收数据的类:

client.cpp

代码语言:javascript
复制
Client::Client(QObject *parent) : QObject(parent)
{
    socket = new QTcpSocket(this);
    connect(socket, SIGNAL(connected()),this, SLOT(on_connected()));
}
void Client::on_connected()
{
    connect(socket, SIGNAL(disconnected()),this, SLOT(on_Disconnected()));
    socket->setReadBufferSize(12e6);
    emit Client_Connected();
}
bool Client::ReadData(char *buffer, int Leanth)
{
    socket->read(buffer , Leanth);
    return true;
}
int Client::GetLanBufferSize()
{
    return socket->bytesAvailable();
}
void Client::on_Disconnected()
{
    emit Client_DisConnected();
}
void Client::ConnectToServer(QString Address , int Port)
{
    socket->connectToHost(Address, Port);
    server = Address;
    server_Port = Port;
}

从发送者收到的数据格式是移动对象的位置,如下所示:

代码语言:javascript
复制
35.51243 51.22478
35.51260 51.22667
35.69270 51.03961

什么是最佳实践解决方案?因为地图上什么都没有。当我使用硬代码时,它显示了与每个连接等效的层,但是读取数据流是有挑战性的。任何线索都将不胜感激。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-12-12 09:54:16

您没有以正确的方式使用QTcpSocket。在您的Client类中,您可以处理所有的阅读内容,甚至不需要那些while循环。使用QIODevice::readyRead()信号和插槽内的数据,qint64 maxSize)方法来读取和解析数据。

这里的问题是,不能保证这些数据将在发送方接收到对齐。例如,如果您从发送方发送>>>SOME_LENGTHY_DATA<<<,QTcpSocket可能会发出两次readyRead()信号,并且您可以在对读取槽的两个不同调用中读取>>>SOME_LENG和THY_DATA<<<。

因此,您应该负责解析您的数据,因为数据是一个字节一个字节的字节。您可以在类的某个位置缓冲传入的数据,或者只使用QTcpSocket内部缓冲区。您确实应该注意将即将进入的消息块分离开来。

下面是Client的一个实现。我使用QPointF来存储点,而不是存储Target类,因为我不知道它是什么。您可以根据解析的lat/lon创建自己的类并将其附加到Client::data中。我没有测试代码,但它应该能工作。我不确定您是否需要与此实现并发,但您将不需要一个IMHO。

代码语言:javascript
复制
//Client.h

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

private:
    QString serverAddress;
    int serverPort;
    QList<QPointF> data; //you can use your own type, just for test

    QByteArray lastLine;

    void parseData();

signals:
    void Client_Connected();

public slots:
    void connectToServer(const QString& address, quint16 port);
    void processData();

};
代码语言:javascript
复制
//Client.cpp

#include "client.h"

Client::Client(QObject *parent) : QTcpSocket (parent)
{
    // we don't really want Client_Connected signal!
    // you can use QTcpSocket::connected signal
    connect(this, SIGNAL(connected()),this, SIGNAL(Client_Connected()));
    // readyRead will be emitted when there is data in buffer to be read
    connect(this, SIGNAL(readyRead()),this, SLOT(processData()));
}

void Client::parseData()
{
    QString str(lastLine);
    str = str.trimmed();
    QStringList parts = str.split(" ",QString::SkipEmptyParts);
    if(parts.count()==2){
        bool success = true;
        double latitude = success ? parts[0].toDouble(&success) : 0;
        double longitude = success ? parts[1].toDouble(&success) : 0;
        if(success)
            data.append(QPointF(longitude,latitude));
    }
}

void Client::connectToServer(const QString& address, quint16 port)
{
    data.clear();
    lastLine.clear();

    serverPort = port;
    serverAddress = address;
    this->connectToHost(address,port);
}

void Client::processData()
{
    QByteArray d = this->readAll();
    for(int i = 0 ; i < d.count() ; ++i){
        if(d.at(i)=='\r')
        {
            parseData();
            lastLine.clear();
        }
        else
            lastLine.append(d.at(i));
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59294212

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档