【斯坦福CS144】Lab3

一、实验目的

完成 TCPSender 的四个接口。

二、实验内容

在该实验中,我们需要完成 TCPSender 的以下四个接口:

**fill_window:**TCPSender 从 ByteStream 中读取数据,并以 TCPSegement 的形式发送,尽可能地填充接收者的窗口。但每个TCP段的大小不得超过 TCPConfig::MAX PAYLOAD SIZE。

若接收方的 Windows size 为 0,则发送方将按照接收方 window size 为 1 的情况进行处理,持续发包。

因为虽然此时发送方发送的数据包可能会被接收方拒绝,但接收方可以在反向发送 ack 包时,将自己最新的 window size 返回给发送者。否则若双方停止了通信,那么当接收方的 window size 变大后,发送方仍然无法得知接收方可接受的字节数量。

若远程没有 ack 这个在 window size 为 0 的情况下发送的一字节数据包,那么发送者重传时不要将 RTO 乘2。这是因为将 RTO 双倍的目的是为了避免网络拥堵,但此时的数据包丢弃并不是因为网络拥堵的问题,而是远程放不下了。

**ack_received:**对接收方返回的 ackno 和 window size 进行处理。丢弃那些已经完全确认但仍然处于追踪队列的数据包。同时如果 window size 仍然存在空闲,则继续发包。

**tick:**该函数将会被调用以指示经过的时间长度。发送方可能需要重新发送一些超时且没有被确认的数据包。

**send_empty_segment:**生成并发送一个在 seq 空间中长度为 0 并正确设置 seqno 的 TCPSegment,这可让用户发送一个空的 ACK 段。

三、实验过程

在minnow目录下输入git merge origin/check3-startercode获取Lab3

用文本编辑器打开./src/tcp_sender.hh

修改代码

用文本编辑器打开./src/tcp_sender.cc

修改代码(详见代码附录)

在build目录下输入make进行编译

输入make check2对程序进行测试

测试成功,实验结束

四、实验体会

1.完成本实验需要注意以下几点:

①当 SYN 设置后,payload 应该在尽可能装的基础之上,少装入 1byte,因为这个 byte 大小被 SYN 占用。

而在 payload 尽可能装的基础上,若 FIN 装不下了,则必须在下一个包中装入 FIN 。

②FIN 包的发送必须满足三个条件:

·从来没发送过 FIN。这是为了防止发送方在发送 FIN 包并接收到 FIN ack 包之后,循环用 FIN 包填充发送窗口的情况。

·输入字节流处于 EOF

·window 减去 payload 大小后,仍然可以存放下 FIN

③当循环填充发送窗口时,若发送窗口大小足够但本地没有数据包需要发送,则必须停止发送。

若当前 Segment 是 FIN 包,则在发送完该包后,立即停止填充发送窗口。

④重传定时器追踪的是发送者距离上次接收到新 ack 包的时间,而不是每个处于发送中的包的超时时间。因此除 SYN 包以外(它会启动定时器),其他发包操作将不会重置 重传定时器,同时也无需为每个数据包配备一个定时器。

同时,只有存在新数据包被接收方确认后,才会重置定时器。

tick 函数也是类似,只有存在处于发送状态的数据包时,重传定时器才起作用。若重传定时器超时,则重传的是第一个 seqno 最小且尚未重传的数据包。

⑤当接收方的 window size 为 0 时,仍旧按照 window size 为 1 时去处理,发送一字节数据。但是,若远程没有发送 ack 包的时候,不要将 RTO 双倍,还是重置为之前的 RTO。

五、代码附录

tcp_sender.hh

#include "tcp_sender.hh"
#include "tcp_config.hh"

#include <random>
#include<algorithm>
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 )
{}

uint64_t TCPSender::sequence_numbers_in_flight() const
{
  //这里有点搞不清楚outstandingdata的定义是什么,应该是只要push了就算outstanding,我刚开始以为要send但是没有ack才算
  return count_outstand_;
  // Your code here.
  //return {};
}

uint64_t TCPSender::consecutive_retransmissions() const
{
  // Your code here.
  return {count_retrans};
}

optional<TCPSenderMessage> TCPSender::maybe_send()
{
  if(buf_ready_send_.empty()==true)return{};
  if(timer.isrunnning==false)
  {
    timer.isrunnning=true;
    timer.cur_tick_time=0;
  }

  TCPSenderMessage sendmsg= buf_ready_send_.front();
  buf_ready_send_.pop();
  buf_send_not_ack_.push(sendmsg);
  //num_not_ack_ += sendmsg.sequence_length();
  //num_ready_send_ -= sendmsg.sequence_length();
  // Your code here.
  return {sendmsg};
}

void TCPSender::push( Reader& outbound_stream )
{
  // 有一个测试样例是在发送syn之前收到了一个ack,所以已经知道了window的大小,这里的代码改了很久
  // if(syn_send_==false)//现在不知道window大小,当做window的大小是1,只发送一个syn(错了)
  // {
  //   syn_send_=true;
  //   TCPSenderMessage syn_msg;
  //   syn_msg.SYN = true;
  //   syn_msg.FIN = false;
  //   syn_msg.seqno = isn_;
  //   buf_ready_send_.push(syn_msg);

  //   count_outstand_++;
  //   //num_ready_send_++;
  //   next_ab_seqno_++;
  //   return;
  // }
  if(fin_send_==true)return;
  // if(buf_send_not_ack_.empty()==false)
  // {
  //   if(syn_send_==true&&buf_send_not_ack_.front().SYN==true)return;
  //   //if(syn_send_==true)return;
  // }
  //syn发送但是还没确认
  //9.15发现没有这个判断也可以通过测试

  uint temp_window_size = windowsize_==0? 1:windowsize_;
  if(temp_window_size>0)
  {
    while(count_outstand_ < temp_window_size)
    {
      TCPSenderMessage msg;
      if(syn_send_==false)
      {
        syn_send_=true;
        msg.SYN=true;
        count_outstand_++;
      }
      msg.seqno = Wrap32::wrap(next_ab_seqno_, isn_);
      uint64_t msglenth = min(TCPConfig::MAX_PAYLOAD_SIZE, temp_window_size - count_outstand_);
      uint64_t streamlen = outbound_stream.bytes_buffered();
      msglenth = min(msglenth, streamlen);
      read(outbound_stream, msglenth, msg.payload);
      //num_ready_send_ += msglenth;
      count_outstand_ +=msglenth;
      

      if(!fin_send_ && outbound_stream.is_finished()==true && count_outstand_<temp_window_size)
      {
        msg.FIN=true;
        fin_send_=true;
        //num_ready_send_++;
        count_outstand_++;
        //msglenth++;
      }
      

      if(msg.sequence_length()==0)break;
      else
      {
        buf_ready_send_.push(msg);
        next_ab_seqno_+=msg.sequence_length();
        //if(msg.FIN==true)
      }

      //if(msg.FIN || outbound_stream.bytes_buffered()==0)break;
      //9.14我认为不加这个break也能行,但是最后测试的时候发现序列号会有问题
      //9.15发现是忘了给fin_send_赋值才出错的
    }
    return;
  }
  // // Your code here.
  // //(void)outbound_stream;
  // uint64_t num_stream = outbound_stream.bytes_buffered();
  // for(uint64_t i = 0 ; i<num_stream; i+=TCPConfig::MAX_PAYLOAD_SIZE)
  // {
  //   TCPSenderMessage temp;
  //   temp.seqno = Wrap32::wrap(ackno_+i);
  //   temp.payload = 
  // }
}

TCPSenderMessage TCPSender::send_empty_message() const
{
  //TCPSenderMessage msg;
  Wrap32 seqno = Wrap32::wrap(next_ab_seqno_,isn_);
  //msg.FIN=false;
  //msg.SYN=false;
  // Your code here.
  return {seqno, false, {}, false};
}

void TCPSender::receive( const TCPReceiverMessage& msg )
{
  windowsize_ = msg.window_size;
  if(msg.ackno.has_value()==false)return;
  uint64_t checkpoint = next_ab_seqno_ - (count_outstand_)/2;
  //收到的ackno应该是在next_ab_seqno_到next_ab_seqno_ - count_stand之间,所以我这里checkpoint取了一个中间值
  uint64_t rec_ab_ackno = msg.ackno.value().unwrap(isn_, checkpoint);
  //这里要求收到的ackno是可能出现的,也就是不能大于next seqno
  //ackno更新了才重启timer
  if(rec_ab_ackno>ab_ackno_ && rec_ab_ackno<=next_ab_seqno_)
  {
    ab_ackno_ = rec_ab_ackno;
    timer.cur_RTO_ms = initial_RTO_ms_;
    timer.cur_tick_time = 0;
    timer.isrunnning=false;
  }
  
  
  
  //if(buf_send_not_ack_.empty()==false)timer.isrunnning=true;
  count_retrans = 0;
  //更新ackno之后要将“发送未确认segment”的队列pop一下
  while(buf_send_not_ack_.empty()==false)
  {
    TCPSenderMessage tempmsg = buf_send_not_ack_.front();
    if(tempmsg.seqno.unwrap(isn_, checkpoint) + tempmsg.sequence_length() <= ab_ackno_)
    {
      //num_not_ack_ -= tempmsg.sequence_length();
      if(count_outstand_>=tempmsg.sequence_length())count_outstand_ -=tempmsg.sequence_length();
      buf_send_not_ack_.pop();
    }
    else break;
  }
  //还有outstandding的数据,启动timer
  if(count_outstand_!=0)
  {
    timer.isrunnning=true;
  }

  // Your code here.
  //(void)msg;
}

void TCPSender::tick( const size_t ms_since_last_tick )
{
  if(timer.isrunnning)
  timer.cur_tick_time += ms_since_last_tick;
  if(timer.cur_tick_time >= timer.cur_RTO_ms)
  {
    timer.cur_tick_time =0;
    buf_ready_send_.push(buf_send_not_ack_.front());
    if(windowsize_!=0)
    {
      timer.cur_RTO_ms *=2;
      count_retrans++;
    }
  }
  return;
  // Your code here.
  //(void)ms_since_last_tick;
}

tcp_sender.cc

#include "tcp_sender.hh"
#include "tcp_config.hh"

#include <random>
#include<algorithm>
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 )
{}

uint64_t TCPSender::sequence_numbers_in_flight() const
{
  //这里有点搞不清楚outstandingdata的定义是什么,应该是只要push了就算outstanding,我刚开始以为要send但是没有ack才算
  return count_outstand_;
  // Your code here.
  //return {};
}

uint64_t TCPSender::consecutive_retransmissions() const
{
  // Your code here.
  return {count_retrans};
}

optional<TCPSenderMessage> TCPSender::maybe_send()
{
  if(buf_ready_send_.empty()==true)return{};
  if(timer.isrunnning==false)
  {
    timer.isrunnning=true;
    timer.cur_tick_time=0;
  }

  TCPSenderMessage sendmsg= buf_ready_send_.front();
  buf_ready_send_.pop();
  buf_send_not_ack_.push(sendmsg);
  //num_not_ack_ += sendmsg.sequence_length();
  //num_ready_send_ -= sendmsg.sequence_length();
  // Your code here.
  return {sendmsg};
}

void TCPSender::push( Reader& outbound_stream )
{
  // 有一个测试样例是在发送syn之前收到了一个ack,所以已经知道了window的大小,这里的代码改了很久
  // if(syn_send_==false)//现在不知道window大小,当做window的大小是1,只发送一个syn(错了)
  // {
  //   syn_send_=true;
  //   TCPSenderMessage syn_msg;
  //   syn_msg.SYN = true;
  //   syn_msg.FIN = false;
  //   syn_msg.seqno = isn_;
  //   buf_ready_send_.push(syn_msg);

  //   count_outstand_++;
  //   //num_ready_send_++;
  //   next_ab_seqno_++;
  //   return;
  // }
  if(fin_send_==true)return;
  // if(buf_send_not_ack_.empty()==false)
  // {
  //   if(syn_send_==true&&buf_send_not_ack_.front().SYN==true)return;
  //   //if(syn_send_==true)return;
  // }
  //syn发送但是还没确认
  //9.15发现没有这个判断也可以通过测试

  uint temp_window_size = windowsize_==0? 1:windowsize_;
  if(temp_window_size>0)
  {
    while(count_outstand_ < temp_window_size)
    {
      TCPSenderMessage msg;
      if(syn_send_==false)
      {
        syn_send_=true;
        msg.SYN=true;
        count_outstand_++;
      }
      msg.seqno = Wrap32::wrap(next_ab_seqno_, isn_);
      uint64_t msglenth = min(TCPConfig::MAX_PAYLOAD_SIZE, temp_window_size - count_outstand_);
      uint64_t streamlen = outbound_stream.bytes_buffered();
      msglenth = min(msglenth, streamlen);
      read(outbound_stream, msglenth, msg.payload);
      //num_ready_send_ += msglenth;
      count_outstand_ +=msglenth;
      

      if(!fin_send_ && outbound_stream.is_finished()==true && count_outstand_<temp_window_size)
      {
        msg.FIN=true;
        fin_send_=true;
        //num_ready_send_++;
        count_outstand_++;
        //msglenth++;
      }
      

      if(msg.sequence_length()==0)break;
      else
      {
        buf_ready_send_.push(msg);
        next_ab_seqno_+=msg.sequence_length();
        //if(msg.FIN==true)
      }

      //if(msg.FIN || outbound_stream.bytes_buffered()==0)break;
      //9.14我认为不加这个break也能行,但是最后测试的时候发现序列号会有问题
      //9.15发现是忘了给fin_send_赋值才出错的
    }
    return;
  }
  // // Your code here.
  // //(void)outbound_stream;
  // uint64_t num_stream = outbound_stream.bytes_buffered();
  // for(uint64_t i = 0 ; i<num_stream; i+=TCPConfig::MAX_PAYLOAD_SIZE)
  // {
  //   TCPSenderMessage temp;
  //   temp.seqno = Wrap32::wrap(ackno_+i);
  //   temp.payload = 
  // }
}

TCPSenderMessage TCPSender::send_empty_message() const
{
  //TCPSenderMessage msg;
  Wrap32 seqno = Wrap32::wrap(next_ab_seqno_,isn_);
  //msg.FIN=false;
  //msg.SYN=false;
  // Your code here.
  return {seqno, false, {}, false};
}

void TCPSender::receive( const TCPReceiverMessage& msg )
{
  windowsize_ = msg.window_size;
  if(msg.ackno.has_value()==false)return;
  uint64_t checkpoint = next_ab_seqno_ - (count_outstand_)/2;
  //收到的ackno应该是在next_ab_seqno_到next_ab_seqno_ - count_stand之间,所以我这里checkpoint取了一个中间值
  uint64_t rec_ab_ackno = msg.ackno.value().unwrap(isn_, checkpoint);
  //这里要求收到的ackno是可能出现的,也就是不能大于next seqno
  //ackno更新了才重启timer
  if(rec_ab_ackno>ab_ackno_ && rec_ab_ackno<=next_ab_seqno_)
  {
    ab_ackno_ = rec_ab_ackno;
    timer.cur_RTO_ms = initial_RTO_ms_;
    timer.cur_tick_time = 0;
    timer.isrunnning=false;
  }
  
  
  
  //if(buf_send_not_ack_.empty()==false)timer.isrunnning=true;
  count_retrans = 0;
  //更新ackno之后要将“发送未确认segment”的队列pop一下
  while(buf_send_not_ack_.empty()==false)
  {
    TCPSenderMessage tempmsg = buf_send_not_ack_.front();
    if(tempmsg.seqno.unwrap(isn_, checkpoint) + tempmsg.sequence_length() <= ab_ackno_)
    {
      //num_not_ack_ -= tempmsg.sequence_length();
      if(count_outstand_>=tempmsg.sequence_length())count_outstand_ -=tempmsg.sequence_length();
      buf_send_not_ack_.pop();
    }
    else break;
  }
  //还有outstandding的数据,启动timer
  if(count_outstand_!=0)
  {
    timer.isrunnning=true;
  }

  // Your code here.
  //(void)msg;
}

void TCPSender::tick( const size_t ms_since_last_tick )
{
  if(timer.isrunnning)
  timer.cur_tick_time += ms_since_last_tick;
  if(timer.cur_tick_time >= timer.cur_RTO_ms)
  {
    timer.cur_tick_time =0;
    buf_ready_send_.push(buf_send_not_ack_.front());
    if(windowsize_!=0)
    {
      timer.cur_RTO_ms *=2;
      count_retrans++;
    }
  }
  return;
  // Your code here.
  //(void)ms_since_last_tick;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Robbi_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值