Use the tlvstream specification to simplify interaction between modules (C/C ++)
Favorites
Problem description:
In the process of software development, the complex real world is generally abstracted, and a divide-and-conquer policy is adopted to break down a large system into subsystems and separate subsystems into modules. Inter-module communication generally uses the function call method, which may cause some problems:
1. Increasing interfaces between modules will cause close coupling between modules, which is not conducive to reuse, debugging and maintenance of modules.
2. interface function parameters are complex and prone to large structures with comprehensive functions.
3. It is not conducive to expansion. To add new functions, you must add new interface functions or modify interface parameters.
Cell Flow Definition
A cell stream is a way to process data. It abstracts the real-world data and forms one cell. Then, these cells are combined in a certain way to form a cell stream to transmit data. The implementation of the cell stream below.
Cell implementation
A cell consists of three parts: the tag, the length, and the value, that is, the TLV format. The c Definition of a cell is as follows:
Typedef struct _ element_st
{
Ele_tag;
Ele_len Len;
Ele_value value;
} Element_st;
Cell stream implementation:
The cell stream consists of the head and body ). The head part is used to record the overall description of the cell stream, such as the start module, target module, and Message Type of the cell stream. Of course, you can also expand it as needed. The body part includes the total size of the element in the cell stream, that is, the length of the body in ele_msg_body.
Typedef struct _ element_stream_st
{
Mod_id src_mod;
Mod_id des_mod;
Ele_msg_type type;
Ele_msg_body body;
} Element_stream_st;
Typedef struct _ ele_msg_body
{
Ele_msg_len Len;
Char body [max_ele_msg_body_len];
} Ele_msg_body;
Construct a cell flow
After the struct is defined, the following two functions are defined to add and delete cells to the cell body.
// Pointer to the pbody. The pointer to the cell stream is not used here to make the function more reusable. You can define the cell stream by yourself.
// Tag: The tag value of the added cell. Len: the length of the added cell. Pvale: value of the added cell.
Int addelementtostreambody (ele_msg_body * pbody, ele_tag tag, ele_len Len, void * pvalue );
// Pointer to the pbody. // Tag: gets the tag value of a cell, and the length of buf_len: pbuf. Pbuf: The target buffer zone. The value of the cell value must be written to the buffer zone.
Int getelementfromstreambody (ele_msg_body * pbody, ele_tag tag, int buf_len, void * pbuf );
The body of a cell stream is a buffer zone in which cells are sequentially arranged. The order of cells in the body does not affect the acquisition of cells. The method for adding and obtaining is relatively simple and will not be described in detail.
An instance of a cell stream
The following is an example to describe how to use a cell stream and its advantages.
Suppose there are two modules A and B. Module A is responsible for processing business logic, and Module B is responsible for handling call control. A calls an interface of B to initiate a call. The interface is as follows.
Typedef struct _ make_call_st
{
Char caller_num [max_num_len]; // caller ID
Char called_num [max_num_len]; // called Number
} Make_call_st;
Int makecall (make_call_st * pcall_info );
The requirements are changed later. In some cases, the caller's callid is carried as the remedy. Structural experience becomes:
Typedef struct _ make_call_st
{
Char caller_num [max_num_len]; // caller ID
Char called_num [max_num_len]; // called Number
Call_id caller_callid;
} Make_call_st;
In some cases, the SDP information of the caller needs to be carried, and the structure becomes:
Typedef struct _ make_call_st
{
Char caller_num [max_num_len]; // caller ID
Char called_num [max_num_len]; // called Number
Call_id caller_callid;
Sdp_info call_sdp;
} Make_call_st;
As the demand increases, this structure is becoming more and more experienced. In addition, the members of the structure need to use it in some cases and do not use it in some cases, resulting in redundant data between modules.
Of course, you can also use other methods, such as defining several more interfaces and struct. However, the number of interfaces and struct will increase explosively.
Cell streams can solve this problem. Define the number, callid, SDP, and so on as the metadata. If necessary, add it. Another advantage is that a module can only publish one interface to receive the cell stream, and then process it separately based on the type of the cell stream. In this way, a module has only one interface, and the coupling between modules will decrease.
Some improvements
The cell stream format defined above has encountered some problems in actual use. The most prominent problem is that the cell stream size is fixed. In this case, if the cell information is small, the space is wasted and the efficiency is reduced. If the cell information is large, the cell stream space is insufficient.
We can optimize the above scheme and change the cell definition:
Typedef struct _ element_st
{
Ele_tag;
Ele_len Len;
Ele_value value;
Element_st * pnext_ele; // next cell stream
} Element_st;
Change the cell stream definition:
Typedef struct _ element_stream_st
{
Mod_id src_mod;
Mod_id des_mod;
Ele_msg_type type;
Element_st * pfirst_ele; // the first cell stream
} Element_stream_st;
Change the cell stream and token to dynamically applied memory. In this way, the efficiency can be improved, and there is no size limit.
You need to add two interfaces to apply for and release the cell stream.
The only bad thing is that the dynamically applied memory needs to be released by the programmer; otherwise, the memory will be leaked. However, there is another way to add a function to apply for a cell stream, as shown below:
Element_stream_st * getelestream ()
{
Static element_stream_st * pstream = NULL;
If (null! = Pstream)
{
Freeelestream (pstream );
Pstream = NULL;
}
Pstream = allocteelestream ();
Return pstream;
}
In this case, the cell Stream Obtained Through the getelestream function is valid only within the function range. After exiting the function, it is invalid immediately.