Qt名古屋勉強会に参加
1週間過ぎてしまいましたが、先週名古屋のQt勉強会に参加してきました。
前回はQMLを中心に触っていたので、今回はC++中心に触る事にしました。 色々あとあと使えそうなTCP/IPで通信する機能を実装することに。
作ったソフト
できたソフトがこんな感じ。
先週のQt勉強会で作ったTCPIPのサンプル。マウスの軌跡を飛ばしてる。 pic.twitter.com/jmbTxED2gd
— ayuma (@ayuma_x) July 28, 2018
やっていることはシンプルで、Client側のViewをマウスでドラッグするとマウスの座標をServer側に飛ばしてServer側のViewに表示してます。
上記のプロジェクト一式はこちら
書いたコード抜粋
Webにもいくつかサンプルコードが載ってたので、あまり迷わず書けました。
ソケットの送信側はこんな感じ
void TCPClientModel::SendMessage(QString message)
{
if (tcpClient == nullptr) return;
QByteArray messageBytes = message.toUtf8();
int messageSize = messageBytes.length();
QByteArray sizeBytes;
QDataStream stream(&sizeBytes, QIODevice::WriteOnly);
stream << messageSize;
tcpClient->write(sizeBytes, sizeBytes.length());
tcpClient->write(messageBytes, messageBytes.length());
tcpClient->flush();
}
TCP/IPのデータ部分はシンプルにサイズ領域とメッセージ領域の2つに分けて、サイズ部分に格納されたバイト数分メッセージが続く感じに。 このサイズをbyte arrayに書き込む部分が地味に悩んで、結果QDataStreamっていうのを見つけてこれを使う事にした。
あとは通信時の遅延を減らすために、1回送信する度にflushしてる。ちょっと探した感じNoDelayオプションの設定方法が見つからなかった。。 今度ちゃんと探そう。
次に受信部分はこんな感じ
void TCPClientModel::OnReceived()
{
while (tcpClient->bytesAvailable() > 0)
{
tempBuffer.append(tcpClient->readAll());
while ((bodySize == 0 && tempBuffer.size() >= 4)
|| (bodySize > 0 && tempBuffer.size() >= bodySize))
{
if (bodySize == 0 && tempBuffer.size() >= 4)
{
QByteArray numArray = tempBuffer.mid(0, 4);
QDataStream stream(&numArray, QIODevice::ReadOnly);
stream >> bodySize;
tempBuffer.remove(0, 4);
}
if (bodySize > 0 && tempBuffer.size() >= bodySize)
{
QByteArray bodyArray = tempBuffer.mid(0, bodySize);
tempBuffer.remove(0, bodySize);
bodySize = 0;
emit dataReceived(QString::fromUtf8(bodyArray));
}
}
}
}
TCP/IPは受信したときにデータが足りてなかったり、逆にデータと次のデータがくっついてるときとかあるので、送信するときよりは面倒。
まずはサイズを知るために4byteバッファが溜まるのを待ってから、メッセージサイズを覚えて、覚えたサイズ分バッファが溜まるのを待ってメッセージを復元してる。
でもこの書き方だと、QByteArrayのmidやらremoveやらを多用してるのでメモリの再配置がたくさん起こっているような予感。。。送信するメッセージサイズが多くなったり送信間隔を短くするならもっと工夫が必要かも。
まとめ
前回の時よりもC++のコードを多めでQMLとC++を連携したソフトを作れて満足です。 今回作成したくらいのコードなら全部Qtのクラスのみで作れるのでマルチプラットフォーム対応になってるはず。 こんな感じであといくつかサンプル作ってから、ちょっとしたアプリにつなげていきたいです。