首页 > 其他分享 >【斯坦福CS144】Lab3

【斯坦福CS144】Lab3

时间:2024-10-09 20:47:47浏览次数:11  
标签:count seqno CS144 斯坦福 timer Lab3 send msg true

一、实验目的

完成 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;
}

标签:count,seqno,CS144,斯坦福,timer,Lab3,send,msg,true
From: https://blog.csdn.net/qq_37293468/article/details/142762938

相关文章

  • 【斯坦福CS144】Lab4
    一、实验目的完成一个网络接口实现。二、实验内容完成一个网络接口实现,其大部分工作是:为每个下一跳IP地址查找(和缓存)以太网地址。而这种协议被称为地址解析协议ARP。三、实验过程在minnow目录下输入gitmergeorigin/check4-startercode获取Lab4用文本编辑器打开./......
  • 【斯坦福CS144】Lab5
    一、实验目的在现有的NetworkInterface基础上实现一个IP路由器。二、实验内容在本实验中,你将在现有的NetworkInterface基础上实现一个IP路由器,从而结束本课程。路由器有几个网络接口,可以在其中任何一个接口上接收互联网数据报。路由器的工作是根据路由表转发它得到的数据......
  • 【斯坦福CS144】Lab2
    一、实验目的实现一个TCPReceiver,用以接收传入的TCPsegment并将其转换成用户可读的数据流。二、实验内容1.接收TCPsegment;2.重新组装字节流(包括EOF);3.确定应该发回给发送者的信号,以进行数据确认和流量控制。三、实验过程输入gitmergeorigin/check2-startercode......
  • 斯坦福:合成LLM持续预训练语料
    ......
  • Lab3 Raft
    Lab3Raft1.GettingStarted代码位置:基础框架代码位置:src/raft/raft.go测试代码:src/raft/test_test.go建议测试时使用-race2.Thecode向raft/raft.go添加代码来实现Raft。实现必须支持以下接口//创建一个RaftServerrf:=Make(peers,me,persister,applyC......
  • Lab3 记录
    Part3A:leaderelection1.选举主要流程新服务器加入集群服务器在启动时状态是Follower。只要持续接收到Leader或Candidate的心跳信息,就继续保持Follower状态。开始选举每个Server都有一个随机的选举超时时间,选举超时在一个固定区间内随机选择(例如,150-300毫秒)如果Follo......
  • 斯坦福大学2014机器学习教程中文笔记目录
    http://www.ai-start.com/ml2014/第一周一、引言(Introduction)1.1欢迎1.2机器学习是什么?1.3监督学习1.4无监督学习二、单变量线性回归(LinearRegressionwithOneVariable)2.1模型表示2.2代价函数2.3代价函数的直观理解I2.4代价函数的直观理解II2.5梯......
  • 探索人工智能的未来:埃里克·施密特2024斯坦福大学分享四
    一、语言模型的经济影响关于语言模型的经济影响,我想先谈谈市场的影响。我们看到一些服务领域的变化速度比预期的要慢,比如CHEG和其他相关服务的表现。对此,您是否认为学术界应该获得人工智能补贴?还是说,他们应该与大公司合作?我个人非常努力地推动为大学争取数据中心。如果......
  • 斯坦福大学深度解析:机器学习优化算法全攻略
    在全球人工智能研究的浪潮中,斯坦福大学以其卓越的学术成就和前沿的研究成果,一直站在该领域的前沿。今天,我们将深入探讨斯坦福大学关于机器学习优化算法的精华讲义,这份讲义不仅包含了丰富的理论知识,还有图解和Pytorch实现代码,是学习和实践机器学习优化算法的宝贵资源。↓↓↓......
  • [MIT6.5840]Lab3A leader election
    文章目录Part3A:leaderelection大致框架详细过程数据结构初始化选举计时器选举过程心跳机制LeaderRPC其他函数测试结果完整代码Part3A:leaderelection实验地址https://pdos.csail.mit.edu/6.824/labs/lab-raft.html论文地址https://pdos.csail.mit.ed......