Revolver源码解析之——接收缓冲区与其滑动窗口

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()。获取接收缓冲区数据的大小。

猜你喜欢

转载自blog.csdn.net/weixin_40825228/article/details/80991670
今日推荐