前段时间有个项目要实现一个基于live555的rtspserver,部分功能要用到环形缓冲区,网上看了一些blog,大部分是实验性质的,不太敢用,原理比较简单,所以就自己写了一个;
实现环形缓冲区的关键点:
1. 一个线程读,一个线程写
2. 读线程维护读指针,写线程维护写指针
3. 数据一致性
3.1 写线程写数据时,要先确定读指针;读线程读数据时,要先确定写指针;
这里写的可能比较拗口,其实就是 写线程写数据时,需要多次用使用读指针,比如说计算ringbuf可用空间,是否达到ringbuf末尾等等;由于读指针是在读线程里实时更新的,所以写线程写数据函数多次使用读指针时,读指针的值会不一样;解决这个问题只需要在 读/写 函数 开始处 定义一个临时变量,保存 读/写 指针的值,后续计算都使用该临时变量就OK了;
3.2 这里多说几句废话
ringbuf实现类中可能不仅会开放 ReadData/WriteData接口,还会有类似GetRingbufDataLen的函数,这类函数内部肯定要使用读写指针,这个时候就要注意,如果ReadData/WriteData函数调用这类函数,要保证它们和ReadData/WriteData 使用的读写指针的值是一致的。
说完废话贴代码:
//left 1Byte for guard
int RingBuffer::GetFreeBufferBytes(int iRidx, int iWidx)
{
if (iRidx == iWidx)
{
return m_uBufferSize-1;
}
else if (iWidx > iRidx)
{
return (iRidx - iWidx + m_uBufferSize - 1);
}
else
{
return (iRidx - iWidx - 1);
}
}
//this func can write to the addr = ridx-1 (at most)
int RingBuffer::Write(const unsigned char* pBuf, unsigned writeLen)
{
//m_pBuffer may alloc memory failed
if (!m_pBuffer)
{
return -1;
}
int iRidx = m_iRIdx;
int iWidx = m_iWidx;
if (!pBuf || 0 == writeLen || GetFreeBufferBytes(iRidx, iWidx) < writeLen)
{
return -1;
}
int len1 = 0;
if (m_iWidx < iRidx)
{
memcpy(&m_pBuffer[m_iWidx], pBuf, writeLen);
m_iWidx += writeLen;
}
else
{
len1 = m_uBufferSize - m_iWidx;
if (writeLen <= len1)
{
memcpy(&m_pBuffer[m_iWidx], pBuf, writeLen);
m_iWidx += writeLen;
}
else
{
memcpy(&m_pBuffer[m_iWidx], pBuf, len1);
memcpy(m_pBuffer, pBuf + len1, writeLen - len1);
m_iWidx = writeLen - len1;
}
}
return writeLen;
}
//
int RingBuffer::Read(unsigned char* pBuf, unsigned readLen)
{
//m_pBuffer may alloc memory failed
if (!m_pBuffer)
{
return -1;
}
if (!pBuf)
{
return -1;
}
int iWidx = m_iWidx;
int iRidx = m_iRIdx;
int bufferDataLen = m_uBufferSize - GetFreeBufferBytes(iRidx, iWidx) - 1;
if (bufferDataLen <= readLen)
{
//can not use readall here, because GetFreeBufferBytes func and readall func may use the different
//ridx and widx
return ReadToWidx(pBuf, iWidx);
}
else
{
if (m_iRIdx < iWidx)
{
memcpy(pBuf, &m_pBuffer[m_iRIdx], readLen);
m_iRIdx += readLen;
}
else
{
int len1 = m_uBufferSize - m_iRIdx;
if (len1 >= readLen)
{
memcpy(pBuf, &m_pBuffer[m_iRIdx], readLen);
m_iRIdx += readLen;
}
else
{
memcpy(pBuf, &m_pBuffer[m_iRIdx], len1);
memcpy(pBuf + len1, m_pBuffer, readLen - len1);
m_iRIdx = readLen - len1;
}
} //end m_iRIdx >= m_iWidx
return readLen;
}//end bufferDataLen > readLen
}
// read to widx
int RingBuffer::ReadToWidx(unsigned char* pBuf, int iWidx)
{
//m_pBuffer may alloc memory failed
if (!m_pBuffer)
{
return -1;
}
if (!pBuf || m_iWidx == m_iRIdx)
{
return -1;
}
int curWidx = m_iWidx;
if (m_iRIdx < curWidx)
{
if (iWidx < m_iRIdx || iWidx > curWidx)
{
return -1;
}
}
else
{
if (iWidx > curWidx && iWidx < m_iRIdx)
{
return -1;
}
}
//must use temp varible here
//int iWidx = m_iWidx;
int readLen = 0;
if (m_iRIdx > iWidx)
{
memcpy(pBuf, &m_pBuffer[m_iRIdx], m_uBufferSize - m_iRIdx);
memcpy(pBuf + m_uBufferSize - m_iRIdx, m_pBuffer, iWidx);
readLen = m_uBufferSize - m_iRIdx + iWidx;
}
else
{
memcpy(pBuf, &m_pBuffer[m_iRIdx], iWidx - m_iRIdx);
readLen = iWidx - m_iRIdx;
}
//###can not set m_iRIdx = m_iWidx!!!!!
m_iRIdx = iWidx;
return readLen;
}
int RingBuffer::ReadAll(unsigned char* pBuf)
{
//m_pBuffer may alloc memory failed
if (!m_pBuffer)
{
return -1;
}
if (!pBuf || m_iWidx == m_iRIdx)
{
return -1;
}
return ReadToWidx(pBuf, m_iWidx);
}
关键函数就这些了,有些地方写的比较繁琐,因为已经测试通过了,当做工具类,暂时就不改了...