ArticleDirectory
- 1. Idle order
- 2. Define the IDL file
- 3. Use the IDL code generator to generate the message definition C ++ header file
- 4. Use the generated C ++ message header file
- 5. Message Processing at the logic layer
- 6. More
1. Idle order
Most game servers use asynchronous messaging. Common Message packaging formats include Google protobuff, Facebook thrift, thousands of custom binary formats, and JSON. The first three are both in binary format, which is very convenient for C ++ developers. The efficiency and package size (data redundancy) are also ideal. JSON is a string protocol, and encode and decode are costly. Parsing 500-byte JSON strings takes about 1 ms. JSON is very common in scripting languages, such as web applications and social games, because Web applications share the CPU overhead of JSON parsing through multiple processes, and these applications are not real-time. JSON is a bit self-describing than the binary protocol. It is very convenient to debug JSON messages. If a message error occurs, you can simply log the message to the file to identify the Authenticity with the naked eye (you cannot grasp the authenticity of the message, see http://www.jsoneditoronline.org/for tools. For more information, see http://json.org /). In fact, because JSON is a string, compression and transmission can also achieve an ideal compression ratio.
Our social game clients are flash, and Flash engineers like JSON very much. Several Flash games use JSON for communication with PHP. The new game supports real-time combat. c ++ is used in the background, and JSON is still used. In backend computing, to ensure real-time processing, we generally put JSON parsing in the network thread (multithreading), parse it into a specific type of struct in C ++, and then post it to the logic thread (single thread) for processing. In this way, JSON resolution can be distributed to multiple CPUs without wasting the cpu Of the main logical thread.
Currently, a struct is added for each added interface, and JSON Parsing is added to the network processing logic function.Code(Including error handling), and then the flash joint debugging protocol. There is also an annoying interface document that needs to be updated every time. If you directly give the header file that defines struct to flash, but it does not seem very elegant, there is still a formal document.
I have referenced Google protobuf and Facebook thrift and want to design the following message definition method.
2. Define the IDL file
Interface Description Language? In fact, I only have a description of the message format, and there is no interface, but IDL is easy to accept.
If you need a message to describe student data, use my definition IDL to describe its content as follows: Student. IDL
//! Defines the student message format, Version 1
Stuct student_t (version =1)
{
//! Description student requires sub-type book
StructBook_t (version =1)
{
//! The book contains two fields: ages 16-digit, content string, which can be empty. The default value is "oh nice".
Int16 pages;
string content ( default = " oh nice! ");
}< br> /// ! Define age, score, and name. They are all basic types
/// ! Defines friends as an array, and the individual type is a string, corresponding to the JSON array
/// ! Define books as a dictionary, key as a string, and item as the book structure, corresponding to the JSON object structure
int8 age;
float grade ( default = 0 );
string name;
array string friends;
dictionary string , book_t> books;
};
3. Use the IDL code generator to generate the message definition C ++ header file
Idl_generator.py student. IDL-l CPP-O msg_def.h
Generate msg_def.h
Idl_generator.py @ http://ffown.googlecode.com/svn/trunk/fflib/lib/generator/
4. Use the generated C ++ message header file
The header file is:
Struct Student_t
{
Struct Book_t
{
Int16_t pages;
String Contents;
};
Int8_t age;
Float Grade;
String Name;
Vector < String > Friends;
Map < String , Book_t> books;
};
//! Template Class. t indicates the callback object type. For each MSG type, the corresponding handle function must be defined. r indicates the socket type pointer of the request. Here the generic expression is used.
Template <typename T, typename r>
Class Msg_dispather_t
{
Typedef runtime_error msg_exception_t ;//! Request format error. An exception is thrown.
Typedef rapidjson: Document json_dom_t ;//! JSON Parsing is implemented using the rapidjson library, but the library may be replaced at a certain time, So typedef
Typedef rapidjson: Value json_value_t ;//! Rapidjson Source code : Http://code.google.com/p/rapidjson/
Typedef R socket_ptr_t ;//! Request socket
Typedef Int (Msg_dispather_t <t, r >:: * reg_func_t )( Const Json_value_t &, socket_ptr_t );//! Resolution functions corresponding to messages
Public :
Msg_dispather_t (T & msg_handler _):
M_msg_handler (msg_handler _)
{
M_reg_func [ " Student_t " ] = & Msg_dispather_t <t, r >:: student_t_dispacher ;//! All messages are registered with the parsing function during construction. The parsing function is automatically generated through IDL.
}
Int Dispath ( Const String & JSON _, socket_ptr_t SOCK _);//! Interface functions. Users only need to access dispatch at a single point. messages are automatically distributed to the specific handle function of msg_handler.
Private :
Int Student_t_dispacher ( Const Json_value_t & jval _, socket_ptr_t SOCK _)//! A specific message parsing function is automatically generated for each message. The prefix is the message name.
{
Student_t s_val;
Const Json_value_t & age = jval _[ " Age " ];
Const Json_value_t & grade = jval _[ " Grade " ];
Const Json_value_t & name = jval _[ " Name " ];
Const Json_value_t & Friends = jval _[ " Friends " ];
Const Json_value_t & books = jval _[ " Books " ];
Char Buff [ 512 ];
If ( False = Age. isnumber ())
{
Snprintf (buff, Sizeof (Buff ), " Student: age [int] field needed " );
Throw Msg_exception_t (buff );
}
S_val.age = age. getint ();
If ( False = Grade. isdouble ())
{
Snprintf (buff, Sizeof (Buff ), " Student: grade [float] field needed " );
Throw Msg_exception_t (buff );
}
S_val.grade = Grade. getdouble ();
If ( False = Name. isstring ())
{
Snprintf (buff, Sizeof (Buff ), " Student: name [String] field needed " );
Throw Msg_exception_t (buff );
}
S_val.name = Name. getstring ();
If ( False = Friends. isarray ())
{
Snprintf (buff, Sizeof (Buff ), " Student: Friends [array] field needed " );
Throw Msg_exception_t (buff );
}
For (Rapidjson: sizetype I = 0 ; I <friends. Size (); I ++)
{
Const Json_value_t & val = friends [I];
If ( False = Val. isstring ())
{
Snprintf (buff, Sizeof (Buff ), " Student: Friends field at [% u] Must string " , I );
Throw Msg_exception_t (buff );
}
S_val.friends.push_back (Val. getstring ());
}
If ( False = Books. isobject ())
{
Snprintf (buff, Sizeof (Buff ), " Student: Books [object] field needed " );
Throw Msg_exception_t (buff );
}
Rapidjson: Document: constmemberiterator it = books. memberbegin ();
For (; It! = Books. memberend (); ++ it)
{
Student_t: book_t book_val;
Const Json_value_t & name = it-> name;
If ( False = Name. isstring ())
{
Snprintf (buff, Sizeof (Buff ), " Student: Books [object] Key must [String] " );
Throw Msg_exception_t (buff );
}
Const Json_value_t & val = it-> value;
If ( False = Val. isobject ())
{
Snprintf (buff, Sizeof (Buff ), " Student: Books [object] value must [object] " );
Throw Msg_exception_t (buff );
}
Const Json_value_t & book_pages = Val [ " Pages " ];
Const Json_value_t & book_contens = Val [ " Contents " ];
If ( False = Book_pages.isnumber ())
{
Snprintf (buff, Sizeof (Buff ), " Student: Books: pages [number] field needed " );
Throw Msg_exception_t (buff );
}
Book_val.pages = book_pages.getint ();
If ( False = Book_contens.isstring ())
{
Snprintf (buff, Sizeof (Buff ), " Student: Books: book_contens [String] field needed " );
Throw Msg_exception_t (buff );
}
Book_val.contents = book_contens.getstring ();
S_val.books [name. getstring ()] = book_val;
}
M_msg_handler.handle (s_val, SOCK _);//! Because msg_handler contains the handle function for all messages, this function will be correctly distributed to the logic layer.
Return 0 ;
}
Private :
T & m_msg_handler;
Map < String , Reg_func_t> m_reg_func;
};
Template <typename T, typename r>
Int Msg_dispather_t <t, r >:: dispath ( Const String & JSON _, socket_ptr_t SOCK _)
{
Json_dom_t document; // Default template parameter uses utf8 and memorypoolallocator.
If (Document. parse < 0 > (JSON _. c_str (). hasparseerror ())
{
Throw Msg_exception_t ( " JSON format not right " );
}
If ( False = Document. isobject ()&& False = Document. Empty ())
{
Throw Msg_exception_t ( " JSON must has one Field " );
}
Const Json_value_t & val = Document. memberbegin ()-> name;
Const Char * Func_name = Val. getstring ();
Typename Map < String , Reg_func_t >:: const_iterator it = m_reg_func.find (func_name );
If (It = m_reg_func.end ())//! Check whether the parsing dispatch function exists.
{
Char Buff [ 512 ];
Snprintf (buff, Sizeof (Buff ), " MSG not supported <% S> " , Func_name );
Throw Msg_exception_t (buff );
Return - 1 ;
}
Reg_func_t func = it-> second;
(This -> * Func) (document. memberbegin ()-> value, SOCK _);
Return 0 ;
}
5. Message Processing at the logic layer
The logic layer does not need to write complicated JSON parsing and error handling. As long as no exception is triggered, messages are automatically distributed to the handle function in msg_handler. Therefore, the logic layer only needs
You can reload a handle function. The sample code is as follows:
Class Msg_handler_t
{
Public :
Typedef Int Socket_ptr_t;
Public :
Void Handle ( Const Student_t & S _, socket_ptr_t SOCK _)
{
Cout < " Msg_handler_t: handle: \ n " ;
Cout < " Age: " < Int (S _. Age) < " Grade: " <S _. Grade < " Friends: " <S _. Friends. Size () < " Name: "
<S _. Name < " Books: " <S _. Books. Size () < " \ N " ;
}
};
Int Main ( Int Argc, Char * Argv [])
{
Try
{
String TMP = " {\ "Student_t \": {\ "Age \": 123, \ "grade \": 1.2, \ "Name \": \ "Bible \", \ "Friends \": [\ "A \", \ "B \"], "
" \ "Books \": {\ "Bible \": {\ "pages \": 123, \ "Contents \": \ "Oh nice \"}}}} " ;
Msg_handler_t xxx;
Msg_dispather_t <msg_handler_t, msg_handler_t: socket_ptr_t> P (XXX );
P. dispath (TMP, 0 );
}
Catch (Exception & E)
{
Cout < " E: " <E. What () < " \ N " ;
}
}
Sample Code: http://ffown.googlecode.com/svn/trunk/fflib/lib/generator/6. More
1> currently, rapidjson is used for JSON parsing, Which is claim to be highly efficient. The biggest advantage here is that you only need to include the header file.
2> parse the IDL fileProgramWritten in Python (in preparation)
3> it is better to support namespace in IDL definition, but the first version is not supported for the time being considering complexity.
4> this article only implements JSON to struct. In fact, struct to struct is also easy to implement. The first character of the JSON string is '{'. If binary messages are used, the first character indicates the length of a message string (one byte is sufficient). For example, if "sdudent_t" is specified, the first byte should be 9 and the first byte should be 1, the length of a string of the description type can be at most 128 characters (enough ). Go to the next article and try again.