一、实验目的
完成 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