1、基本定义:
接受缓冲区的滑动窗口的定义。
typedef map<uint64_t, RUDPRecvSegment*> RecvWindowMap;
key为数据片的ID,value为指向数据片的对象指针。
用来接收连续的数据片的链表缓冲定义
typedef list<RUDPRecvSegment*> RecvDataList;
记录丢失的数据片的ID与丢失时间戳的map。
typedef map<uint64_t, uint64_t> LossIDTSMap;
2、实现。
【.h】
/*************************************************************************************
*filename: rudp_recv_buffer.h
*
*to do: 定义RUDP接收窗口Buffer
*Create on: 2013-04
*Author: zerok
*check list:
*************************************************************************************/
#ifndef __RUDP_RECV_BUFFER_H_
#define __RUDP_RECV_BUFFER_H_
#include "rudp/rudp_channel_interface.h"
#include "rudp/rudp_segment.h"
#include <map>
#include <list>
BASE_NAMESPACE_BEGIN_DECL
typedef map<uint64_t, RUDPRecvSegment*> RecvWindowMap;
typedef list<RUDPRecvSegment*> RecvDataList;
typedef map<uint64_t, uint64_t> LossIDTSMap;
class RUDPCCCObject;
class RUDPRecvBuffer
{
public:
RUDPRecvBuffer();
virtual~RUDPRecvBuffer();
void reset();
//来自网络中的数据
int32_t on_data(uint64_t seq, const uint8_t* data, int32_t data_size);
//定时
void on_timer(uint64_t now_timer, uint32_t rtc, uint32_t rtt);
//读取BUFFER中的数据
int32_t read(uint8_t* data, int32_t data_size);
uint64_t get_ack_id() const { return first_seq_;}
uint32_t get_bandwidth();
void set_net_channel(IRUDPNetChannel* channel) {net_channel_ = channel;}
void set_send_last_ack_ts(uint64_t ts) {last_ack_ts_ = ts; recv_new_packet_ = false;}
//只有启动收到对方握手时调用一次!!
void set_first_seq(uint64_t first_seq) {first_seq_ = first_seq - 1; max_seq_ = first_seq_;}
void check_buffer();
int32_t get_buffer_data_size();
protected:
void check_recv_window();
bool check_loss();
protected:
IRUDPNetChannel* net_channel_;
//接收窗口
RecvWindowMap recv_window_;
//已完成的连续数据片
RecvDataList recv_data_;
//丢包序列
LossIDTSMap loss_map_;
//当前BUFFER中最大连续数据片的SEQ
uint64_t first_seq_;
//当期BUFFER中收到的最大的数据片ID
uint64_t max_seq_;
//最后一次发送ACK的时刻
uint64_t last_ack_ts_;
//在上次发送ACK到现在,受到新的连续报文的标志
bool recv_new_packet_;
int ok_count_;
uint32_t bandwidth_;
uint64_t bandwidth_ts_;
uint32_t rtc_;
uint32_t rtt_;
};
BASE_NAMESPACE_END_DECL
#endif
/************************************************************************************/
【.cpp】
#include "rudp/rudp_recv_buffer.h"
#include "rudp/rudp_log_macro.h"
#include "revolver/base_timer_value.h"
BASE_NAMESPACE_BEGIN_DECL
#define MAX_SEQ_INTNAL 204800
RUDPRecvBuffer::RUDPRecvBuffer()
: net_channel_(NULL)
, first_seq_(0)
, max_seq_(0)
, last_ack_ts_(0)
, recv_new_packet_(false)
, ok_count_(0)
{
reset();
}
RUDPRecvBuffer::~RUDPRecvBuffer()
{
reset();
}
void RUDPRecvBuffer::reset()
{
first_seq_ = 0;
max_seq_ = 0;
last_ack_ts_ = 0;
recv_new_packet_ = false;
ok_count_ = 0;
for(RecvWindowMap::iterator it = recv_window_.begin(); it != recv_window_.end(); ++it)
{
RETURN_RECV_SEG(it->second);
}
recv_window_.clear();
for(RecvDataList::iterator it = recv_data_.begin(); it != recv_data_.end(); ++it)
{
RETURN_RECV_SEG(*it);
}
recv_data_.clear();
loss_map_.clear();
bandwidth_ = 0;
bandwidth_ts_ = CBaseTimeValue::get_time_value().msec();
rtt_ = 5;
rtc_ = 0;
}
int32_t RUDPRecvBuffer::on_data(uint64_t seq, const uint8_t* data, int32_t data_size)
{
recv_new_packet_ = true;
//删除丢包
loss_map_.erase(seq);
uint64_t ts = CBaseTimeValue::get_time_value().msec();
if(seq > first_seq_ + MAX_SEQ_INTNAL || data_size > MAX_SEGMENT_SIZE)
{
//报告异常
RUDP_RECV_DEBUG("on data exception!!");
net_channel_->on_exception();
return -1;
}
RUDPRecvSegment* seg = NULL;
if(first_seq_ + 1 == seq)
{
//将数据缓冲到队列中
GAIN_RECV_SEG(seg);
seg->seq_ = seq;
seg->data_size_ = data_size;
memcpy(seg->data_, data, data_size);
recv_data_.push_back(seg);
first_seq_ = seq;
check_recv_window();
//报告可读
net_channel_->on_read();
net_channel_->send_ack(first_seq_);
set_send_last_ack_ts(ts);
}
else if(seq > first_seq_ + 1)
{
RecvWindowMap::iterator it = recv_window_.find(seq);
if(it == recv_window_.end())
{
//将数据缓冲到队列中
GAIN_RECV_SEG(seg);
seg->seq_ = seq;
seg->data_size_ = data_size;
memcpy(seg->data_, data, data_size);
recv_window_[seq] = seg;
}
ok_count_++;
}
if (max_seq_ < seq)
{
if (max_seq_ > 0)
{
for (uint64_t i = max_seq_ + 1; i < seq; i++)
loss_map_[i] = ts - rtt_;
}
max_seq_ = seq;
}
return 0;
}
void RUDPRecvBuffer::check_buffer()
{
}
void RUDPRecvBuffer::check_recv_window()
{
if(recv_window_.empty())
return ;
//将所有连续的数据片放到数据读取队列
RecvWindowMap::iterator it = recv_window_.begin();
while (it != recv_window_.end() && it->first == first_seq_ + 1)
{
first_seq_ = it->first;
bandwidth_ts_ += it->second->data_size_;
recv_data_.push_back(it->second);
recv_window_.erase(it ++);
}
}
bool RUDPRecvBuffer::check_loss()
{
bool ret = false;
LossIDArray ids;
int count = 0;
uint64_t ts = CBaseTimeValue::get_time_value().msec();
for (LossIDTSMap::iterator it = loss_map_.begin(); it != loss_map_.end(); it++)
{
if (it->second + rtc_ + rtt_ < ts)
{
ids.push_back(static_cast<uint32_t>(it->first - first_seq_));
ret = true;
it->second = ts;
if (count++ > 80)
break;
}
else
break;
}
if(ret && net_channel_ != NULL)
net_channel_->send_nack(first_seq_, ids);
return ret;
}
void RUDPRecvBuffer::on_timer(uint64_t now_timer, uint32_t rtc, uint32_t rtt)
{
rtc_ = rtc;
rtt_ = rtt;
if (last_ack_ts_ + rtc_ < now_timer){
if (!check_loss())
net_channel_->send_ack(first_seq_);
set_send_last_ack_ts(now_timer);
}
//判断是否可以读
if(!recv_data_.empty() && net_channel_ != NULL)
net_channel_->on_read();
}
int32_t RUDPRecvBuffer::read(uint8_t* data, int32_t data_size)
{
int32_t ret = 0;
uint8_t* pos = data;
if(recv_data_.empty())
return ret;
while(!recv_data_.empty() && ret < data_size)
{
RUDPRecvSegment* seg = recv_data_.front();
int32_t remain = data_size - ret;
if(remain < seg->data_size_) //不能完全拷贝下一个数据片
{
memcpy(pos, seg->data_, remain);
//覆盖掉已经拷贝的数据
memmove(seg->data_, seg->data_ + remain, seg->data_size_ - remain);
seg->data_size_ = seg->data_size_ - remain;
pos += remain;
ret += remain;
}
else //足够拷贝下一个数据片
{
memcpy(pos, seg->data_, seg->data_size_);
pos += seg->data_size_;
ret += seg->data_size_;
RETURN_RECV_SEG(seg);
recv_data_.pop_front();
}
}
return ret;
}
uint32_t RUDPRecvBuffer::get_bandwidth()
{
uint32_t ret = 0;
uint64_t cur_ts = CBaseTimeValue::get_time_value().msec();
if(cur_ts > bandwidth_ts_)
ret = static_cast<uint32_t>(bandwidth_ * 1000 / (cur_ts - bandwidth_ts_));
else
ret = bandwidth_ * 1000;
bandwidth_ts_ = cur_ts;
bandwidth_ = 0;
return ret;
}
int32_t RUDPRecvBuffer::get_buffer_data_size()
{
int32_t ret = 0;
for(RecvDataList::iterator list_it = recv_data_.begin(); list_it != recv_data_.end(); ++ list_it)
ret += (*list_it)->data_size_;
for(RecvWindowMap::iterator map_it = recv_window_.begin(); map_it != recv_window_.end(); ++ map_it)
ret += map_it->second->data_size_;
return ret;
}
BASE_NAMESPACE_END_DECL
3、重要的成员变量与成员函数的解析。
[1].int32_t on_data(uint64_t seq,const uint8_t* data,int32_t data_size)。
接收缓冲区处理接收的数据片的成员函数。首先检查数据片的合法性。对于按照数据片ID顺序依次到达数据片,将其依次放入用来存放连续数据片的数据片缓冲区链表。对于乱序到达的数据片,将其存放到接收滑动窗口里面。对于丢失的数据片的序号,将其存放到loss_map_中。注意要标记当前最大的连续数据片的序号。
[2].void check_recv_window()。之前是乱序到达的数据片,现在可能与当前数据片的ID构成连续数据片序列。将暂存于接收窗口的、连续的数据片放到数据读取队列,并从接收窗口删除之。
[3].bool check_loss()。遍历丢包序列,将现在的时刻减去丢包时刻的时间差大于rtc的数据片的ID加入重发序列。若有丢失的包,则往发送数据片的端发送nack包。
[4].void on_timer(uint64_t now_timer,uint32_t rtc,uint32_t rtt)。若最后一次发送ACK的时刻距离现在时间已经超过了一个rtc,则检查是否需要往发送端发送nack包,若不需要发送nack包,就发送ACK包。判断是否可读,若可读,则交由应用层处理。
[5].int32_t read(uint8_t* data,int32_t data_size)。读取连续数据片缓冲链表中的数据。
[6].uint32_t get_bandwidth()。计算带宽,单位为b/s。
[7].int32_t get_buffer_data_size()。获取接收缓冲区数据的大小。