基于柔性数组和类型转换以及sendmsg的消息组装方法

实用消息组装方法

1.变长消息体使用柔性数组

    linux中消息通常使用结构体进行封装,在消息结构体中使用柔性数组不仅不占用结构体空间,还可以使消息结构体的长度随使用的需要进行扩展。

消息结构:

typedef struct tagDate

{

    s_32cmd_id;

    s_32len;

    s_8body[0];

}Date;

#defineMSG_HEAD_LEN (sizeof(Date))

一般使用方法:

Date* sdate = NULL;

……

sdate = (Date*)malloc(MSG_HEAD_LEN+ user_body_len);

sdate->cmd_id = user_cmd;

sdate->len = (MSG_HEAD_LEN+ user_body_len);

memcpy(sdate->body,user_body, user_body_len);

……

user_body可以是另外一个结构体,至此,消息封装完成。使用柔性数组与使用指针的区别主要有一下两点:

    ①当使用指针p时,p占用4字节空间,而使用柔性数组时,不占结构体空间,只是一个地址常量。

    ②当使用指针p时,需要为p开辟消息体的空间,p所指向的空间与结构体分离,释放空间时需要单独释放且需要在释放结构体指针之前完成;而使用柔性数组时,消息体的内存空间与结构体空间相邻,是连续的。  

2.兼容消息体使用类型转换和偏移

有时候我们在工程中使用的消息结构体需要新增成员,但是又要兼容以前的消息,这时候如果定义两套消息结构,适配点可能比较多,如果消息体当初设计的容量有冗余,可以考虑强制类型转化加偏移的方法适配新的消息结构。

原有的消息结构:

typedef struct tagDate

{

    s_32cmd_id;

    s_32len;

    s_8body[1024];

}Date;

新的消息结构:

typedef struct tagDate_new

{

    s_32cmd_id;

    s_32len;

    s_32 type;

    s_8body[1020];

}Date_new;

#defineMSG_HEAD_LEN        8

#defineMSG_HEAD_LEN_OFFSET 4

 

比如在消息结构中新增一个成员type,如果原来消息结构中body的容量有富裕,可以从body中借4个字节用作type

填充消息前的消息初始化:

void init_msg_head(Date* date)

{

   if (g_new_msg_type== 1)

{

       date->len = MSG_HEAD_LEN_OFFSET;

}

else

{

date->len= 0;

}  

}

填充消息成员:

Date  sdate;

……

sdate.cmd_id = user_cmd;

Date_new* tmp_date =  (Date_new*)&sdate;

tmp_date->type =user_type;

填充消息体:

put_msg_body(&sdate,user_body,user_body_len);

void put_msg_body(Date*date, void* user_body,int user_body_len)

{

memcpy(&((sdate->body)[sdate->len]), user_body, user_body_len);

}

sdate.len += MSG_HEAD_LEN;

这是一个简单的发送端消息实现,消息接收端在收到消息后,使用完type后,可以使用memmove将新消息结构的body前移覆盖掉type,后面的消息处理流程可以保持原状。

void handle_new_msg(Date_new* date)

{

   ……

   handle_msg_type(date->type);

   ……

   //还原消息结构兼容原有的处理流程

   memmove((void*)&(date->type),

(void*)(date->body),

date->len- MSG_HEAD_LEN - MSG_HEAD_LEN_OFFSET);

       date->len -= MSG_HEAD_LEN_OFFSET;

       char* tmp = (char*)date;

       memset((void*)&(tmp[date->len]),

0,

MSG_HEAD_LEN_OFFSET);

   ……

}

这个方法可以用在一些比较特殊的场合,比如给消息增加鉴权字段,新的消息中含有鉴权而老的消息结构没有,一般接收消息后,首先取出消息头中的鉴权字段校验,通过后即可用消息体将鉴权字段覆盖,之后的消息处理流程可以沿用老的流程。发送消息时通过body体偏移和新消息结构体强制转换,将老消息结构一部分body体转化为鉴权字段使用。

3.使用sendmsg方法拼接多个消息

    sendmsg一般使用为一次性将多个消息拼接发送。其中使用到了io向量iovec,一般使用方法如下:

#define IO_MAXSIZE 100

/*消息体结构支持多消息组合*/

typedef struct tagMsg

{

    s_32 count;

    struct iovec io[IO_MAXSIZE];

}MultiMsg;

typedef structtagIOSockPara

{

    u_32 sockfd;

    struct sockaddr_in remote_addr;

}IOSockPara;

/*存入分段数据*/

int Data_Push(MultiMsg *msg, void *data, int len)

{

    if ((NULL == data)||(msg->count >= IO_MAXSIZE))

    {

       printf("Push failure !\n");

       return FAILURE;

    }

    msg->io[msg->count].iov_base = data;

    msg->io[msg->count].iov_len = len;

    msg->count++;

    return SUCCESS;

}

/*基于端口的发包函数*/

u_32SendPacket(IOSockPara* ioport, MultiMsg *msg, u_32 msg_len)

{

    s_32 send_size;

    struct msghdr msgtmp;

    memset(&msgtmp,0,sizeof(msgtmp));

    msgtmp.msg_name = &(ioport->remote_addr);

    msgtmp.msg_namelen = sizeof(struct sockaddr_in);

    msgtmp.msg_iov = msg->io;

    msgtmp.msg_iovlen = msg->count;

    send_size = sendmsg(ioport->sockfd, &msgtmp, 0);

    if (send_size < 0)

    {

       perror("sendmsg err\n");

    }

    return SUCCESS;

}

    以上是工程中经常用到的方法,根据场景各有优势,做个小结,后续有心得继续补充。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值