memo

プログラミング備忘録





【Qt】UDPSocket通信

Todo:
QUdpSocketクラスを使用して自分のPC内で通信してみます。
ボタンを押して送信、受信出来たら何等かを表示します。

f:id:shinopikapi:20190916001954p:plain
初期画面
f:id:shinopikapi:20190916002012p:plain
ボタン押下後



前準備
~~~~.proファイルに「QT += network 」を追加しておきます。
参考にしたクラス仕様(公式)について、最後にリンク貼っています。

デバッグ確認する際、以下のような警告が出たので許可します。

f:id:shinopikapi:20190916005712p:plain
警告



最終的にpika.hは以下のようになっています。

#include <QUdpSocket>
#include <QDataStream>
#include <QNetworkDatagram>

namespace Ui {
class Pika;
}

class Pika : public QMainWindow
{
    Q_OBJECT

public:
    explicit Pika(QWidget *parent = 0);
    ~Pika();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Pika *ui;

    // 追加
    QUdpSocket *sendUdpSocket;  // 送信
    QUdpSocket *recUdpSocket;   // 受信

    const unsigned int PORT_NUM = 5556;//とりあえず

    void readPendingDatagrams();
    void readMsg(QNetworkDatagram datagram);
};


pika.cpp

#include "pika.h"
#include "ui_pika.h"


Pika::Pika(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Pika)
{
    ui->setupUi(this);

    // 初期化
    sendUdpSocket = new QUdpSocket(this);
    recUdpSocket = new QUdpSocket(this);

    recUdpSocket->bind(PORT_NUM,QUdpSocket::DefaultForPlatform);

    // 受信したら呼ばれる
    connect(recUdpSocket, &QUdpSocket::readyRead, this, &Pika::readPendingDatagrams);

    // label初期化
    ui->label->setText("");
    // 分かりやすいようlabel枠を表示する
    ui->label->setFrameStyle(QFrame::Box|QFrame::Plain);
}

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

void Pika::on_pushButton_clicked()
{
    // 押された -> send
    QByteArray datagram;
    QDataStream msg(&datagram, QIODevice::WriteOnly);
    msg << "123456789";

    sendUdpSocket->writeDatagram(datagram, QHostAddress::LocalHost, PORT_NUM);
}

void Pika::readPendingDatagrams()
{
    while (recUdpSocket->hasPendingDatagrams()) {
        QNetworkDatagram datagram = recUdpSocket->receiveDatagram();
        readMsg(datagram);
    }
}

void Pika::readMsg(QNetworkDatagram datagram)
{
    QByteArray data = datagram.data();
    QString msg = "";

    int len = data.size();
    msg = QString::fromLocal8Bit(data,len);
    ui->label->setText(msg);
}



送信側
最初にボタンとラベルをui画面から作成しておきます。

コンストラクタで送信側については、以下のことをしています。

    sendUdpSocket = new QUdpSocket(this);

    // label初期化
    ui->label->setText("");
    // 分かりやすいようlabel枠を表示する
    ui->label->setFrameStyle(QFrame::Box|QFrame::Plain);


次に、ボタンを押したら送信するようにします。
送信側 pika.cppにてボタンを作成します。

void Pika::on_pushButton_clicked()
{
    // 押された -> send
    QByteArray datagram;
    QDataStream msg(&datagram, QIODevice::WriteOnly);
    msg << "123456789";

    sendUdpSocket->writeDatagram(datagram, QHostAddress::LocalHost, PORT_NUM);
}

ui画面でボタン右クリック→スロットへ移動……から、シグナル on_pushButton_clicked() を作成。
始めて作ってみるので、とりあえず「123456789」で決め打ちで送ります。

writeDatagramで内容、あて先、ポート番号設定して送信します。
自分のPC内で送信→受信するので、あて先は「QHostAddress::LocalHost」、ポート番号は適当に使ってない値を設定。
TCPやUDPにおけるポート番号の一覧 - Wikipedia



受信側

受信側について、コンストラクタで以下のようにしています。

void Pika::on_pushButton_toggled(bool checked)
{
    recUdpSocket = new QUdpSocket(this);

    recUdpSocket->bind(PORT_NUM,QUdpSocket::DefaultForPlatform);

    // 受信したら呼ばれる
    connect(recUdpSocket, &QUdpSocket::readyRead, this, &Pika::readPendingDatagrams);

バインドではどのポート番号から送信が来るか設定しています。
BindMode modeはとりあえずデフォルトにしています。


公式より、以下のことが書いてありました。

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() or receiveDatagram() to read it.

Note: An incoming datagram should be read when you receive the readyRead() signal, otherwise this signal will not be emitted for the next datagram.

コンストラクタでconnectしておき、受信したらPika::readPendingDatagramsを呼ばれるようにしました。
readPendingDatagrams()については、公式の見本そのまま使っています。


readMsg()では、QNetworkDatagram型でもらってきたデータをQByteArrayにして、それをQStringに変換してラベル表示しています。


QByteArray dataをQStringに変換する場合、以下のようにしています。

    int len = data.size();
    QString msg = QString::fromLocal8Bit(data,len);

今回一番引っかかったのが、最後のQByteArray→QString変換でバイナリを表示する箇所……
長さ指定したら表示出来ました。

実際作るならチェックサムとかやった方がいいと思います。



参考:
https://doc.qt.io/qt-5/qudpsocket.html
https://doc.qt.io/qt-5/qdatastream.html
https://doc.qt.io/qt-5/qstring.html
https://doc.qt.io/qt-5/qframe.html