昨日も書いたようにQtでソフトウエアを作っている。
目的はUDPを使ったプログラムを作るため。
UDPってブロードキャストできるから楽しそうですね。ということで遊んでみるために作っている。
GUIでやりたくて、せっかくC++やってきたんだからQtかなと思ってQtでやっている。
QtにはQUdpSocketというのがあるので、これを使えばさほど苦労せずに作れる。
とはいえどうやってやればいいかはなかなかわからんのでサンプル見た。
それを参考にUDPAgentクラスを作ってみた。
まずヘッダファイル。
#include <QObject>
#include <QString>
#include <QtNetwork>
class UDPAgent : public QObject{
Q_OBJECT
private:
QUdpSocket udp;
private slots:
void _recive(void);
public slots:
void SendBloadcast(const QString& data);
signals:
void Recive(const QString& data);
public:
UDPAgent(void);
};
ところでQtのシグナルとスロットってのはご存じですかね?
シグナル言うのはなにかあったら発生するもの。
スロット言うのはメソッドで、シグナルに接続することのできるもの。
シグナルはクラスの定義の時に signals: 以下に定義します。
定義だけして実装はしないわけですね。この辺はQtは勝手に実装してくれると。
スロットは public slots: か private slots: 以下に定義して、内容も他のメソッド同様に実装すると。
そういうことを踏まえてみて欲しい。
なんか受信したらReciveというシグナルが発生して、なにか送りたければSendBloadcastというメソッドを実行すればよいと。
そういう設計になっています。
ソースコードですが、こんな感じ。
#include "udpagent.h"
UDPAgent::UDPAgent(void):udp(this){
connect(&this->udp,SIGNAL(readyRead()),this,SLOT(_recive()));
udp->bind(12345);
}
void UDPAgent::_recive(void){
QTextCodec* euc=QTextCodec::codecForName("EUC-JP");
QTextDecoder eucdec(euc);
while(udp.hasPendingDatagrams()){
QByteArray datagram;
datagram.resize(udp.pendingDatagramSize());
udp.readDatagram(datagram.data(),datagram.size());
this->Recive(eucdec.toUnicode(datagram));
}
}
void UDPAgent::SendBloadcast(const QString& data){
QUdpSocket udp;
QTextCodec* euc=QTextCodec::codecForName("EUC-JP");
QTextEncoder eucenc(euc);
this->Debug(QString("Send : %1").arg(data));
QByteArray bytedata=eucenc.fromUnicode(data);
udp.writeDatagram(bytedata,QHostAddress::Broadcast,12345);
}
コンストラクタで、udpが受信できるようになったときに発生するreadyReadシグナルと、privateなスロット_reciveを接続しています。
こういう風にして接続することを書けるんですね。
Qtではこういう風にシグナルとスロットの接続で書くことが多いですね。
_reciveでは受信の処理がいろいろ書いてあります。
データはEUC-JPでエンコードされているはずなのでデコードしてReciveシグナルを発生させると。
シグナルを発生させるのはシグナルのメソッドを実行するだけです。
SendBloadcastメソッドは受信処理とは全く別になっていて、
文字列をEUC-JPでエンコードしてブロードキャストで送信しているだけです。
これでシグナルをなんしかのスロットに接続したりすれば使えるわけですね。
こういう風にQtは使うわけですよ。
ところでさっきEUC-JPでエンコードしたりデコードしたり書きましたが、実はこうなったことには事情があります。
いや、元々はUTF-8だったんですよ。けどちょっと問題がありましてEUC-JPになったわけです。
その問題というのはBOMです。
BOMというのはUTF-16・UTF-32において、ビックエンディアンかリトルエンディアンかを判別するために最初に置く特殊な文字。
ただUTF-8ではその判別はいらないのでBOMはなくてもいいはず。
だけどUTF-16・UTF-32同様にBOMを置くという流儀もあったりする。UTF-8である印になるからとかなんとか。
けど置かないという流儀もある。むしろそれが基本のはず。
BOMさえ置かなければUTF-8というのはASCIIの範囲内ならばASCIIとの互換性があるんですね。
だから置くとこの互換性がなくなって都合が悪いと。そういう考えもあります。
日本では特に付けない方を UTF-8N と表記してあることがあります。TeraPadとか。正式な表記じゃないのだけどね。
ただ、そういうわけでもなく単にUTF-8とあればBOM付きかBOMなしか分からないんですね。
それでQtにエンコードさせたらBOMが付いてエンコードされたわけです。
ただQtにデコードされたらBOMをBOMと認識せずにU+FEFFの文字が一番最初に残ってしまったんですよ。
それでバグが発生していて困ってね。
バグを消すためにUTF-8から日本語に限ればまぁ無難なEUC-JPに変えてみました。
ちょっとこの辺は残念なところなのだが、別にEUC-JPでも問題はないからこれで行こうと思う。