QUdpSocket is not properly sending messages from s

2019-03-06 02:24发布

问题:

I am building a user interface Server/Client. In order to replicate the problem I prepared a MVCE that replicates the problem I have.

The GUI is described as follows: the Server machine has a QGraphicsView where there are N.1 green square moving within the boundaries of the QGraphicsScene and an obstacle. As soon as the square hits either the boundaries or the obstacle, the server machine should send a message to the client with the coordinates of the collision.

In order to run the example the steps are:

1) set the port number on both Client and Server (port number has to be the same)

2) Prepare the message to send from Server to Client

3) Connect the Client

4) Send the message from Server to Client

5) Wait until the square hits either the boundaries or the obstacle

6) As soon as it happens the Server should send the coordinates of the collision to the Client. (I am stuck in this last point)

See the picture below that describes

What I tried so far:

It seems that from this post there should be a priority of the messages sent from/to the Server machine. Howerver I can successfully send messages from Server to Client.

Also according to this source in order not to lose information that more than one datagram should be set.

Additionally always from the same post UDP vs TCP could be a major cause of data loss.

See below the code that replicates the problem:

UDP Client

widget.h

namespace Ui {
class Widget;
}
class QUdpSocket;
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();
private slots:
    void on_connectBtn_clicked();
private:
    Ui::Widget *ui;
    QUdpSocket *mSocket;
};
#endif // WIDGET_H

widget.cpp

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    mSocket = new QUdpSocket(this);
    setWindowTitle("Client Machine");
    connect(mSocket, &QUdpSocket::readyRead, [&]() {
       if(mSocket->hasPendingDatagrams()) {
           QByteArray datagram;
           datagram.resize(mSocket->pendingDatagramSize());
           mSocket->readDatagram(datagram.data(), datagram.size());
           ui->listWidget->addItem(QString(datagram));
       }
    });
}

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

void Widget::on_connectBtn_clicked()
{
    mSocket->bind(ui->portSpinBoxClient->value(), QUdpSocket::ShareAddress);
}

UDP Server

widget.h

#include <QWidget>
#include <QGraphicsItem>
namespace Ui {
class Widget;
}
class QUdpSocket;
class Widget : public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();
private slots:
    void on_sendBtn_clicked();
    void on_pushButton_clicked();
private:
    Ui::Widget *ui;
    QUdpSocket *mSocket;
    QGraphicsScene *scene;
    QTimer *timer;
    QGraphicsRectItem *itemRect;
#endif // WIDGET_H

widget.cpp

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    mSocket = new QUdpSocket(this);
    setWindowTitle("Server Machine");

    scene = new QGraphicsScene(this);
    itemRect = new QGraphicsRectItem();

    ui->graphicsView->setScene(scene);
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);
    scene->setSceneRect(-200, -200, 300, 300);

    QBrush darkBrush(Qt::darkRed);
    QPen darkRedPen(Qt::darkRed);
    itemRect = scene->addRect(-100, -100, 30, 100, darkRedPen, darkBrush);
    itemRect->setFlag(QGraphicsItem::ItemIsMovable);

    QPen myPen = QPen(Qt::red);
    QLineF TopLine(scene->sceneRect().topLeft(), scene->sceneRect().topRight());
    QLineF LeftLine(scene->sceneRect().topLeft(), scene->sceneRect().bottomLeft());
    QLineF RightLine(scene->sceneRect().topRight(), scene->sceneRect().bottomRight());
    QLineF Bottom(scene->sceneRect().bottomLeft(), scene->sceneRect().bottomRight());

    scene->addLine(TopLine, myPen);
    scene->addLine(LeftLine, myPen);
    scene->addLine(RightLine, myPen);
    scene->addLine(Bottom, myPen);

    int itemCount = 1;
    for(int i = 0; i < itemCount; i++) {
        MyItem *item = new MyItem();
        scene->addItem(item);
    }
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), scene, SLOT(advance()));
    timer->start(100);
    ui->pushButton->setStyleSheet("QPushButton{ background-color: grey }");
}

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

void Widget::on_sendBtn_clicked()
{
    auto datagram = ui->msgLineEdit->text().toLatin1();
    mSocket->writeDatagram(datagram, QHostAddress::Broadcast,ui->spinBox->value());
}

void Widget::on_pushButton_clicked()
{
    ui->pushButton->setStyleSheet("QPushButton{ background-color: green }");
    ui->pushButton->setText("Stop");
}

And finally the class where the colliding objects are detected:

myitem.h

class MyItem : public QGraphicsItem
{
public:
    MyItem();
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
protected:
    void advance(int phase);
private:
    qreal angle;
    qreal speed;
    void doCollision();
};

#endif // MYITEM_H

myitem.cpp

#include "myitem.h"
MyItem::MyItem()
{
    angle = (qrand() % 360);
    setRotation(angle);
    speed = 5;
    int startX = 0;
    int startY = 0;
    if((qrand() % 1)){
        startX = (qrand() % 200);
        startY = (qrand() % 200);
    }else{
        startX = (qrand() % -100);
        startY = (qrand() % -100);
    }
    setPos(mapToParent(startX, startY));
}

QRectF MyItem::boundingRect() const
{
    return QRect(0, 0, 30, 30);
}

void MyItem::paint(QPainter *painter,
                   const QStyleOptionGraphicsItem *option,
                   QWidget *widget)
{
    QRectF rec = boundingRect();
    QBrush brush(Qt::gray);
    if(scene()->collidingItems(this).isEmpty()){
        brush.setColor(Qt::green); // no collision
    }else{
        brush.setColor(Qt::red);  // yes collision
        doCollision();
    }
    painter->fillRect(rec, brush);
    painter->drawRect(rec);
}

void MyItem::advance(int phase)
{
    if(!phase) return;
    QPointF location = this->pos();
    setPos(mapToParent(0, -(speed)));
}

void MyItem::doCollision()
{
    if((qrand() %1)) {
        setRotation(rotation() + (180+(qrand() % 10)));
    }else{
        setRotation(rotation() + (180+(qrand() % -10)));
    }
    // see if the new position is in bounds
    QPointF newPoint = mapToParent(-(boundingRect().width()), -(boundingRect().width() + 2));
    if(!scene()->sceneRect().contains((newPoint))){
        newPoint = mapToParent(0, 0); // move it back in bounds
    }else{
        setPos(newPoint); // set new position
    }
}

Expected Result: After the green square hits either the boundaries of the QGraphicsScene or the object, the colliding coordinates should be sent to the Client.

Actual Result: How to send colliding coordinates to the Client?

Any insights on how to solve this issue would be very useful.