CSndBuffer发送缓冲区---------将应用层的数据分成大小为MSS的块并放入发送队列
一:初始化,构造函数
// initial physical buffer of "size"
m_pBuffer = new Buffer;
m_pBuffer->m_pcData = new char [m_iSize * m_iMSS];//缓冲区总大小
m_pBuffer->m_iSize = m_iSize;
m_pBuffer->m_pNext = NULL;
// circular linked list for out bound packets
//创建 环形链表, m_iSize个块,每个块大小为m_iMSS
m_pBlock = new Block;
Block* pb = m_pBlock;
for (int i = 1; i < m_iSize; ++ i)
{
pb->m_pNext = new Block;
pb->m_iMsgNo = 0;
pb = pb->m_pNext;
}
pb->m_pNext = m_pBlock;
pb = m_pBlock;
//初始化每个块 首地址,每个块大小为m_iMSS
char* pc = m_pBuffer->m_pcData;
for (int i = 0; i < m_iSize; ++ i)
{
pb->m_pcData = pc;
pb = pb->m_pNext;
pc += m_iMSS;
}
二:添加应用层数据到发送队列
void CSndBuffer::addBuffer(const char* data, int len, int ttl, bool order)
{
//先计算需要多少个块(因每个块大小固定为m_iMSS)
int size = len / m_iMSS;
if ((len % m_iMSS) != 0)
size ++;
// dynamically increase sender buffer
while (size + m_iCount >= m_iSize)//超过了目前的,则翻倍增加
increase();
uint64_t time = CTimer::getTime();
int32_t inorder = order;
inorder <<= 29;
//将数据按最大m_iMSS大小填充每个块,并初始化报文值,每个块当做最小粒度发送单元,从UDP->IP发送出去,这样在上层分片后,
//可避免在IP层分片导致丢包率增加
Block* s = m_pLastBlock;
for (int i = 0; i < size; ++ i)
{
int pktlen = len - i * m_iMSS;
if (pktlen > m_iMSS)
pktlen = m_iMSS;
memcpy(s->m_pcData, data + i * m_iMSS, pktlen);
s->m_iLength = pktlen;
s->m_iMsgNo = m_iNextMsgNo | inorder;
if (i == 0)
s->m_iMsgNo |= 0x80000000;
if (i == size - 1)
s->m_iMsgNo |= 0x40000000;
s->m_OriginTime = time;
s->m_iTTL = ttl;
s = s->m_pNext;
}
m_pLastBlock = s;
CGuard::enterCS(m_BufLock);
m_iCount += size;
CGuard::leaveCS(m_BufLock);
m_iNextMsgNo ++;
if (m_iNextMsgNo == CMsgNo::m_iMaxMsgNo)
m_iNextMsgNo = 1;
}
三:从发送队列取一个块 用于发送
int CSndBuffer::readData(char** data, int32_t& msgno)
{
// No data to read
if (m_pCurrBlock == m_pLastBlock)
return 0;
*data = m_pCurrBlock->m_pcData;
int readlen = m_pCurrBlock->m_iLength;
msgno = m_pCurrBlock->m_iMsgNo;
m_pCurrBlock = m_pCurrBlock->m_pNext;
return readlen;
}
该块最终通过 CSndQueue::worker->CSndUList::pop(->CUDT::packData)->CChannel::sendto->sendmsg(|WSASendTo)
四.确认已发送成功的数据
void CSndBuffer::ackData(int offset)
{
CGuard bufferguard(m_BufLock);
for (int i = 0; i < offset; ++ i)
m_pFirstBlock = m_pFirstBlock->m_pNext;
m_iCount -= offset; //队列中发送块 减少 m_iCount 是发送队列的数据块个数
CTimer::triggerEvent();
}
CRcvBuffer 接收缓冲区-----------从网络上接收数据放入接收队列
一:初始化最大队列 默认65536个
CRcvBuffer::CRcvBuffer(CUnitQueue* queue, int bufsize):
m_pUnit(NULL),
m_iSize(bufsize),
m_pUnitQueue(queue),
m_iStartPos(0),
m_iLastAckPos(0),
m_iMaxPos(0),
m_iNotch(0)
{
m_pUnit = new CUnit* [m_iSize];
for (int i = 0; i < m_iSize; ++ i)
m_pUnit[i] = NULL;
}
二.将网络接收的数据放入接收队列
int CRcvBuffer::addData(CUnit* unit, int offset)
{
int pos = (m_iLastAckPos + offset) % m_iSize;
if (offset > m_iMaxPos)
m_iMaxPos = offset;
if (NULL != m_pUnit[pos])
return -1;
m_pUnit[pos] = unit;
unit->m_iFlag = 1;
++ m_pUnitQueue->m_iCount;
return 0;
}
接收线程CRcvQueue::worker->网络接收CChannel::recvfrom->数据处理CUDT::processData->放入接收队列CRcvBuffer::addData(CUnit存放了接收的数据包)
三.将接收队列拷贝数据到应用层缓存
使用接口 UDT::recv->CUDT::recv(static)->CUDT::recv->CRcvBuffer::readBuffer
int CRcvBuffer::readBuffer(char* data, int len)
{
int p = m_iStartPos;
int lastack = m_iLastAckPos;
int rs = len;
while ((p != lastack) && (rs > 0))
{
int unitsize = m_pUnit[p]->m_Packet.getLength() - m_iNotch;
if (unitsize > rs)
unitsize = rs;
memcpy(data, m_pUnit[p]->m_Packet.m_pcData + m_iNotch, unitsize);
data += unitsize;
if ((rs > unitsize) || (rs == m_pUnit[p]->m_Packet.getLength() - m_iNotch))
{
CUnit* tmp = m_pUnit[p];
m_pUnit[p] = NULL;
tmp->m_iFlag = 0;
-- m_pUnitQueue->m_iCount;
if (++ p == m_iSize)
p = 0;
m_iNotch = 0;
}
else
m_iNotch += rs;
rs -= unitsize;
}
m_iStartPos = p;
return len - rs;
}