本文最后更新于 157 天前,其中的信息可能已经有所发展或是发生改变。
如果已经通过了lab2的测试,就可以开始lab3
然后就可以开始同步实验文件了`cmake –build build –target check 3`
实验开始
这次需要实现的其实也就四个函数,其他函数只需要调用头文件中维护的变量
我当时卡在不知道tick要如何实现,不让调用系统时间,只通过传参要怎么实现,说到底还是不了解这个传参的作用,其实每一次该函数被调用结束,都会重置为0,换一句话说,就是调用到第二次调用所经历的时间间隔就是传参的值,相当于一个随机的数,你需要的就是处理这个传参。
接下来我讲讲我的实现思路,想自己思考的可以去实验了。
整体上,需要找到一个数据结构来存放我们的数据,我这里使用的是deque,因为数据的增删频繁,用queque也可以,但是实现起来复杂,没有必要。
我们创建两存储,一个是已经发送的但是未确认的,一个是准备发送的报文段。push函数用来把需要发送的报文段放入准备发送的deque类型存储,receive函数用来改变已经发送但未确认的deque类存储。时钟的开关用一个变量来维护,当发送出去后就开启时钟,如果当前发送但未确认的deque类存储中没有元素那么表明这个阶段的所有数据均发送过去,可以重置有关时钟的参数,如RTO。具体可以看我的代码和代码中的注释。
TCP_sender.cc
#include "tcp_sender.hh"
#include "tcp_config.hh"
#include <random>
#include <algorithm>
#include <iostream>
using namespace std;
/* TCPSender constructor (uses a random ISN if none given) */
TCPSender::TCPSender( uint64_t initial_RTO_ms, optional<Wrap32> fixed_isn )
: isn_( fixed_isn.value_or( Wrap32 { random_device()() } ) ), initial_RTO_ms_( initial_RTO_ms )
,RTO_ms_( initial_RTO_ms )
{}
//返回当前在传输中的序列号数量,也就是已经发送但还未收到确认的数据包数量。
uint64_t TCPSender::sequence_numbers_in_flight() const
{
return abs_seqno-por_abs_ackno;
}
//返回连续重传的次数,即同一数据包被重传的次数。
uint64_t TCPSender::consecutive_retransmissions() const
{
// Your code here.
return retransmissions;
}
//该方法尝试发送
optional<TCPSenderMessage> TCPSender::maybe_send()
{
// Your code here.
//是否没有准备
if(_ready_collections.empty()){
return nullopt;
}
//如果准备了的话
TCPSenderMessage maybe_send_Message;
maybe_send_Message=_ready_collections.front();
//缓存数据(空包不要缓存)
if(maybe_send_Message.sequence_length()==0){
return maybe_send_Message;
}
_outstanding_collections.push_back(maybe_send_Message);
_ready_collections.pop_front();
//让时钟启动
tick_start=true;
return maybe_send_Message;
}
//该方法初定为去放数据
void TCPSender::push( Reader& outbound_stream )
{ // Your code here.
//循环去处理每一个数据,保证其空间可以放入数据即可
//假设看看剩余空间是否仍然有空余(sequence_numbers_in_flight() 的最大值就是 window_size)不会产生uint64_t回滚
uint64_t available_window_space =max(static_cast<uint64_t>(1), window_size)-sequence_numbers_in_flight();
//用于测试
while (available_window_space>0)
{
// cout <<n<< "available_window_space1:"<<available_window_space<<","<<endl;
//建立一个默认的消息,范围在0到MAX_PAYLOAD_SIZE
TCPSenderMessage NewMessages;
size_t NewMessages_length=(static_cast<size_t>(available_window_space))>=TCPConfig::MAX_PAYLOAD_SIZE?
TCPConfig::MAX_PAYLOAD_SIZE:(static_cast<size_t>(available_window_space));
read(outbound_stream,NewMessages_length,NewMessages.payload);
available_window_space-=NewMessages.payload.size();
//是否需要SYN包装
if(!has_SYN){
has_SYN=true;
NewMessages.SYN=true;
NewMessages.seqno=isn_;
}else{
NewMessages.seqno = Wrap32::wrap( abs_seqno, isn_ );
}
//是否需要FIN包装
if(!has_FIN&&outbound_stream.is_finished()&&available_window_space>0){
has_FIN=true;
NewMessages.FIN=true;
}
//是否可以退出(只有当空间不足够以及到结尾以后才会产生空包)
if(!NewMessages.SYN&&!NewMessages.FIN&&NewMessages.payload.size()==0){
return;
}
//用于测试
// n++;
// cout <<n<< "psuh:"<<has_SYN<<","<<endl;
// cout <<n<< "available_window_space2:"<<available_window_space<<","<<endl;
// cout <<n<< "window_size:"<<window_size<<","<<endl;
// cout <<n<< "sequence_numbers_in_flight():"<<sequence_numbers_in_flight()<<","<<endl;
// cout <<n<< "NewMessages.sequence_length():"<<NewMessages.sequence_length()<<","<<endl;
// cout <<n<< "abs_seqno:"<<abs_seqno<<","<<endl;
//均可以放入
//更新下次的位置
_ready_collections.push_back(NewMessages);
abs_seqno += NewMessages.sequence_length();
}
// //当对方没有空间的时候,我们仍然假设他有一个,让他更新窗口
// uint16_t window_size=new_win_r-new_win_l; //获取窗口的大小
// if(window_size==0){
// _ready_collections.push_back( send_empty_message());
// return;
// }
// //保证已经建立好传输
// if(has_SYN){
// //生成尽可能多的TCPSenderMessage
// while(window_size!=0){
// TCPSenderMessage NewMessages;
// uint16_t NewMessages_length=window_size>=
// TCPConfig::MAX_PAYLOAD_SIZE?
// TCPConfig::MAX_PAYLOAD_SIZE:window_size;
// window_size-=NewMessages_length;
// //如果是最后一段,那么我们就标识FIN包
// if(outbound_stream.is_finished()){
// NewMessages.FIN=true;
// }
// //只给我看我怎么构建信息啊,我直接引用?
// NewMessages.payload{&outbound_stream.peek()};
// outbound_stream.pop(NewMessages_length);
// NewMessages.seqno=temp.wrap( outbound_stream.bytes_popped() + 1 + outbound_stream.is_finished(), zero_point );
// total__ready_collections_length=NewMessages.seqno;
// _ready_collections.push_back(NewMessages);
// };
// }else{
// //如果没有建立连接就先发出SYN包
// TCPSenderMessage Message_SYN;
// Message_SYN.SYN=true;
// Message_SYN.seqno=isn_;
// zero_point=isn.raw_value_;
// _ready_collections.push_back(Message_SYN);
// has_SYN=true;
// }
(void)outbound_stream;
}
//该方法初定为发送空数据
TCPSenderMessage TCPSender::send_empty_message() const
{
// Your code here.
//首先是定义一段空数据
return TCPSenderMessage{Wrap32::wrap(abs_seqno, isn_ ), false, Buffer(), false};
}
//该方法设置窗口大小,同时删除无用段
void TCPSender::receive( const TCPReceiverMessage& msg )
{
// Your code here.
//获得窗口左和窗口右(由于窗口大小是uint16MAX所以设置为uint16MAX)
window_size=msg.window_size;
//获得abs_ackno
if(msg.ackno.has_value()){
abs_ackno=msg.ackno.value().unwrap( isn_, por_abs_ackno );
}
//确认新的确认码的有效性
if(!msg.ackno.has_value()||abs_ackno>abs_seqno||abs_ackno<por_abs_ackno){
return;
}
// 删除已经确认的段
while (!_outstanding_collections.empty() ) {
if( _outstanding_collections.front().seqno.unwrap(isn_,abs_ackno) +_outstanding_collections.front().sequence_length() - 1 >= abs_ackno){
break;
}
_outstanding_collections.pop_front();
// RTO设置
cur_time = 0;
RTO_ms_ = initial_RTO_ms_;
retransmissions = 0;
// cout << "receive"<<RTO_ms_<<endl;
}
//设置时钟关闭
if(_outstanding_collections.empty()){
tick_start=false;
}
//更新确认
por_abs_ackno=abs_ackno;
(void)msg;
}
//该段设置为更新时间
void TCPSender::tick( const size_t ms_since_last_tick )
{
// Your code here.
//是否建立启动了时钟,是否建立了连接
if(!tick_start||!has_SYN){
// cout <<n<< "是否建立启动了时钟,是否建立了连接"<<"tick_start:"<<tick_start<<","<<"has_SYN:"<<has_SYN<<endl;
return;
}
// //测试temp
// uint64_t temp=cur_time;
cur_time+=ms_since_last_tick;
// cout <<n<< "赋值时的时间"<<"cur_time:"<<temp<<","<<"ms_since_last_tick:"<<cur_time-temp<<endl;
//是没有超时的话返回
if(cur_time<RTO_ms_){
// cout <<n<< "是没有超时的话返回"<<"cur_time:"<<cur_time<<","<<"RTO_ms_:"<<RTO_ms_<<endl;
return;
}
//RTO退避
if((window_size)>0){
RTO_ms_*=2;
// cout <<n<< "RTO退避"<<"RTO_ms_:"<<RTO_ms_<<","<<"window_size:"<<window_size<<endl;
}//从新放到我们准备发送的deque
if(!_outstanding_collections.empty()){
_ready_collections.push_front(_outstanding_collections.front());
retransmissions++;
cur_time=0;
// cout <<n<< "从新放到我们准备发送的deque"<<"cur_time:"<<cur_time<<","<<"retransmissions:"<<retransmissions<<endl;
}
// cout<<n<<"最后的所有参数一览"<<"ms_since_last_tick"<<ms_since_last_tick<<","
// <<"has_SYN:"<<has_SYN<<","
// <<"tick_start:"<<tick_start<<","
// <<"cur_time:"<<cur_time<<","
// <<"RTO_ms_:"<<RTO_ms_<<","
// <<"retransmissions:"<<retransmissions<<endl;
(void)ms_since_last_tick;
}
TCP_sender.hh
#pragma once
#include "byte_stream.hh"
#include "tcp_receiver_message.hh"
#include "tcp_sender_message.hh"
using namespace std;
class TCPSender
{
Wrap32 isn_;
uint64_t initial_RTO_ms_;
uint64_t RTO_ms_;
uint64_t window_size=1;
uint64_t cur_time=0;//这次的总毫秒数
uint64_t abs_seqno=0;//记录下次的绝对位置
uint64_t por_abs_ackno=0;//记录上次的绝对位置
uint64_t abs_ackno=0;//记录下次的绝对位置
uint8_t retransmissions=0;//记录重传次数
bool tick_start=false;//是否启动了时钟
bool has_SYN=false;//连接与否
bool has_FIN=false;//是否尝试断开
std::deque<TCPSenderMessage> _outstanding_collections={}; // 记录所有已发送未完成的段
std::deque<TCPSenderMessage> _ready_collections={}; // 记录所有准备好发送的段
public:
/* Construct TCP sender with given default Retransmission Timeout and possible ISN */
/* 使用给定的默认重传超时和可能的ISN构造TCP发送者 */
TCPSender( uint64_t initial_RTO_ms, std::optional<Wrap32> fixed_isn );
/* Push bytes from the outbound stream */
/* 从出站流中推送字节 */
void push( Reader& outbound_stream );
/* Send a TCPSenderMessage if needed (or empty optional otherwise) */
/* 如果需要发送TCPSenderMessage,则发送(否则返回空的optional) */
std::optional<TCPSenderMessage> maybe_send();
/* Generate an empty TCPSenderMessage */
/* 生成一个空的TCPSenderMessage */
TCPSenderMessage send_empty_message() const;
/* Receive an act on a TCPReceiverMessage from the peer's receiver */
/* 接收并响应对端接收者的TCPReceiverMessage */
void receive( const TCPReceiverMessage& msg );
/* Time has passed by the given # of milliseconds since the last time the tick() method was called. */
/* 自上次调用tick()方法以来,时间已经过去了给定的毫秒数 */
void tick( uint64_t ms_since_last_tick );
/* Accessors for use in testing */
/* 用于测试的访问器 */
uint64_t sequence_numbers_in_flight() const; // How many sequence numbers are outstanding?// 有多少序列号正在传输中?
uint64_t consecutive_retransmissions() const; // How many consecutive *re*transmissions have happened?// 发生了多少次连续的*重*传输?
};