1

After a first topic that help me send correctly data to someone using UDP Protocol, I have a problem in the reception of these data. This problem is very strange and only happen the first time I launch the function for sending data. The first time, the server only receive the first frame. But after, if I re use the function everything is ok.

So here's the code that send data through UDP Protocol (my data is a structure) :

void MyUDP::sendUDP()
{
    //Structure to send
    typedef struct MyStructTag
    {
       int test1;
       bool test2;
       char test3;
    } MyStruct;

    MyStruct envoie;

    envoie.test1 = 1;
    envoie.test2 = true;
    envoie.test3 = 97;

    // Sends the datagram datagram
    // to the host address and at port.
    // qint64 QUdpSocket::writeDatagram(const QByteArray & datagram,
    //                      const QHostAddress & host, quint16 port)

    QByteArray buf;
    QDataStream s(&buf, QIODevice::WriteOnly);
    // The encoding is big endian by default, on all systems. You
    // can change it if you wish.
    if (false) s.setByteOrder(QDataStream::LittleEndian);
    s << (qint32)envoie.test1 << (quint8)envoie.test2 << (qint8)envoie.test3;

    //I'm sending 5 frames        

    socket->writeDatagram(buf, QHostAddress("10.100.14.79"), 4000);
    socket->writeDatagram(buf, QHostAddress("10.100.14.79"), 4000);
    socket->writeDatagram(buf, QHostAddress("10.100.14.79"), 4000);
    socket->writeDatagram(buf, QHostAddress("10.100.14.79"), 4000);
    socket->writeDatagram(buf, QHostAddress("10.100.14.79"), 4000);
}

And here's the function that allow me te receive these data :

void MyUDP::readyRead()
{

    QHostAddress sender;
    quint16 senderPort;

    // qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize,
    //                 QHostAddress * address = 0, quint16 * port = 0)
    // Receives a datagram no larger than maxSize bytes and stores it in data.
    // The sender's host address and port is stored in *address and *port
    // (unless the pointers are 0).

    typedef struct MyStructTag
    {
       int test1;
       bool test2;
       char test3;

    } MyStruct;

    MyStruct recois;
    socket->readDatagram((char*)&recois, sizeof (recois), &sender, &senderPort);


    qDebug() << "Message from: " << sender.toString();
    qDebug() << "Message port: " << senderPort;
    qDebug() << "Message: " << recois.test3;
}

Why did I only receive 1 frame the first time I launch sendUDP ?

vhu
  • 12,244
  • 11
  • 38
  • 48
Evans Belloeil
  • 2,413
  • 7
  • 43
  • 76
  • 1
    Because UDP isn´t reliable per design? – deviantfan Jun 12 '14 at 13:25
  • @deviantfan I'm in local transmission, so i won't use another protocol. And if the problem is always the same, that means it comes from my code. – Evans Belloeil Jun 12 '14 at 13:29
  • 1
    Only local and deterministic i no guarantee for anything. Had such situations myself in the past, local UDP, and the problem was proven (not by me) to be the OS part. – deviantfan Jun 12 '14 at 16:05

2 Answers2

4

There are two problems:

  1. Within the readyRead you must loop while socket->hasPendingDatagrams() is true.

  2. You must use the QDataStream on both the sending and the receiving end.

Finally, you are writing C++, you should not use the C structure syntax. It is also counterproductive to have the structure declaration duplicated. What you need is to have the streaming operators for MyStruct.

Below is a complete example.

#include <QCoreApplication>
#include <QUdpSocket>
#include <QDataStream>
#include <QBasicTimer>

static const quint16 port = 4000;

class MyUDP : public QObject {
   Q_OBJECT
   QUdpSocket m_socket;
   QBasicTimer m_timer;

   void timerEvent(QTimerEvent*ev) {
      if (ev->timerId() != m_timer.timerId()) return;
      sendUDP();
   }
   void sendUDP();
public:
   MyUDP() {
      m_timer.start(1000, this);
      connect(&m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
      m_socket.bind(QHostAddress::LocalHost, port);
   }
   Q_SLOT void readyRead();
};

struct MyStruct {
   int test1;
   bool test2;
   char test3;
   MyStruct() {}
   MyStruct(int t1, bool t2, char t3) : test1(t1), test2(t2), test3(t3) {}
};

template <typename T> T get(QDataStream & str) {
   T value;
   str >> value;
   return value;
}

QDataStream & operator<<(QDataStream & str, const MyStruct & m)
{
   return str << (qint32)m.test1 << (bool)m.test2 << (qint8)m.test3;
}

QDataStream & operator>>(QDataStream & str, MyStruct & m)
{
   m.test1 = get<qint32>(str);
   m.test2 = get<bool>(str);
   m.test3 = get<qint8>(str);
   return str;
}

void MyUDP::sendUDP()
{
   MyStruct envoie(1, true, 97);

   QByteArray buf;
   QDataStream s(&buf, QIODevice::WriteOnly);
   // The encoding is big endian by default, on all systems. You
   // can change it if you wish.
   if (false) s.setByteOrder(QDataStream::LittleEndian);
   s << envoie;

   for (int i = 0; i < 5; ++ i) {
      m_socket.writeDatagram(buf, QHostAddress::LocalHost, port);
   }
}

void MyUDP::readyRead()
{
   QHostAddress sender;
   quint16 senderPort;

   MyStruct recois;
   while (m_socket.hasPendingDatagrams()) {
      QByteArray buf(m_socket.pendingDatagramSize(), Qt::Uninitialized);
      QDataStream str(&buf, QIODevice::ReadOnly);
      m_socket.readDatagram(buf.data(), buf.size(), &sender, &senderPort);
      str >> recois;
      qDebug() << "Message from: " << sender;
      qDebug() << "Message port: " << senderPort;
      qDebug() << "Message: " << recois.test3;
   }
}

int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   MyUDP udp;
   return a.exec();
}

#include "main.moc"
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • It works fine, until I want to save the structure in a file, i add this line to readyRead, and now I still have the same error of not receiving all the frame the first time. FILE *fichier = fopen("test.txt", "wb"); if(fichier) { fwrite((char*)&recois, sizeof(recois), 6, fichier); fclose(fichier); } else std::cerr << "Impossible d'ouvrir le fichier !" ; – Evans Belloeil Jun 13 '14 at 07:52
  • This is really weird, my add doesn't impact the code, and if I cancel my change, the bug is still here now ... – Evans Belloeil Jun 13 '14 at 08:03
  • @EvansBelloeil You cannot "write" nor "read" a structure by directly dumping its contents to a file - as I've said, it's not portable. If you do not want to use the `QDataStream`, you must implement the packing/unpacking functionality yourself, or look for a library that handles it for you. If you want to implement it from scratch in C, you're free to ask another question. There's no benefit to using the C file api in a C++ program. Your code will balloon and you'll be dealing with things that Qt already deals with. – Kuba hasn't forgotten Monica Jun 13 '14 at 11:32
  • @EvansBelloeil Whatever structure you wrote using the FILE-based C code is likely unreadable using the C++ code, proving my point that doing so is *not* portable. The `QDataStream`-based code will work on *all* platforms that Qt runs on. Your C code is not guaranteed to work anymore if you switch your C compiler. – Kuba hasn't forgotten Monica Jun 13 '14 at 11:34
  • @EvansBelloeil But in any case, the code I've shown works, so if you insist on not using it, you're on your own (or ask another question). – Kuba hasn't forgotten Monica Jun 13 '14 at 11:35
0

Not really enough information in your question to be sure, but when you read a datagram, you only get 1 datagram per read. Unlike TCP which reads in a stream mode, UDP is message-oriented. If you want to read more messages, do multiple reads or read them in a loop. Note also that you can't rely on guaranteed order or even guaranteed delivery...

mark
  • 5,269
  • 2
  • 21
  • 34