逐步实现TCP服务端Step03-6:封装MessageHead类

与消息相关的几个类中,MessageHead是相对比较简单的。它维护消息首部的各字段,提供对这些字段的Encode和Decode方法。

首部中应该设定哪些字段呢?消息ID是必须的,然后是消息类型。如果只讨论client与server之间的消息交互的话,只要这两个字段就够了。但实际上,server与server之间也会发生通信,有时候还需要某个机构把来自某个server的消息转发给另一个指定的server。应该再在首部中再加入一些能标识消息来源和去向的字段。

来源和去向就是说这个消息是谁发出的,要发给谁。在类中,这个“谁”应如何描述呢?“谁”包括了client和server,对于特定的后台系统,合法的client其实就一种,但server会有多种,比如:loginsvrd,dbsvrd,storesvrd等。同时,为承载更多业务,这些server又分别会启动多个实例。首部中的字段要能准确的描述是哪种server的哪个实例才行。

也就是说需要两类字段,一个描述“哪个类型”,一个描述“哪个实例”。client和server本质上都是进程,我们把它统称为实例或实体。“哪个类型”即实体类型,“哪个实例”即实体ID,这个ID可以是server在启动时从配置文件中载入的一个数值。考虑到发送方(源)和接收方(目的),一共四个字段:src_entity_,src_entity_id_,dst_entity_,dst_entity_id_ 。
在使用的时候,可能需要用负数来表示一些特殊的实体,所以各属性都使用的是有符号变量。

对于具体实现,要说的是Encode和Decode方法,分别针对属性的变量类型实现编解码函数,供这两个方法调用

不要纠结Encode/Decode函数的参数是unsigned还是signed。同类型的unsigned与signed变量,只要赋予它们相同的表面值,其在内存中的二进制形式都是一样的,只有在被解释为表面值时(比如打印的时候)才能看出不同。而Encode/Decode函数并不基于变量的表面值工作,它们做的事情,说通俗点就是倒腾内存中的数据,因此,不用太在意这个事情。

图中两个short型变量,t为unsigned,赋予表面值65535,tt为signed,其表面值由t赋予。short型长度为2字节,signed short所能接受的表面值最大为32767,tt显然放不下t中的值,肯定要溢出。不过,这些都是表面现象,内存中存放的值是一样的,只是t与tt进行了不同的解读而已。

注意,Encode/Decode会完成对int,short型数据,本地-网络字节序的互转。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
int MessageHead::Encode(unsigned char* code, int& len)
{
    if (!code) return -1;
    char* tmp_code = code;
    int tmp_len;
    len = 0;
    tmp_len = EncodeShort(&tmp_code, (unsigned short)id_); 
    len += tmp_len;
    tmp_len = EncodeShort(&tmp_code, (unsigned short)type_); 
    len += tmp_len;
    tmp_len = EncodeChar(&tmp_code, (unsigned char)src_entity_); 
    len += tmp_len;
    tmp_len = EncodeChar(&tmp_code, (unsigned char)dst_entity_); 
    len += tmp_len;
    tmp_len = EncodeShort(&tmp_code, (unsigned short)src_entity_id_); 
    len += tmp_len;
    tmp_len = EncodeShort(&tmp_code, (unsigned short)dst_entity_id_); 
    len += tmp_len;
    return 0;
}

int MessageHead::Decode(const unsigned char* code, const int len)
{
    if (!code || len <= 0) return -1;
    char* tmp_code = (char*)code;
    int left_len = len;
    int tmp_len;
    tmp_len = DecodeShort(&tmp_code, (unsigned short*)&id_); 
    left_len -= tmp_len;
    tmp_len = DecodeShort(&tmp_code, (unsigned short*)&type_); 
    left_len -= tmp_len;
    tmp_len = DecodeChar(&tmp_code, (unsigned char*)&src_entity_); 
    left_len -= tmp_len;
    tmp_len = DecodeChar(&tmp_code, (unsigned char*)&dst_entity_); 
    left_len -= tmp_len;
    tmp_len = DecodeShort(&tmp_code, (unsigned short*)&src_entity_id_); 
    left_len -= tmp_len;
    tmp_len = DecodeShort(&tmp_code, (unsigned short*)&dst_entity_id_); 
    left_len -= tmp_len;
    if (left_len < 0 ) return -1;
    return 0;
}

这个几个基本的Encode和Decode函数作为公共部分放在base.h文件中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int EncodeChar(unsigned char** code, unsigned char src)
{
    if (!code || !*code) return 0;
    **code = src;  
    (*code)++;
    return sizeof(src);
}

int DecodeChar(unsigned char** code, unsigned char* dst)
{
    if (!code || !*code || !dst) return 0;
    *dst = **code;
    (*code)++;
    return sizeof(unsigned char);
}

int EncodeShort(unsigned char** code, unsigned short src)
{
    if (!code || !*code) return 0;
    unsigned short tmp_src = htons(src); 
    memcpy(*code, &tmp_src, sizeof(tmp_src));
    *code += sizeof(tmp_src);
    return sizeof(src);
}

int DecodeShort(unsigned char** code, unsigned short* dst)
{
    if (!code || !*code || !dst) return 0;
    unsigned short tmp_dst; 
    memcpy(&tmp_dst, *code, sizeof(tmp_dst));
    *code += sizeof(tmp_dst);
    *dst = ntohs(tmp_dst);
    return sizeof(tmp_dst);
}

详细代码:

<==  index  ==>