数据块定义
在RUDP模块中,所有发送的数据被定义成RUDPRecvSegment 和 RUDPSendSegment结构,其中RUDPSendSegment是发送块定义,RUDPRecvSegment 是接收块定义。如下:
//发送数据片 typedef struct tagRUDPSendSegment { uint64_t seq_; //块序号 uint64_t push_ts_; //进入发送队列的时刻 uint64_t last_send_ts_; //最后一次发送的时刻 uint16_t send_count_; //发送的次数 uint8_t data_[MAX_SEGMENT_SIZE]; //块数据 uint16_t data_size_; //块数据长度 }RUDPSendSegment;
typedef struct tagRUDPRecvSegment { uint64_t seq_; //块序号 uint8_t data_[MAX_SEGMENT_SIZE]; //块数据 uint16_t data_size_; //块数据长度 }RUDPRecvSegment;
块的最大尺寸为MAX_SEGMENT_SIZE = 1408(不能大于MTU,一般MTU是1492)。为了加快内存分配的速度,RUDP模块中使用了对象池来保证块对象的快速申请,对象池定义:
ObjectPool<RUDPSendSegment, RUDP_SEGMENT_POOL_SIZE> SENDSEGPOOL; ObjectPool<RUDPRecvSegment, RUDP_SEGMENT_POOL_SIZE> RECVSEGPOOL; #define GAIN_SEND_SEG(seg) RUDPSendSegment* seg = SENDSEGPOOL.pop_obj(); seg->reset() #define RETURN_SEND_SEG(seg) if(seg != NULL) SENDSEGPOOL.push_obj(seg) #define GAIN_RECV_SEG(seg) RUDPRecvSegment* seg = RECVSEGPOOL.pop_obj(); seg->reset() #define RETURN_RECV_SEG(seg) if(seg != NULL) RECVSEGPOOL.push_obj(seg)
几个宏是作为块申请和释放的宏。以上就是块的定义介绍,更具体的只有去查看相关源代码了。
发送缓冲区
发送缓冲区,定义如下:
class RUDPSendBuffer { public: ... //发送数据接口 int32_t send(const uint8_t* data, int32_t data_size); //ACK处理 void on_ack(uint64_t ack_seq); //NACK处理 void on_nack(uint64_t base_seq, const LossIDArray& loss_ids); //定时器接口 void on_timer(uint64_t now_ts); //检查BUFFER是否可以写入数据 void check_buffer(); ... public: uint64_t get_buffer_seq() {return buffer_seq_;}; //设置NAGLE算法 void set_nagle(bool nagle = true){nagle_ = nagle;}; bool get_nagle() const {return nagle_;}; //设置发送缓冲区的大小 void set_buffer_size(int32_t buffer_size){buffer_size_ = buffer_size;}; int32_t get_buffer_size() const {return buffer_size_;}; ... protected: IRUDPNetChannel* net_channel_; //正在发送的数据片 SendWindowMap send_window_; //正在发送的报文的丢包集合 LossIDSet loss_set_; //等待发送的数据片 SendDataList send_data_; //发送缓冲区的大小 int32_t buffer_size_; //当前缓冲数据的大小 int32_t buffer_data_size_; //当前BUFFER中最大的SEQ uint64_t buffer_seq_; //当前WINDOW中最大的SEQ uint64_t cwnd_max_seq_; //接收端最大的SEQ uint64_t dest_max_seq_; //速度控制器 RUDPCCCObject* ccc_; //是否启动NAGLE算法 bool nagle_; }
其中send函数是数据写入函数,在这个函数里面,缓冲区对象先会对写入的数据进行报文拼接成发送块,让发送数据尽量接近MAX_SEGMENT_SIZE,如果发送的数据大于MAX_SEGMENT_SIZE,也会进行MAX_SEGMENT_SIZE为单元的分片。然后写入到对应的发送缓冲列表send_data_当中。最后尝试进行网络发送。伪代码如下:
int32_t RUDPSendBuffer::send(const uint8_t* data, int32_t data_size) { int32_t copy_pos = 0; int32_t copy_size = 0; uint8_t* pos = (uint8_t *)data; uint64_t now_timer = CBaseTimeValue::get_time_value().msec(); if(!send_data_.empty()) //拼接报文,让其接近MAX_SEGMENT_SIZE { //取出send_data_中的最后一片,如果它没有达到MAX_SEGMENT_SIZE,数据追加到MAX_SEGMENT_SIZE大小为止。 RUDPSendSegment* last_seg = send_data_.back(); if(last_seg != NULL && last_seg->data_size_ < MAX_SEGMENT_SIZE) { copy_size = MAX_SEGMENT_SIZE - last_seg->data_size_; if( copy_size > data_size) copy_size = data_size; memcpy(last_seg->data_ + last_seg->data_size_, pos, copy_size); copy_pos += copy_size; pos += copy_size; last_seg->data_size_ += copy_size; } } //剩余数据分成MAX_SEGMENT_SIZE为单位的若干分片 while(copy_pos < data_size) { GAIN_SEND_SEG(last_seg); //设置初始化的的时刻 last_seg->push_ts_ = now_timer; //记录压入时间戳 last_seg->seq_ = buffer_seq_; buffer_seq_ ++; //确定拷贝的块长度 copy_size = (data_size - copy_pos); if(copy_size > MAX_SEGMENT_SIZE) copy_size = MAX_SEGMENT_SIZE; memcpy(last_seg->data_, pos, copy_size); copy_pos += copy_size; pos += copy_size; last_seg->data_size_ = copy_size; //压入发送队列 send_data_.push_back(last_seg); } //记录缓冲区的数据长度 buffer_data_size_ += copy_pos; //尝试发送,立即发送 attempt_send(now_timer); return copy_pos; }
这里会触发attempt_send()函数。这个函数是尝试发送的核心函数。在后面的几个过程里面也会调用到这个函数。以上就是发送函数的过程。
时间: 2024-11-29 08:37:42