
The article covers the implementation of the Hessian binary web protocol on the Qt library stack. Target platform C ++.
Hessian is a binary web protocol designed to create useful web services (to put it simply, this thing will let you know what the weather is like outside the window from any of your applications). Hessian was developed by Caucho Technology, Inc. The company also developed protocol implementations for Java, Python, and ActionScript. Third-party companies have developed implementations for almost all programming languages ​​(C ++, C #, Perl, PHP, Ruby, Objective-C, D, Erlang).
')
Imagine that we have a basket of apples on the server. And we want, at the request of the client, to give him an apple. This is where hessian will help us.
On the server, we create a service interface (server in Java):
public interface FruitService {
Apple getNextApple();
}
* This source code was highlighted with Source Code Highlighter .
We create a similar class on the client (C ++):
class FruitService {
public :
Apple getNextApple();
}
* This source code was highlighted with Source Code Highlighter .
When the getNextApple () method is called on the client, the hessian implementation makes a request to the server (to a similar server method) and returns the result to the client. Hessian undertakes to deliver apples to us through the net. In fact, it serializes the data on the server and deserializes it on the client. Turning an apple into an array of bytes, transferring it over the network and restoring an apple from an array is the hessian task. The rest is your task.
QtQt as a framework is widely known and hardly needs any introduction. I will only note that it will be a question of implementing the Hessian protocol for Qt C ++.
In general, there are several implementations (I managed to find 2) of the protocol under C ++:
Hessian cpp and hessianorb are great projects and they are quite functional. Initially, for my development I used hessianorb - it supports code generation, i.e. allows you to create a C ++ proxy for all your services with one team (after two hours of preparation).
Unfortunately, both implementations force you to create code that looks foreign in the Qt environment. They require an additional library stack: hessian cpp - SSLPP, hessianorb - CURL. This fact, of course, complicates cross-platform development. Obviously, these libraries are needed for network data transfer, but Qt has its own layer for working with the network and I wanted to use it. In addition, both implementations block the thread of execution when interacting with the server, while the Qt network layer is asynchronous.
Thus , my goal was to create a protocol implementation based on the use of
QNetworkAccessManager and its asynchronous nature.
QHessianQHessian (hereinafter referred to as qh) is an implementation of the hessian protocol that does not use third-party libraries. In other words, a stack of Qt libraries is sufficient for working with this implementation.
As I mentioned earlier, the hessian task is to serialize, network, and deserialize data. The QNetworkAccessManager takes over the full network transmission. Serialization and deserialization of data is carried out according to the
Hessian 2.0 Serialization Protocol document. The document states that the implementation of the protocol should be able to:
- read / write raw binary data
- read / write boolean
- read / write 64-bit millisecond date
- read / write 64-bit double
- read / write 32-bit int
- read / write 64-bit long
- read / write null
- read / write UTF8-encoded string
- read / write lists and arrays
- read / write maps and dictionaries
- read / write objects
- read / write ref for shared and circular object references
- read / write object / list reference map
- read / write class definition reference map
- read / write type (class name) reference map
To implement this requirement, qh uses its own type hierarchy:
- Null read / write null values
- Boolean read / write bool
- Integer read / write 32-bit number (qint32)
- Long read / write 64-bit number (qint64)
- Double read / write non-integer 64-bit IEEE 754 (qreal)
- String read / write UTF-8 string (QString)
- DateTime read / write date (QDateTime)
- Binary read / write byte array (QByteArray)
- BeginCollection read / zapis start collection
- EndCollection read / write end of collection
- BeginMap read / write start map
- HasMoreMap is read only - check end of map
- Endmap read / write end map
- BeginObject read / write object start
- EndObject read / write end of object
- Ref read / write object reference
The mechanisms for working with qh copy mechanisms for working with standard input / output streams. Those. reading is carried out using the operator “
>> ”, and writing, respectively, using the operator “
<< ”. Reading is from a special object QHessianReturnParser (server response parser). And the record is in the QHessianMethodCall object (preparing the data for sending to the server).
For example, to call the sample method of the server (Integer, String, Date) you need to run the code:
{
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 .
This code will call the sample method with parameters 55, Vasily, current time. The server response will be processed in the reply () slot of the myQObject object, or, if an error occurs, in the error slot of the myQObject object. Let me remind you that the call is not blocking, i.e. no need to steam with multithreading.
Imagine that our service simply returns the data passed to it in the com.googlecode.AnswerObject object. Then the reply response in the reply slot will be:
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 .
After execution, long will take the value 55, string - Vasily, dateTime - transmitted time.
QHessian supports collections (arrays, lists, associative arrays, etc.).
This could be the reading of the Polygon class from the server response:
{
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 .
In this code:
- BeginObject ("Polygon"); - open an object with the Polygon class;
- BeginCollection (“points”, pointCount); - open the collection and put its length in pointCount;
- In the for loop, we read the positions of the points;
- EndCollection (); - close the collection;
- EndObject (); - close the object.
It is also possible that we do not know the type of the object, for example, the server returns an untyped List <? extends geometry>. In this case, you need to use a special peek () method, which determines whether it is possible to perform the desired read operation from the stream (an example is on the project website).
findingsI think that it turned out to be a completely suitable implementation of the protocol, which can be used in real applications.
The advantages include:
- Fully based on Qt, i.e. No need to connect third-party libraries.
- Asynchronous nature.
- It does not require recreating the full server object model in the client application (i.e., read only what you need).
- Tested - passes all tests proposed by Caucho Technology, Inc. plus your own stack of tests.
- Easy to use (I think).
By cons:
- In qh there is no code generation. Experience with hessianorb convinced me that code generation is not as useful as it seems. Firstly: the environment often changes and you have to constantly perform code generation, which is not as easy to do in hessianorb as we would like; and secondly: there is a lot of unnecessary unnecessary code (the entire object model is generated, but it is often not needed). However, qh turned out to be quite low-level and, with a strong desire, it is possible to develop a mechanism for code generation.
- You need to know the structure of the server response.
References:
Hessian:
http://hessian.caucho.comQHessian:
http://code.google.com/p/qhessianQHessian FAQ:
http://code.google.com/p/qhessian/wiki/FAQQHessian QA:
http://code.google.com/p/qhessian/wiki/QHessian_QAThank.
==
Thanks to Sergey Bizin for his help in developing the project and writing the article.
UPD:
Blog