Qtでソケットを操作する

はじめに



画像

数年前のあるフォーラムで、私はそのような素晴らしいフレーズを見つけました。 それから私の知識は私にこれをすることを許しませんでした。 私はただ微笑んでこのフレーズを渡しました。 しかし、つい最近、私はこの特定の問題に直面しました-私は自分のチャットを書かなければなりませんでした。 さて、最近私の関心はQtアプリケーションの研究と開発に集中しており、それが行われるので、それはそれ自体で決定されました。



QtのネットワーキングはQtNetworkを介して行われます。 プロジェクトがサポートを開始するには、.proファイルにQT + = networkを追加する必要があります。 Qtは、いくつかのタイプのソケット接続をサポートしています。 主なものは次のとおりです。QUdpSocketおよびQTcpSocket。 QUdpSocketは、データパケットを交換するためのデータグラムソケットです。 このソケットを使用すると、データが到着したかどうかを確認せずにデータが送信されます。 一方、QTcpSocketはポイントツーポイント接続を確立し、破損とデータ損失に対する追加のメカニズムを提供します。



両方のクラスはQAbstractSocketクラスを継承します。 QSslSocketクラスもありますが、その時点では必要ありませんでしたので、触れません。



チャット



アプリケーションアーキテクチャの設計を始めたときの質問は、「チャットユーザー同士がどの程度正確にやり取りするのか?」でした。 チャット自体は、一般的なチャットウィンドウとファイル転送機能を備えたプライベートメッセージの両方をサポートする必要があったというだけでした。 ファイル転送ですべてが明確な場合-TCPのみがあり、パケットが到着したかどうかを制御およびチェックする必要があるため、メインチャットに問題がありました。 結局のところ、チャットには明確なサーバーはありません。 はい。TCPを介して相互作用を行う場合、各ユーザーにポートを割り当てる必要がありますが、これは少しオーバーヘッドです。 さて、TCPでなければ、UDP! これには完全に適しています。 特定のポートを聞いて、同じポートに送信したユーザーから全員にメッセージを受信します。 また、チャットメッセージが失われても、それほど怖くはありません。



QUdpSocket



結局のところ、QUdpSocketクラスの操作は簡単ではありませんが、非常に簡単です。 クラスオブジェクトを作成し、指定されたポートにバインドし、QIODeviceクラスの信号に接続して、このポートにメッセージが到着するのを待ちます。

UdpChat(QString nick, int port) {

nickname = nick;

socket = new QUdpSocket( this );

_port = port;

socket->bind(QHostAddress::Any, port);

// //

connect(socket, SIGNAL(readyRead()), SLOT(read()));

}



* This source code was highlighted with Source Code Highlighter .






また、次のコードブロックの前に、この場合、チャットはネットワーク上の人々の数を知らないことを伝えたいと思います。 ブロードキャスト要求のみを行います。 しかし、人々のリストを作成するために、「Who's online?」のようなものを送信します。このメッセージを受信した人は「I!」を送信します。 したがって、3種類のメッセージがあります。

  1. ユーザーからの通常のメッセージ
  2. メッセージ-オンラインユーザー
  3. メッセージ-オンラインのユーザー


その結果、以下が得られます。

void send(QString str, qint8 type) {

QByteArray data;

QDataStream out (&data, QIODevice::WriteOnly);

out << qint64(0);

out << qint8(type);

out << str;

out .device()->seek(qint64(0));

out << qint64(data.size() - sizeof (qint64));

socket->writeDatagram(data, QHostAddress::Broadcast, _port);

}



void read() {

QByteArray datagram;

datagram.resize(socket->pendingDatagramSize());

QHostAddress *address = new QHostAddress();

socket->readDatagram(datagram.data(), datagram.size(), address);



QDataStream in (&datagram, QIODevice::ReadOnly);



qint64 size = -1;

if ( in .device()->size() > sizeof (qint64)) {

in >> size;

} else return ;

if ( in .device()->size() - sizeof (qint64) < size) return ;



qint8 type = 0;

in >> type;



if (type == USUAL_MESSAGE) {

QString str;

in >> str;

// //

} else if (type == PERSON_ONLINE) {

// QHostAddress //

} else if (type == WHO_IS_ONLINE) {

sending(nickname, qint8(PERSON_ONLINE));

}

}




* This source code was highlighted with Source Code Highlighter .






すべてが透明で明確です。 インターフェースを固定し、すべての信号を接続し、メインチャットウィンドウの準備ができたままです。



QTcpSocket



このソケットに関しては、クライアントサーバーモデルを実装しています。 つまり、私たちの場合、最初に誰かにプライベートメッセージを書いたりファイルを送信したりすると、各ユーザーはサーバーのようになります。 または、クライアントが最初にプライベートで彼を書いた場合。



メッセージの送信に問題はありません。 しかし、ファイルを送信するには、古典的なトリックを行います。 ファイル全体をストリームにプッシュできないため、ファイルからデータを読み取り、ストリームに部分的に書き込み、送信します。

void sendFile(QString fileName) {

if (sendFile != NULL){

return ;

}

sendFile = new QFile(fileName);

if (sendFile->open(QFile::ReadOnly)){

QByteArray data;

QDataStream out (&data, QIODevice::WriteOnly);

// //

clientSocket->write(data);

clientSocket->waitForBytesWritten();

connect(clientSocket, SIGNAL(bytesWritten(qint64)), this , SLOT(sendPartOfFile()));

sendPartOfFile();

} else {

emit this ->errorSendFile(QString( "File not can open for read" ));

return ;

}

}



void sendPartOfFile() {

char block[SIZE_BLOCK_FOR_SEND_FILE];

if (!sendFile->atEnd()){

qint64 in = sendFile->read(block, sizeof (block));

qint64 send = clientSocket->write(block, in );

} else {

sendFile->close();

sendFile = NULL;

disconnect(clientSocket, SIGNAL(bytesWritten(qint64)), this , SLOT(sendPartOfFile()));

emit endSendFile();

}

}




* This source code was highlighted with Source Code Highlighter .






ストリームの読み取りを検討する必要があります。 MESSAGEとSENDFILEの2つのデータ型のみが入力されるため、通常の入力パーサーの作成は難しくありません。 UDPソケットを実装するクラスメソッドと同じです。 興味深いのは、転送されたファイルのストリームからの読み取りです。

void receiveFile(QString fileName) {

QString savePath = "Downloads/" ;

QDir dir;

dir.mkpath(savePath);

receiveFile = new QFile(savePath + fileName);

sizeReceivedData = 0;

receiveFile();

}



void receiveFile() {

QDataStream in (clientSocket);

if (!bufferForUnreadData.isEmpty()){

receiveFile->write(bufferForUnreadData);

sizeReceivedData += bufferForUnreadData.size();

bufferForUnreadData.clear();

}

char block[SIZE_BLOCK_FOR_SEND_FILE];

while (! in .atEnd()){

qint64 toFile = in .readRawData(block, sizeof (block));

sizeReceivedData += toFile;

receiveFile->write(block, toFile);

}

if (sizeReceivedData == sizeReceiveFile){

receiveFile->close();

receiveFile = NULL;

sizeReceiveFile = 0;

sizeReceivedData = 0;

}

}



* This source code was highlighted with Source Code Highlighter .






これがプライベートメッセージの本質です。 もちろん、信号、接続、および特定のコードが不足しています。 たとえば、送信および受信ファイルは単なる一般的なビューです。 多くが欠けています。 しかし、すべてを追加してインターフェースを固定すれば、完全に機能するチャットクライアントを作成できます。



この記事の目的は、ソケットの操作が簡単で簡単であることを例で示すことでした。 この記事が将来誰かに役立つことを願っています。 がんばって。



All Articles