巧妙使用static 为数据打包并发送

只要做过项目的朋友对关键字static应该都有一些了解,但未见了解很全面的。在C语言中,关键字static有以下明显的作用:1.static变量分配到静态内存中,这一点和全局非静态变量相同。

2.在函数体,static变量只要不进行修改操作,在被调用过程中其值将保持不变。
3.在模块内,全局static变量可以被模块内所有函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量,具有私有特点。
4.在模块内,一个static函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用,也具有私有特点。

   如果我们能够很好地利用static的这些特性,书写高内聚低耦合,更具模块化的代码就不会显得象句口号;如果不能很好地理解和使用它,一切都只是空谈。下面来举个例子来说明static在实际应用可能会遇到问题。

  如果要求实现下面的接口:为每个大小约1600字节的音视频裸数据(audio/video)添加包头(packet header),然后将打过包头的整个数据包发送到网络。打过包头的数据包格式如下:

                     <------packet----->

                     | header | data |

 

  我想有人可能会这么做:

 

#define MAX_PACKET_SIZE 1600

 

typedef struct _header

{

  bool type;  /*数据包类型*/

  bool length; /*数据包类型*/

 ...

}header;

 

bool send(bool type,uint8*data,uint32 length)

{

  uint8 packet[MAX_PACKET_SIZE+sizeof(header)]={0};/*定义数组*/

  header* pheader=(header*)packet;

 

  if(NULL==data || length<1)

  {

    return FALSE;

  }

 

 /*设置包头*/

  pheader->type=type;

  pheader->length=length;

 

  pheader++;

  memcpy((uint8*)pheader,data,length);/*拷贝数据到包头之后*/

  netSend(packet);/*将包发送到网络*/

  return TRUE;

}

  

  对于上面的代码,有人可能会有这样的疑虑,发送数据包到网络是一个非常频繁的操作,所以在send函数中,频繁地为packet分配栈内存是一种低效的做法。他可能会将上的代码修改为:

bool send(bool type,uint8*data,uint32 length)

{

  uint8* packet=NULL;

  header* pheader=NULL;

 

  if(NULL==data || length<1)

  {

    return FALSE;

  }

 

  packet= (uint8*)malloc(length+sizeof(header));

 

  if(NULL==packet)

  {

    return FALSE;

  }

 

  pheader=(header*)packet;

  pheader->type=type;

  pheader->length=length;

  pheader++;

 

  memcpy((uint8*)pheader,data,length);

 

  net_send(packet);/*将包发送到网络*/

 

  free(packet);

  packet=NULL;

 

  return TRUE;

}

 

使用动态内存好象可以解决上面的问题,但它没有考虑到频繁地使用malloc-free会产生大量的内存碎片。在嵌入式系统环境中,一般内存大小有限,所以这种做法最终会导致分配失败。对于处理大流量数据问题,一种比较常用的高效方法就是在函数内部使用静态数组(全局静态数组在这个应用中不建议使用,因为全局变量会增加函数间的耦合度)。嘿嘿,听到这样的建议,估计有人会马上这么改:

bool send(bool type,uint8*data,uint32 length)

{

  static uint8 packet[MAX_PACKET_SIZE+sizeof(header)]={0};

  header* pheader=(header*)packet;

 

  if(NULL==data || length<1)

  {

    return FALSE;

  }

 

  pheader->type=type;

  pheader->length=length;

  pheader++;

 

  memcpy((uint8*)pheader,data,length);

  net_send(packet);/*将包发送到网络*/

  memset(packet,0,sizeof(packet));/*清除本次内存操作*/

 

  return TRUE;

}

 

  朋友且慢,小心地雷!

 

  你是否忘了考虑代码可重入(reentrance)问题呢?这里使用packet静态数组的确不用频繁地分配动态或栈内存,但同时引入了代码不可重入的问题。因为函数内的static变量分配在静态内存区,供所有对象共用。在多任务系统中,如果有一个以上的任务同时访问该内存,很可能会出现问题。所以我们必须要用其它手段来消除这个不可重入问题。使用信号量semaphore是一个很好解决不可重入问题的方法。在上面代码中加入信号量:

bool send(bool type,uint8*data,uint32 length)

{

  static uint8 packet[MAX_PACKET_SIZE+sizeof(header)]={0};

  header* pheader=(header*)packet;

 

  if(NULL==data || length<1)

  {

    return FALSE;

  }

 

  semTake(semaphore,WAIT_FOREVER);/*等待信号量*/

 

  pheader->type=type;

  pheader->length=length;

  pheader++;

 

  memcpy((uint8*)pheader,data,length);

  net_send(packet);/*将包发送到网络*/

  memset(packet,0,sizeof(packet));/*清除本次内存操作*/

 

  semGive(semaphore);/*释放信号量*/

 

  return TRUE;

}

 

好,现在终于解决这个问题了。在我们的视频会议系统中,曾经就在将数据发送到网络上

踩过这个地雷,修改这个bug也化了很大功夫

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值