环形数据缓冲的实现

关键在于写指针不能追上读指针,让读指针和写指针保留一个字节的距离来区分2个指针重叠的情况。

如何保证线程安全的?

其实就是某个线程在读取或者写入的时候取的某个时间点的指针来判断时候符合条件,条件只能是越来越好,而不可能是越来越差。比如判断能够写入的时候,空间肯定是越来越多的

 

 

//实际的缓冲区大小要多一个字节
struct RingBuffer
{
 //缓冲区头指针
       char* m_pBuffer;

 //缓冲区大小
 int m_iBufferSize;
      
       //读指针
       char* m_pRead;
      
       //写指针
       char* m_pWrite;
      
       //缓冲区指针头
       char* m_pHead;
      
       //缓冲区指针尾
       char* m_pTail;
};

//实际的缓冲区大小要多一个字节
void ip_reset_ringbuffer(RingBuffer* pRingBuffer)
{
 assert(NULL != pRingBuffer);

 pRingBuffer ->m_pWrite = pRingBuffer ->m_pRead;
}

RingBuffer* ip_create_ringbuffer(int bufferSize)
{
 RingBuffer* pRingBuffer = (RingBuffer*)malloc(sizeof(RingBuffer));

 if (NULL == pRingBuffer)
     {
  #ifdef _LOG
   printf("%s : %d get memory is NULL", __FILE__, __LINE__);
  #else
   WRITELOG(ErrLog, "%s : %d get memory is NULL", __FILE__, __LINE__);
  #endif
  
              return NULL;
     }
     else
     {
  pRingBuffer ->m_iBufferSize = bufferSize;
  
              pRingBuffer ->m_pBuffer = (char*)malloc(sizeof(char) * bufferSize);
 
  if (NULL == pRingBuffer ->m_pBuffer)
  {
   #ifdef _LOG
    printf("%s : %d get memory is NULL", __FILE__, __LINE__);
   #else
    WRITELOG(ErrLog, "%s : %d get memory is NULL", __FILE__, __LINE__);
   #endif
  
   free(pRingBuffer);

   return NULL;
  }

  memset( pRingBuffer ->m_pBuffer, 0, bufferSize);

  pRingBuffer ->m_pHead = pRingBuffer ->m_pBuffer;

  pRingBuffer ->m_pTail = pRingBuffer ->m_pBuffer + bufferSize;

  pRingBuffer ->m_pRead = pRingBuffer ->m_pWrite = pRingBuffer ->m_pHead;
           
              return pRingBuffer;
     }

}

//在某个时间点由写线程先判断缓冲区是否有足够的空间进行写操作
//写指针在追赶读指针的时候, 必须和读指针保持1个字节的间距,
//不能赶上读指针
bool ip_checkcanwrite_ringbuffer(RingBuffer* pRingBuffer, int checkSize)
{
      assert(NULL != pRingBuffer);
    
      //保存读指针,有可能读线程正在读取数据
      char* pSaveRead = pRingBuffer ->m_pRead;

 //剩余空间大小
      int left = 0;
    
      //写指针在读指针前面, 或者读指针赶上写指针
      if (pRingBuffer ->m_pWrite >= pSaveRead)
      {
    //判断剩余部分是否够1500字节
  left = pRingBuffer ->m_pTail - pRingBuffer ->m_pWrite;

  if (left >= checkSize)
  {
   return true;
  }
  //需要再加上从缓冲头部到读指针的距离, 这个时候要算加起来的长度
  //且需要多算1个字节, 防止写指针在追赶读指针的时候重叠
  //由于读指针在写指针后面,必然不会操作写指针, 这里可以实时取读指针
  //这个时候可能读线程又读了数据
  else
  {
   left = left + pRingBuffer ->m_pRead - pRingBuffer ->m_pHead;

   if (left >= checkSize + 1)
   {
    return true;
   }
   else
   {
    return false;
   }
  }
   
      }
      //写指针跑的较快
      //这里不能取实时的读指针, 因为可能读线程导致读指针又跑到了写指针的后面
      else
      {
         left = pSaveRead - pRingBuffer ->m_pWrite;

  if (left >= checkSize + 1)
  {
   return true;
  }
  else
  {
   return false;
  }
      }  
}

//该函数必须先进行剩余空间检查后才能进行写操作
//check函数已经保证了必然有足够的1500字节
void ip_pushdata_ringbuffer(RingBuffer* pRingBuffer, char* pData, int length, int maxfree)
{
      assert(NULL != pRingBuffer);
      assert(NULL != pData);
      assert(length <= maxfree);
    
      //保存读指针,有可能读线程正在读取数据
      char* pSaveRead = pRingBuffer ->m_pRead;
   
      //写指针大于读指针
      if (pRingBuffer ->m_pWrite >= pSaveRead)
      {
         int part = pRingBuffer ->m_pTail - pRingBuffer ->m_pWrite;

  //数组末尾的空间就足够了
  if (part >= length)
  {
   //拷贝数据到末尾
   memcpy(pRingBuffer ->m_pWrite, pData, length);

   if (part > length)
   { 
    //修改写指针指向
    pRingBuffer ->m_pWrite = pRingBuffer ->m_pWrite + length;
   }
   else
   {
    //写指针指向数组头
    pRingBuffer ->m_pWrite = pRingBuffer ->m_pHead;
   }

  }
  //末尾的空间不够,还要拷贝到数组头
  else
  {
   //先拷贝到数组尾
   memcpy(pRingBuffer ->m_pWrite, pData, part);

   //移动源数据指针
   pData = pData + part;

   length = length - part;

   //然后拷贝到数组头
   memcpy(pRingBuffer ->m_pHead, pData, length);

   //修改写指针指向
   pRingBuffer ->m_pWrite = pRingBuffer ->m_pHead + length;
  }
 }
      //写指针小于读指针
      else
      {
      
        int left = pSaveRead - pRingBuffer ->m_pWrite;
  
  assert(left >= maxfree + 1);

  memcpy(pRingBuffer ->m_pWrite, pData, length);

  pRingBuffer ->m_pWrite = pRingBuffer ->m_pWrite + length;
      }  
}

//在缓冲区上试图找到一个完整的IP包然后发送
static void sendippacket(TunWriter* pTunWriter, RingBuffer* pRingBuffer, int tunDeviceFd)
{
 assert(NULL != pTunWriter);
 assert(NULL != pRingBuffer);

 again:

 char pSendBuffer[SEND_BUFFER];

 char *pBegin = pSendBuffer;

 memset(pSendBuffer, 0, SEND_BUFFER);

 int iPacketLength;

 int iTotalLength;

 //先保存写指针
 char* pWriteSave = pRingBuffer ->m_pWrite;

 //写指针在读指针前面
 if (pWriteSave >= pRingBuffer ->m_pRead)
 {
  //从当前读指针判断该IP包的长度
  int left = pWriteSave -pRingBuffer ->m_pRead;

  //不足一个IP包的首部
  if (left < 20)
  {
   return;
  }
  //够一个IP包的首部
  else if (left >= 20)
  {
   //取该IP包的长度
   iPacketLength = ntohs(*((unsigned short *)(pRingBuffer ->m_pRead + 2)));

   //足够一个完整的IP包
   if (left >= iPacketLength)
   {
    //将完整的IP包先拷贝出来
    memcpy(pBegin, pRingBuffer ->m_pRead, iPacketLength);

    //读指针++
    pRingBuffer ->m_pRead = pRingBuffer ->m_pRead + iPacketLength;

    //进入发送环节
    goto Send;
   }
   //不足一个完整的IP包
   else
   {
    return;
   }
  }
 }
 //读指针在写指针的前面, 写指针肯定不会赶上读指针,这里可以取实时的写指针
 else
 {
  //判断整个剩余长度
  int left = pRingBuffer ->m_pTail - pRingBuffer ->m_pRead + pRingBuffer ->m_pWrite - pRingBuffer ->m_pHead;

  //不足一个IP包的首部
  if (left < 20)
  {
   return;
  }
  //够一个IP包的首部
  else if (left >= 20)
  {
   //先将首部的数据拷出来
   //全部在缓冲末尾
   int part = pRingBuffer ->m_pTail - pRingBuffer ->m_pRead;
   
   if (part >= 20)
   {
    memcpy(pBegin, pRingBuffer ->m_pRead, 20);
   }
   //有一部分在末尾, 一部分在头部
   else
   {
    memcpy(pBegin, pRingBuffer ->m_pRead, part);

    pBegin = pBegin + part;

    part = 20 -part;

    memcpy(pBegin, pRingBuffer ->m_pHead, part);
   }

   //取该IP包的长度
   pBegin = pSendBuffer;

   //末尾剩余数据包长度
   part = pRingBuffer ->m_pTail - pRingBuffer ->m_pRead;

   iPacketLength = ntohs(*((unsigned short *)(pBegin + 2)));

   iTotalLength = iPacketLength;

   //足够一个完整的IP包
   if (left >= iPacketLength)
   {
    //判断缓冲末尾是否就足够
    if (part >= iPacketLength)
    {
     pBegin = pSendBuffer;
    
     //从头进行拷贝
     memcpy(pBegin, pRingBuffer ->m_pRead, iPacketLength);

     //修改读指针指向
     pRingBuffer ->m_pRead = pRingBuffer ->m_pRead + iPacketLength;
    }
    //不够,要再从缓冲头部拷贝
    else
    {
     memcpy(pBegin, pRingBuffer ->m_pRead, part);

     iTotalLength = iTotalLength -part;

     pBegin = pBegin + part;

     memcpy(pBegin, pRingBuffer ->m_pHead, iTotalLength);

     //修改读指针指向
     pRingBuffer ->m_pRead = pRingBuffer ->m_pHead + iTotalLength;
    }

    //进入发送环节
    goto Send;
   }
   //不足一个完整的IP包
   else
   {
    return;
   }
  }
 }

 Send:

  int iSendTun = write(tunDeviceFd, pSendBuffer, iPacketLength);

  if ((-1 == iSendTun) && (errno == EINTR))
  {
   goto Send;
  }
  //出现下面两种情况,表示Tun设备不可写入,就不需要读取操作了
  else if ((-1 == iSendTun) && (errno == EWOULDBLOCK))
  {
   return;
  }
  else if ((-1 == iSendTun) && (errno != EINTR) && (errno != EWOULDBLOCK))
  {
   #ifdef _LOG
    ip_writeLog_threadlog(pTunWriter ->m_pLog, LOG_EMERG, "sendippacket()", "write tun fail with errno %d",  errno); 
   #else
    WRITELOG(ErrLog, "TunWriter:%d write tun fail with errno %d", pTunWriter->m_iThreadID, errno);
   #endif
     
   return;
  }
  //发送成功
  else
  {
   //向Tun设备写入的必然是一个完整的包
   assert(iSendTun == iPacketLength);
   
   goto again;
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值