1 文档解读

如果本lab中测试点99及之后有问题,考虑是环境问题!!

1.1 Receiving segments

  • 如果RST标志位被设置,把 inbound and outbound streams都设置为error状态,并且永久地kill这个连接。(设置isActive)
  • 解析后发送segment给TCPReceiver
  • 如果ACK 标志位被设置,那么给TCPSender ackno 和 window_size。(没有ACK 那么window_size都不需要传吗?因为第二次握手只有一个SYN标记?)
  • 如果传入的段占用了任何序列号,TCPConnection确保至少发送一个段作为回应,以反映ackno和窗口大小的更新。(占用了任何序列号表示什么?什么情况下是没有占用)

先测试一下 :传输的段可以使得 ack 增加。

1.2 Sending segments

  • 无论何时TCPSender 发送了一个segment,已经设置这个segment的信息:seqno、SYN、payload、FIN。
  • 在发送之前,TCPConnection需要去询问TCPReceiver ackno和window_size的值。如果是一个确认号ackno,设置ACK标志位。(连接的时候没有ACK)
  • 尽可能地发送segment,即在sender._segment_out中有信息的时候就写入 connection.__segment_out.

回顾sender中写入_segment_out的情况:

  • 向stream中写入完毕后
  • ackReceived后
  • close后
  • 测试类初始化时
  • tick超时

1.3 Time passes

TCPConnection有一个tick方法会被OS周期性调用。当tick被调用时:

  • 通知TCPSender,多长时间过去了。
  • 如果连续重传次数大于最大次数,关闭连接,并发送一个RST标志位被设置的空segment给对方。
  • 如果有必要就结束连接(见1.5)

1.4 FAQ

  • tick()调用和TCPSender类似。
  • 收到设置RST的segment:
    • inbound and outbound ByteStreams的状态设置为error
    • 之后调用TCPConnection::active()都返回false
  • 什么时候发送RST:
    • 重传次数超限
    • 当active()返回true,TCPConnection的析构函数被调用时
  • 发送RST和接受RST一样,设置error状态,然后active()返回也为false。
  • RST segment的seq如何确定?
    • 发送正确的seq的空的segment
    • 或者你也可以通过调用fill_window()去填满window(如果有数据需要进行发送,来自stream 或者 SYN/FIN)
  • ACK标志位的作用?所有的segment都有吗?
    • 几乎所有的segment都是确认帧,除了第一次握手。
    • 对于outgoing segments,任何时候,你都需要设置ackno和ACK
    • 对于ingoing segments,只有当ACK被设置时,你才需要去看。
  • 如果TCPReceiver想要一个更大的window_size,如何去设置?
    • 尽可能大的值。std::numeric_limits class 会有帮助。
  • 什么时候已经TCP连接结束?什么时候 active()会return false?
    • 见下一节

1.5 The end of a TCP connection

当TCPConnection 决定TCP连接结束之后,会释放一个端口号,停止发送确认帧,active()返回false。

有两种方法使得连接结束。

unclean shutdown:接收或者发送RST。这种情况下 ByteStream的状态是error,active()返回false。

clean shutdown:是一种没有error但active() = false的情况。这表示实现了完整可靠地发送以及接收。由于”Two Generals Problem“,我们不可能实现一个 clean shutdown,但是TCP有一个精彩的close方法。从某一方TCPConnection的视角来看,实现clean shutdown 有四个必备条件:

  • inbound stream完全排列完,并达到ended。
  • outbound stream的数据完全发送出去。
  • outbound stream的数据完全被对方所确认。
  • local peer 需要认为remote peer已经完成了第三个要求。有两种方式可以使得TCPConnection知道对方已经完全确认。

方式一:流传送结束后等待

因为不会传送可靠的对确认帧的确认帧,所以local peer不能明确知道remote peer是否完成了全部流的接收。

但是local peer可以通过等待一段时间,来观察remote peer是否会重新发送任何信息来判断对方是否完全接收了。lab中采用的等待时间是10个initial retransmission。(一般应用中应该是2个MSL,报文在网络中传输的最大时间)

方式二:被动关闭

对方是先结束stream的情况。

1.5.1 The end of a TCP connection (practical summary)

在实践的角度中,用一个linger_after_streams_finish 变量来处理。起始值为true。如果 inbound stream 在 outbound stream EOF之前end,这个变量设置为false。

(方式二)在任何时间点,满足1,3条件,且 linger_after_streams_finish = false,这个连接就结束(active() = false)。否则就需要等待(方式一)。

2 Debug

不能用get去拿stl成员

2.1 空确认帧是不需要回复的。

通过seg.length_in_sequence_space() 去判断是否是。

2.2 大于index + window_size的data丢弃(还未处理一半在里面的)

2.3 未完成握手,不进行回复

可以回复确认帧。

2.4 在SYN发送给对方后,对方有可能只回复ACK,不回复SYN

3 Code

改动比较多,就直接贴代码了。

// tcp_connection.hh
class TCPConnection {private:TCPConfig _cfg;TCPReceiver _receiver{_cfg.recv_capacity};TCPSender _sender{_cfg.send_capacity, _cfg.rt_timeout, _cfg.fixed_isn};//! outbound queue of segments that the TCPConnection wants sentstd::queue<TCPSegment> _segments_out{};//! Should the TCPConnection stay active (and keep ACKing)//! for 10 * _cfg.rt_timeout milliseconds after both streams have ended,//! in case the remote TCPConnection doesn't know we've received its whole stream?bool _linger_after_streams_finish{true};bool _isActive{true};size_t _time_since_last_segment_received{0};public://! \name "Input" interface for the writer//!@{//! \brief Initiate a connection by sending a SYN segmentvoid connect();//! \brief Write data to the outbound byte stream, and send it over TCP if possible//! \returns the number of bytes from `data` that were actually written.size_t write(const std::string &data);//! \returns the number of `bytes` that can be written right now.size_t remaining_outbound_capacity() const;//! \brief Shut down the outbound byte stream (still allows reading incoming data)void end_input_stream();//!@}//! \name "Output" interface for the reader//!@{//! \brief The inbound byte stream received from the peerByteStream &inbound_stream() { return _receiver.stream_out(); }ByteStream &outbound_stream() { return _sender.stream_in(); }const ByteStream &outbound_stream() const { return _sender.stream_in(); }//!@}//! \name Accessors used for testing//!@{//! \brief number of bytes sent and not yet acknowledged, counting SYN/FIN each as one bytesize_t bytes_in_flight() const;//! \brief number of bytes not yet reassembledsize_t unassembled_bytes() const;//! \brief Number of milliseconds since the last segment was receivedsize_t time_since_last_segment_received() const;//!< \brief summarize the state of the sender, receiver, and the connectionTCPState state() const { return {_sender, _receiver, active(), _linger_after_streams_finish}; };//!@}//! \name Methods for the owner or operating system to call//!@{//! Called when a new segment has been received from the networkvoid segment_received(const TCPSegment &seg);//! Called periodically when time elapsesvoid tick(const size_t ms_since_last_tick);//! \brief TCPSegments that the TCPConnection has enqueued for transmission.//! \note The owner or operating system will dequeue these and//! put each one into the payload of a lower-layer datagram (usually Internet datagrams (IP),//! but could also be user datagrams (UDP) or any other kind).std::queue<TCPSegment> &segments_out() { return _segments_out; }//! \brief Is the connection still alive in any way?//! \returns `true` if either stream is still running or if the TCPConnection is lingering//! after both streams have finished (e.g. to ACK retransmissions from the peer)bool active() const;//!@}//! when send or receive a RST,set the state.void comeRST(){outbound_stream().set_error();inbound_stream().set_error();_isActive = false;};//! Send the segments in _sender._segments_outvoid sendAll();//! Construct a new connection from a configurationexplicit TCPConnection(const TCPConfig &cfg) : _cfg{cfg} {}//! \name construction and destruction//! moving is allowed; copying is disallowed; default construction not possible//!@{~TCPConnection();  //!< destructor sends a RST if the connection is still openTCPConnection() = delete;TCPConnection(TCPConnection &&other) = default;TCPConnection &operator=(TCPConnection &&other) = default;TCPConnection(const TCPConnection &other) = delete;TCPConnection &operator=(const TCPConnection &other) = delete;//!@}
};
// tcp_connection.cc
size_t TCPConnection::remaining_outbound_capacity() const { return outbound_stream().remaining_capacity(); }size_t TCPConnection::bytes_in_flight() const { return _sender.bytes_in_flight(); }size_t TCPConnection::unassembled_bytes() const { return _receiver.unassembled_bytes(); }size_t TCPConnection::time_since_last_segment_received() const { return _time_since_last_segment_received; }void TCPConnection::segment_received(const TCPSegment &seg) {_time_since_last_segment_received = 0;// RST setif(seg.header().rst){comeRST();return;}// Ignore ACK,when in LISTEN.if(!seg.header().syn && seg.header().ack && !_receiver._isInit){return;}//! Test the case: the segment will increment the receiver's expt_seq_receiver.segment_received(seg);// Set the _linger_after_streams_finish.if(inbound_stream().get_end_input_flag() && !outbound_stream().eof())_linger_after_streams_finish = false;if(seg.header().ack){_sender.ack_received(seg.header().ackno,seg.header().win);}_sender.fill_window();// If no segment to be sent,send an empty one.if(seg.length_in_sequence_space() && _sender._segments_out.empty()){_sender.send_empty_segment();}// Add the ackno and window_size on the segment to be sent.sendAll();// Passive closeif(!_linger_after_streams_finish && inbound_stream().get_end_input_flag() && outbound_stream().eof() && _sender._next_seqno == _sender._ack_seqno){_isActive = false;}}bool TCPConnection::active() const { return _isActive; }size_t TCPConnection::write(const string &data) {size_t ret = outbound_stream().write(data);_sender.fill_window();sendAll();if(!_linger_after_streams_finish && inbound_stream().get_end_input_flag() && outbound_stream().eof() && _sender._next_seqno == _sender._ack_seqno){_isActive = false;}return ret;
}//! \param[in] ms_since_last_tick number of milliseconds since the last call to this method
void TCPConnection::tick(const size_t ms_since_last_tick) {_sender.tick(ms_since_last_tick);// Exceed the limit,abort the connection.if(_sender._num_consecutive_retransmissions > TCPConfig::MAX_RETX_ATTEMPTS){while(!_sender._segments_out.empty())_sender._segments_out.pop();_sender.send_empty_segment(true);sendAll();// Set the local peer state.comeRST();}// Send the TCPSegments.sendAll();_time_since_last_segment_received += ms_since_last_tick;// Clean shutdown.if(_time_since_last_segment_received >= 10 * _cfg.rt_timeout &&  inbound_stream().get_end_input_flag() && outbound_stream().eof() && _sender._next_seqno == _sender._ack_seqno){_isActive = false;}
}void TCPConnection::end_input_stream() {outbound_stream().end_input();_sender.fill_window();sendAll();if(!_linger_after_streams_finish && inbound_stream().get_end_input_flag() && outbound_stream().eof() && _sender._next_seqno == _sender._ack_seqno){_isActive = false;}
}void TCPConnection::connect() {_sender.fill_window();if(_sender._segments_out.size() != 1){return;}//! There is no need to send window_size and ack?_segments_out.push(_sender._segments_out.front());_sender._segments_out.pop();
}void TCPConnection::sendAll(){while(!_sender._segments_out.empty()){_sender._segments_out.front().header().ackno = wrap(_receiver.getExpt_seq(),_receiver.get_isn());_sender._segments_out.front().header().win = _receiver.window_size();// Set ACK flagif(_receiver.getExpt_seq() != 0)_sender._segments_out.front().header().ack = true;_segments_out.push(_sender._segments_out.front());_sender._segments_out.pop();}
}TCPConnection::~TCPConnection() {try {if (active()) {cerr << "Warning: Unclean shutdown of TCPConnection\n";// Your code here: need to send a RST segment to the peer_sender.send_empty_segment(true);// Set the local peer state.comeRST();// Send the TCPSegments.sendAll();}} catch (const exception &e) {std::cerr << "Exception destructing TCP FSM: " << e.what() << std::endl;}
}

吐了,测试点99死活过不了,服务器收不到seg,一步一步测试到TCP底层代码,还是收不到。最后认为问题不可能出现在要写的部分,然后clone了一个网上的题解,测试还是过不了,然后确定是环境问题。下载了官方提供的ova文件,进行了测试,测试成功!

Stanford CS144: Lab 4相关推荐

  1. [CS144] Lab 1: stitching substrings into a byte

    Lab 1: stitching substrings into a byte stream Lab Guide: Checkpoint 1: stitching substrings into a ...

  2. Stanford University CS144 Lab2 The TCP Receiver

    文章目录 前引 Lab2 The TCP Receiver 获取实验文档+调试方法介绍 1.Overview(总览) 2.Getting started(开始) 编译报错 获取LIBPCAP-DEV ...

  3. Stanford University CS144 Lab0 Networking Warmup

    文章目录 前引 二次编辑补引 Lab0 Networking Warmup 获取实验文档 Ubuntu20.04安装(Vmware Workstation) Ubuntu20.04安装G++8 切换G ...

  4. 《纯干货-6》Stanford University 2017年最新《Tensorflow与深度学习实战》视频课程分享

    分享一套Stanford University 在2017年1月份推出的一门Tensorflow与深度学习实战的一门课程.该课程讲解了最新版本的Tensorflow中各种概念.操作和使用方法,并且给出 ...

  5. 国内高校计算机教育存在哪些问题?

    最近我在逛知乎的时候看到一个问题: 随手写了个回答,没想到很多读者都表示说出了心声,所以也同步发到公众号. 以下是原回答 我自己是 CS 科班的,读者里也有很多各大高校计算机的同学,覆盖了上交.北邮. ...

  6. 硕士毕业半年的茫茫社招路

    作者丨胡津铭@知乎 来源丨https://zhuanlan.zhihu.com/p/377154343 编辑丨CVer 原文发在这里. https://github.com/conanhujinmin ...

  7. 从计算机视觉的小白变为大神,你需要经历这七个阶段

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 如果想要机器能够进行思考,我们需要先教会它们去看.  李飞飞--D ...

  8. lisp 线性标注自动避让_自动化数据增强:实践、理论和新方向

    选自Stanford AI Lab Blog 作者:Sharon Y. Li 机器之心编译 参与:Panda 对当今需要大量数据的机器学习模型而言,数据增强是一种具有显著价值的技术--既可用于缓解数据 ...

  9. 七步带你认识计算机视觉

    如果想要机器能够进行思考,我们需要先教会它们去看. 李飞飞--Director of Stanford AI Lab and Stanford Vision Lab 计算机视觉(Computer vi ...

最新文章

  1. 生信分析平台方案推介,助力科研
  2. logsoftmax前面为什么没用激活函数,有与没有影响不大吗,难道是这个原因,求明白的高手解答
  3. js中window.location.href,location.href,parent.location.href,top.location.href的用法
  4. kotlin学习之数据类(七)
  5. HDU - 1028——母函数入门
  6. 7 个有用的 PyTorch 技巧
  7. Highcharts双饼图使用实例
  8. windows下安装VM虚拟机和Ubuntu系统(附注册密钥)
  9. 10个技巧,帮你改进UI设计
  10. python中isalpha()、isdigit()、isalnum()、isupper()、islower()的含义、区别和细节
  11. 共享文件问题 -- 无法访问 您可能没有权限使用网络资源
  12. 批量自动打印PDF文件辅助工具BatchOutput PDF for Mac
  13. js 时间格式化转换
  14. 毕业设计-基于微信小程序的电影票网购系统
  15. 计算机毕业设计如何制作电子商务网站怎么制作购物网站计算机课程设计电子商城做什么(PHP-ASP.NET-c#-JavaWeb-SSM-SSH-J2EE-springBoot
  16. 【深度学习】 为Tesla K40c(显卡算力小于3.5)安装pytorch(要求显卡算力3.7以上)笔记
  17. P1386 座位安排
  18. css选择第一个标签,倒数第二个标签,最后一个标签总结
  19. 黑人抬棺用计算机演奏的乐谱,原神乐谱黑人抬棺怎么演奏_乐谱黑人抬棺_3DM手游...
  20. 《asp.net夜话》一书视频ASP.NET夜话视频1-19章下载(ASP.NET夜话2009年5月15日更新)

热门文章

  1. 兴趣部落总是显示连接不到服务器,腾讯qq兴趣部落怎么没有了?腾讯QQ兴趣部落即将停运是什么原因...
  2. html标题过长如何隐藏,CSS实现标题文字过长部分显示省略号的方法
  3. This version of the rendering library is more recent than your version of ADT plug-in. Please update
  4. JLU吉林大学21级软件数据结构上机实验(1)
  5. quartz配置动态任务,从数据库读取相应的类及方法,执行任务(任务添加、修改、暂停、恢复)
  6. python输入学生成绩判断学生等级_Python练习3:判断学生成绩等级
  7. 怀旧服服务器阵营比例小程序,魔兽怀旧服阵营比例曝光 主播扎堆布鲁?10年老玩家表示:只关心排队问题...
  8. java题 答案是玫瑰花,《长腿的玫瑰》阅读练习及答案(2019年湖北省十堰市中考题)...
  9. wps日期加减算天数_WPS表格计算这个月的剩余天数的公式
  10. 正则表达式中 ?=、?:、?: 的含义