この記事では、QtライブラリスタックでのヘッセバイナリWebプロトコルの実装について説明します。 C ++ターゲットプラットフォーム。
Hessianは、有用なWebサービスを作成するために設計されたバイナリWebプロトコルです(言い換えれば、このことにより、ウィンドウの外のアプリケーションから天気がどのようなものかを知ることができます)。 ヘシアンは、Caucho Technology、Inc.によって開発されました。 同社は、Java、Python、およびActionScriptのプロトコル実装も開発しました。 サードパーティ企業は、ほぼすべてのプログラミング言語(C ++、C#、Perl、PHP、Ruby、Objective-C、D、Erlang)の実装を開発しています。
サーバー上にリンゴのバスケットがあると仮定します。 そして、クライアントのリクエストに応じて彼にリンゴを渡したいと思います。 これは、ヘシアンが私たちを助ける場所です。
サーバー上で、サービスインターフェイス(Javaのサーバー)を作成します。
public interface FruitService {
Apple getNextApple();
}
* This source code was highlighted with Source Code Highlighter .
クライアントで同様のクラスを作成します(C ++):
class FruitService {
public :
Apple getNextApple();
}
* This source code was highlighted with Source Code Highlighter .
クライアントでgetNextApple()メソッドが呼び出されると、ヘシアンの実装はサーバー(同様のサーバーメソッド)に要求を行い、結果をクライアントに返します。 ヘッシアンは、ネットワークを通じてリンゴを届けることに取り組んでいます。 実際、サーバー上のデータをシリアル化し、クライアント上のデータを逆シリアル化します。 リンゴをバイト配列に変換し、ネットワークを介して転送し、配列からリンゴを復元することはヘシアンのタスクです。 残りはあなたの仕事です。
Qt
フレームワークとしてのQtは広く知られており、導入する必要はほとんどありません。 Qt C ++のヘッセプロトコルの実装についてお話しします。
一般に、C ++でのプロトコルの実装はいくつかあります(私はなんとか2を見つけました)。
Hessian cppとhessianorbは素晴らしいプロジェクトであり、非常に機能的です。 最初は、開発にhessianorbを使用しました-コード生成をサポートしています。 1つのチームですべてのサービスのC ++プロキシを作成できます(2時間の準備後)。
残念ながら、どちらの実装でも、Qt環境では異質なコードを作成する必要があります。 追加のライブラリスタックが必要です:hessian cpp-SSLPP、hessianorb-CURL。 この事実は、もちろん、クロスプラットフォーム開発を複雑にします。 明らかに、これらのライブラリはネットワークデータ転送に必要ですが、Qtにはネットワークを操作するための独自のレイヤーがあり、それを使用したいと思いました。 さらに、両方の実装はサーバーと対話するときに実行フローをブロックしますが、Qtネットワークレイヤーは非同期です。
したがって 、私の目標は、
QNetworkAccessManagerの使用とその非同期の性質に基づいてプロトコル実装を作成することでした。
QHessian
QHessian(以降qh)は、サードパーティのライブラリを使用しないヘシアンプロトコル実装です。 言い換えれば、Qtライブラリスタックはこの実装で動作するのに十分です。
前に述べたように、ヘシアンの目標は、シリアル化、ネットワークを介した送信、およびデータの逆シリアル化です。 QNetworkAccessManagerは、ネットワーク経由の送信を処理します。 データは、
Hessian 2.0 Serialization Protocolに従ってシリアライズおよびデシリアライズされます。 この文書は、プロトコルの実装が以下を行えるべきであると述べています。
- 生のバイナリデータの読み取り/書き込み
- 読み取り/書き込みブール
- 64ビットミリ秒の日付の読み取り/書き込み
- 64ビットdoubleの読み取り/書き込み
- 32ビット整数の読み取り/書き込み
- 64ビット長の読み取り/書き込み
- nullの読み取り/書き込み
- UTF8エンコードされた文字列の読み取り/書き込み
- リストと配列の読み取り/書き込み
- マップおよび辞書の読み取り/書き込み
- オブジェクトの読み取り/書き込み
- 共有および循環オブジェクト参照の参照の読み取り/書き込み
- オブジェクトの読み取り/書き込み/参照マップのリスト
- クラス定義参照マップの読み取り/書き込み
- 読み取り/書き込みタイプ(クラス名)参照マップ
この要件を実装するために、qhは独自のタイプ階層を使用します。
- ヌル値の読み取り/書き込みヌル値
- ブール読み取り/書き込みブール
- 整数読み取り/書き込み32ビット数(qint32)
- 長い読み取り/書き込み64ビット数(qint64)
- ダブル読み取り/書き込み64ビットIEEE 754非整数(qreal)
- 文字列の読み取り/書き込みUTF-8文字列(QString)
- DateTime読み取り/書き込み日付(QDateTime)
- バイナリ読み取り/書き込みバイト配列(QByteArray)
- BeginCollectionは、コレクションの先頭を読み書きします
- EndCollection読み取り/書き込み終了コレクション
- BeginMap読み取り/書き込み開始マップ
- HasMoreMap読み取り専用-マップの終わりを確認します
- EndMap読み取り/書き込みマップの終わり
- BeginObjectは、オブジェクトの先頭を読み書きします
- EndObjectオブジェクトの読み取り/書き込み終了
- 参照読み取り/書き込みオブジェクト参照
qhを使用するメカニズムは、標準の入力/出力ストリームを使用するメカニズムをコピーします。 つまり 読み取りは「
>> 」演算子を使用して行われ、書き込みはそれぞれ「
<< 」演算子を使用して行われます。 読み取りは、特別なQHessianReturnParserオブジェクト(サーバー応答パーサー)から行われます。 また、レコードはQHessianMethodCallオブジェクトにあります(サーバーに送信するためのデータを準備します)。
たとえば、サンプルサーバーメソッド(整数、文字列、日付)を呼び出すには、コードを実行する必要があります。
{
using namespace QHessian:: in ;
QHessian::QHessianMethodCall call( "sample" );
call << Long(55) << String (“”) << DateTime (QDateTime::currentDateTime ());
call.invoke(networkManager,
QUrl(“http: //serviceUrl”), //
myQObject, // QObject,
SLOT(reply()), //
SLOT(error( int , const QString&))); //
}
* This source code was highlighted with Source Code Highlighter .
このコードは、パラメーター55、Vasily、現在の時刻でサンプルメソッドを呼び出します。 サーバーの応答は、myQObjectオブジェクトのreply()スロットで処理されます。エラーが発生した場合は、myQObjectオブジェクトのエラースロットで処理されます。 通話がブロックされていないこと、つまり、 マルチスレッドで処理する必要はありません。
サービスがオブジェクトcom.googlecode.AnswerObjectで渡されたデータを単に返すと想像してください。 応答スロットでの応答の処理は次のようになります。
void MyQObject::reply() {
using namespace QHessian:: out ;
qint32 long ;
QString string ;
QDateTime dateTime;
QHessian::QHessianReturnParser& parser =
*(QHessian::QHessianReturnParser*) QObject::sender();
parser >> BeginObject(“com.googlecode.AnswerObject”)
>> Long( long )
>> String ( String )
>> DateTime (dateTime)
>> EndObject();
parser.deleteLater();
}
* This source code was highlighted with Source Code Highlighter .
実行後、longは値55、文字列-Vasily、dateTime-送信時刻を取ります。
QHessianはコレクション(配列、リスト、連想配列など)をサポートしています。
これは、サーバーの応答からPolygonクラスを読み取ることができます。
{
using namespace QHessian:: out ;
QHessian::QHessianReturnParser& parser =
*(QHessian::QHessianReturnParser*) QObject::sender();
qint32 pointCount;
parser >> BeginObject( "Polygon" );
parser >> BeginCollection(“points”, pointCount);
for ( int i=0; i<pointCount; ++i) {
qint32 x, y;
parser >> Integer(x) >> Integer(y);
}
parser >> EndCollection();
parser >> EndObject();
parser.deleteLater();
}
* This source code was highlighted with Source Code Highlighter .
このコードでは:
- BeginObject( "ポリゴン"); -Polygonクラスでオブジェクトを開きます。
- BeginCollection(「ポイント」、pointCount); -コレクションを開き、その長さをpointCountに入れます。
- forループでは、ポイントの位置を読み取ります。
- EndCollection(); -コレクションを閉じます。
- EndObject(); -オブジェクトを閉じます。
サーバーが型なしリストList <?を返すなど、オブジェクトのタイプがわからない可能性もあります。 Geometry>を拡張します。 この場合、特別なpeek()メソッドを使用する必要があります。このメソッドは、ストリームからの読み取りの目的の操作を実行できるかどうかを決定します(例はプロジェクトWebサイトにあります)。
結論
プロトコルの完全に適切な実装が判明し、実際のアプリケーションで使用できると思います。
プラスは次のとおりです。
- Qtに完全に基づいている、つまり サードパーティのライブラリを接続する必要はありません。
- 非同期の性質。
- クライアントアプリケーションでサーバーオブジェクトモデル全体を再構築する必要はありません(つまり、必要なもののみを読み取ります)。
- テスト済み-Caucho Technology、Inc.が提案するすべてのテストに合格 さらに、独自のテストスタック。
- 使いやすい(私には思えます)。
短所で:
- Qhにはコード生成がありません。 hessianorbでの私の経験から、コード生成は見た目ほど有用ではないと確信しました。 まず、環境は頻繁に変化するため、コード生成を常に実行する必要があります。これはhessianorbでは簡単ではありません。 第二に、不必要なコードが大量に表示されます(オブジェクトモデル全体が生成されますが、多くの場合必要ありません)。 しかし、qhは非常に低レベルであることが判明し、大きな要望があれば、コード生成メカニズムを開発できます。
- サーバー応答の構造を知る必要があります。
参照:
ヘッセ行列:
http :
//hessian.caucho.com
QHessian:
http ://code.google.com/p/qhessian
QHessian FAQ:
http ://code.google.com/p/qhessian/wiki/FAQ
QHessian QA:
http ://code.google.com/p/qhessian/wiki/QHessian_QA
ありがとう
==
プロジェクトの開発と記事の執筆に協力してくれたSergey Bizinに感謝します。
UPD:
ブログ