Jlalt Jlalt - 1 month ago 29
C++ Question

c++ QT QUdp socket not sending / receiving data?

According to this page https://wiki.sa-mp.com/wiki/Query_Mechanism I can send some data to the host and it will respond with some information to me, am I correct?
if no I would be thankful if any one could help me in this.

However I am doing the below codes but it just simple doing nothing O.O any one know what I am doing wrong?

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QUdpSocket>
#include <qmessagebox.h>

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QUdpSocket *mysock = new QUdpSocket(this);
mysock->writeDatagram("i", QHostAddress("164.132.69.65"), 7777);
QByteArray myarry;
QHostAddress myhost;
quint16 port;
mysock->readDatagram(myarry.data(), myarry.size(), &myhost, &port);
ui->textEdit->setText(myarry.data());
}

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


myarry.data() is just simple empty ;-;....

Answer

QUdpSocket::readDatagram() (and most Qt network IO functions) do not block until data is available. This means that if you call it at a point where no data has arrived yet, you'll not have any data in the buffer you provided and the call should return -1.

From the docs:

The readyRead() signal is emitted whenever datagrams arrive. In that case, hasPendingDatagrams() returns true. Call pendingDatagramSize() to obtain the size of the first pending datagram, and readDatagram() to read it.

You should be calling readDatagram() from a slot that is connected to the readyRead() signal.

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //mysock should be a member of the MainWindow class
    //so that you can access it from other functions
    mysock = new QUdpSocket(this);
    mysock->writeDatagram("i", QHostAddress("164.132.69.65"), 7777);
    connect(mysock, &QUdpSocket::readyRead, this, &MainWindow::readPendingDatagrams);
}

//add this slot to the class's declaration
void MainWindow::readPendingDatagrams(){
    //read datagrams in a loop to make sure that all received datagrams are processed
    //since readyRead() is emitted for a datagram only when all previous datagrams are read
    while(mysock->hasPendingDatagrams()){
        QByteArray datagram;
        datagram.resize(mysock->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;

        mysock->readDatagram(datagram.data(), datagram.size(),
                                &sender, &senderPort);

        //do whatever you want to received datagram
        ui->textEdit->setText(datagram.data());
    }
}

Edit:

Looks like the request you are sending to the server does not conform to the specifications in your link. In your code, you are just sending an "i" letter and nothing else.

The page denotes (in brief) that you should send the prefix "SAMP" followed by the server's ip then the server's port, after that you can send your opcode (which is "i" in your case).

Putting that altogether, Here is how I would implement it:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //mysock should be a member of the MainWindow class
    //so that you can access it from other functions
    mysock = new QUdpSocket(this);
    QHostAddress serverAddress("164.132.69.65");
    quint16 port= 7777;
    QByteArray data;
    //building request
    data+= "SAMP";      //"SAMP" prefix
    //convert to big endian to make sure the octets are sent in the specified order
    quint32 ipv4AddrBigEndian= qToBigEndian(serverAddress.toIPv4Address());
    //convert to little endian to make sure port bytes are sent in the specified order
    quint16 portLittleEndian= qToLittleEndian(port);
    //add server ip address
    data.append(reinterpret_cast<char*>(&ipv4AddrBigEndian), sizeof(ipv4AddrBigEndian));
    //add server port
    data.append(reinterpret_cast<char*>(&portLittleEndian), sizeof(portLittleEndian));
    //add opcode
    data.append("i");
    mysock->writeDatagram(data, serverAddress, port);
    connect(mysock, &QUdpSocket::readyRead, this, &MainWindow::readPendingDatagrams);
}


void MainWindow::readPendingDatagrams(){
    //read datagrams in a loop to make sure that all received datagrams are processed
    //since readyRead() is emitted for a datagram only when all previous datagrams are read
    while(mysock->hasPendingDatagrams()){
        QByteArray datagram;
        datagram.resize(mysock->pendingDatagramSize());
        QHostAddress sender;
        quint16 senderPort;

        mysock->readDatagram(datagram.data(), datagram.size(),
                                &sender, &senderPort);

        qDebug() << "received: " << datagram.toHex();

        //do whatever you want to received datagram
        //you need to parse information you want from the datagram
        //It can NOT be just displayed as text in a QTextEdit (as it contains binary data)
    }
}

The format of the server's binary reply that you need to parse is specified in the page you linked here.


Side Note:

I really think that you should separate out building requests and parsing responses in their own class, instead of having all of that in the MainWindow class (which should be used for the definition of the UI and nothing else).