日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CS144 计算机网络实验 lab3 笔记

發(fā)布時間:2023/12/10 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CS144 计算机网络实验 lab3 笔记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

CS144 計算機網(wǎng)絡(luò)實驗 lab3 筆記

介紹

本實驗中,我們將會在之前實驗的基礎(chǔ)上,實現(xiàn)一個TCP sender ----將字節(jié)流轉(zhuǎn)換成數(shù)據(jù)報并發(fā)送.

TCP協(xié)議是一個在不可靠的協(xié)議上提供可靠的,流量控制的協(xié)議。

我們在本實驗中會實現(xiàn)一個TCP發(fā)送端,負(fù)責(zé)將發(fā)送端應(yīng)用層傳入的比特流轉(zhuǎn)換成一系列由發(fā)出的TCP報文段,在另一端,由TCP接收端將TCP報文段轉(zhuǎn)換成原始比特流(也有可能什么都不做,在從未受到syn,或者多次受到的時候),并將ack和窗口大小返回給TCP發(fā)送端

TCP接收端和發(fā)送端都負(fù)責(zé)處理一部分TCP報文段。TCP發(fā)送端寫入到報文中的每部分都會被TCP接收端解析,包括:seq,SYN,FIN,內(nèi)容

但是,TCP發(fā)送端只會一部分報文的內(nèi)容,包括:ackno和SYN

TCP發(fā)送端有以下任務(wù):

  • 跟蹤window size,也就是處理ackno和window sizes
  • 盡可能的填充 ( 自己的 ) windows,直到滿了或者ByteStream空了為止
  • 對已經(jīng)發(fā)送但沒有收到ackno的報文段 ( 未被確認(rèn)的報文段) 保持跟蹤
  • 如果超出規(guī)定的時間,還沒有收到對應(yīng)的ack,就重新發(fā)送所有已經(jīng)發(fā)送,但沒有收到ackno的報文段

TCP發(fā)送端是如何知道報文段丟失呢?

TCP發(fā)送端會發(fā)送一系列的報文段. 每個報文段都是來自ByteStream的子串加上位置索引seq,如果是第一個報文段,需要加上syn,最后一個需要加上fin

為了發(fā)送這些報文段,TCP發(fā)送端會對所有已發(fā)送的報文段保持跟蹤,直到收到響應(yīng)報文段的ack

具體實現(xiàn):

  • TCP發(fā)送端會定期的調(diào)用TCPSender::tick函數(shù),來表明時間的流逝.
  • TCP發(fā)送端負(fù)責(zé)監(jiān)管已發(fā)出報文段,如果最早的已發(fā)出但沒有收到ack的報文段,超出了規(guī)定時間,它將會被重新發(fā)送
  • 實現(xiàn)記錄時間,以及計算時間

  • 每隔幾毫秒,TCP發(fā)送端的TCPSender’s tick方法將被調(diào)用,它告自上次調(diào)用此方法以來已經(jīng)過多少毫秒,不需要自己處理時間
  • 當(dāng)TCP發(fā)送端初始化的時候,將會有一個retransmission timeout (RTO),這就是重傳時間,重傳時間是變化的,但是最初的超時時間都是一樣的,需要調(diào)用_initial retransmission timeout
  • 實現(xiàn)一個重傳定時器: 當(dāng)重傳時間到達時發(fā)出警告,超過重傳時間關(guān)閉警告,只可以依賴tick方法,不可以根據(jù)現(xiàn)實時間
  • 每次一個數(shù)據(jù)報發(fā)送的時候,如果定時器沒有開啟,就要開啟定時器
  • 當(dāng)確認(rèn)所有數(shù)據(jù)時,停止重傳計時器
  • 如果調(diào)用tick后發(fā)現(xiàn)定時器過期:
    • 重傳最早的未收到ack的包
    • 如果windows size大小不為零:
      • 你需要跟蹤連續(xù)重傳的數(shù)量,因為TCP連接需要據(jù)此判斷連接是否出現(xiàn)問題,是否需要中斷
      • 將重傳定時器時長加倍,這叫做exponential backoff指數(shù)補償,隨著重傳次數(shù)的增加,補償?shù)某潭纫矔笖?shù)增長
    • 在超過重傳時間后重置重傳定時器并開啟
  • 當(dāng)成功收到數(shù)據(jù)
    • 設(shè)置重傳時間為初始值
    • 如果發(fā)送重傳數(shù)據(jù),就需要重啟定時器
    • 將連續(xù)重傳記錄的數(shù)量置零
  • 實現(xiàn)TCP發(fā)送端 Implementing the TCP sender

    思路

  • void fill_window()
    • TCP發(fā)送端被要求盡可能的讀取ByteStream中的數(shù)據(jù),并且形成數(shù)據(jù)報 ( 未發(fā)送 )
    • 確保每次發(fā)送的數(shù)據(jù)報的量正好等于TCP接收端窗口的大小
    • 使用TCPSegment::length in sequence space()得到seq,不要忘了SYN , FIN 也占用一個序列號,所以也占用窗口空間
  • void ack_received(const WrappingInt32 ackno, const uint16 t window size)
    • 得到接收端TCP窗口的左沿和大小 , ackno理應(yīng)大于所有已發(fā)送數(shù)據(jù)報的seq
  • void tick( const size t ms since last tick )
    • 表示自從上一次發(fā)送后經(jīng)過多少毫秒了.不需要我們來調(diào)用,參數(shù)的意義是距離上次調(diào)用經(jīng)過了多少時間,這個不需要我們操心,我們需要實現(xiàn)每次調(diào)用tick()的時候函數(shù)做什么
  • void send empty segment()
    • 生成空的數(shù)據(jù)報,只發(fā)一個ack
  • 思路總結(jié)

  • void tick( const size t ms since last tick )
    • 表示自從上一次發(fā)送后經(jīng)過多少毫秒了.不需要我們來調(diào)用,參數(shù)的意義是距離上次調(diào)用經(jīng)過了多少時間,這個不需要我們操心,我們需要實現(xiàn)每次調(diào)用tick()的時候函數(shù)做什么
  • 我,們需要存儲已發(fā)送但未被確認(rèn)的報文段,進行累計確認(rèn),如果超時,只需要重傳最早的報文段即可
  • TCPReceiver 調(diào)用 unwrap 時的 checkpoint 是上一個接收到的報文段的 absolute_seqno,TCPSender 調(diào)用unwrap時的 checkpoint 是 _next_seqno。
  • retransmission timeout(RTO),具體實現(xiàn)是RFC6298的簡化版
    • 重傳連續(xù)的之后double ;收到ackno后重置到_initial_RTO
    • 可參考RFC 6298第5小節(jié)實現(xiàn)_timer
  • 加入成員變量

    private:bool _syn_sent = false;bool _fin_sent = false;uint64_t _bytes_in_flight = 0; // Number of bytes in flight 就是未被確認(rèn)的字節(jié)數(shù)uint16_t _receiver_window_size = 0; //接收方的滑動窗口uint16_t _receiver_free_space = 0; //接收方的剩余空間uint16_t _consecutive_retransmissions = 0; //unsigned int _time_elapsed = 0;bool _timer_running = false; std::queue<TCPSegment> _segments_outstanding{}; // the segment has been sentvoid send_segment(TCPSegment &seg); // send segmentbool _ack_valid(uint64_t abs_ackno);//! our initial sequence number, the number for our SYN.WrappingInt32 _isn;//! outbound queue of segments that the TCPSender wants sentstd::queue<TCPSegment> _segments_out{};//! retransmission timer for the connectionunsigned int _initial_retransmission_timeout;//! outgoing stream of bytes that have not yet been sentByteStream _stream;//! the (absolute) sequence number for the next byte to be sentuint64_t _next_seqno{0};unsigned int _rto = 0; public:

    函數(shù)實現(xiàn)

    fill_window() 實現(xiàn)

    • 如果syn未發(fā)送,發(fā)送并返回
    • 如果syn未應(yīng)答,返回
    • 如果fin已發(fā)送,返回
    • 如果 _receiver_window_size 不為 0
      • 當(dāng) receiver_free_space 不為 0,盡可能地填充 payload
      • 如果 _stream 已經(jīng) EOF,且 _receiver_free_space 仍不為 0,填上 FIN(fin 也會占用 _receiver_free_space)
      • 如果 _receiver_free_space 還不為 0,且 _stream 還有內(nèi)容,回到步驟 1 繼續(xù)填充
    • 如果 _receiver_window_size 為 0,則需要發(fā)送零窗口探測報文
      • 如果 _receiver_free_space 為 0
        • 如果 _stream 已經(jīng) EOF,發(fā)送僅攜帶 FIN 的報文
        • 如果 _stream 還有內(nèi)容,發(fā)送僅攜帶一位數(shù)據(jù)的報文

    send_segment()實現(xiàn)

    • 發(fā)送seq,不要忘記isn
    • 記錄接收方窗口大小,如果syn true,減去發(fā)送包的大小
    • 如果計時器未開啟,開啟計時器

    其他的都很好理解,看一下代碼就能懂

    總體來說,有點小復(fù)雜,總有一些地方不明了,最后借鑒了一位博主的實現(xiàn)成功完成

    #include "tcp_sender.hh"#include "tcp_config.hh"#include <random> #include <algorithm> // Dummy implementation of a TCP sender// For Lab 3, please replace with a real implementation that passes the // automated checks run by `make check_lab3`.template <typename... Targs> void DUMMY_CODE(Targs &&.../* unused */) {}using namespace std;//! \param[in] capacity the capacity of the outgoing byte stream //! \param[in] retx_timeout the initial amount of time to wait before retransmitting the oldest outstanding segment //! \param[in] fixed_isn the Initial Sequence Number to use, if set (otherwise uses a random ISN) TCPSender::TCPSender(const size_t capacity, const uint16_t retx_timeout, const std::optional<WrappingInt32> fixed_isn): _isn(fixed_isn.value_or(WrappingInt32{random_device()()})), _initial_retransmission_timeout{retx_timeout}, _stream(capacity), _rto{retx_timeout} {} uint64_t TCPSender::bytes_in_flight() const { return _bytes_in_flight; }void TCPSender::fill_window() {if (!_syn_sent) {_syn_sent = true;TCPSegment seg;seg.header().syn = true;send_segment(seg);return;}if (!_segments_outstanding.empty() && _segments_outstanding.front().header().syn)return;if (!_stream.buffer_size() && !_stream.eof())return;if (_fin_sent)return;if (_receiver_window_size) {while (_receiver_free_space) {TCPSegment seg;size_t payload_size = min({_stream.buffer_size(),static_cast<size_t>(_receiver_free_space),static_cast<size_t>(TCPConfig::MAX_PAYLOAD_SIZE)});seg.payload() = _stream.read(payload_size);if (_stream.eof() && static_cast<size_t>(_receiver_free_space) > payload_size) {seg.header().fin = true;_fin_sent = true;}send_segment(seg);if (_stream.buffer_empty())break;}} else if (_receiver_free_space == 0) {// The zero-window-detect-segment should only be sent once (retransmition excute by tick function).// Before it is sent, _receiver_free_space is zero. Then it will be -1.TCPSegment seg;if (_stream.eof()) {seg.header().fin = true;_fin_sent = true;send_segment(seg);} else if (!_stream.buffer_empty()) {seg.payload() = _stream.read(1);send_segment(seg);}} }void TCPSender::send_segment(TCPSegment &seg) {seg.header().seqno = wrap(_next_seqno, _isn);_next_seqno += seg.length_in_sequence_space();_bytes_in_flight += seg.length_in_sequence_space();if (_syn_sent)_receiver_free_space -= seg.length_in_sequence_space();_segments_out.push(seg);_segments_outstanding.push(seg);if (!_timer_running) {_timer_running = true;_time_elapsed = 0;} } // See test code send_window.cc line 113 why the commented code is wrong. //! \param ackno The remote receiver's ackno (acknowledgment number) //! \param window_size The remote receiver's advertised window size void TCPSender::ack_received(const WrappingInt32 ackno, const uint16_t window_size) {uint64_t abs_ackno = unwrap(ackno, _isn, _next_seqno);if (!_ack_valid(abs_ackno)) {// cout << "invalid ackno!\n";return;}_receiver_window_size = window_size;_receiver_free_space = window_size;while (!_segments_outstanding.empty()) {TCPSegment seg = _segments_outstanding.front();if (unwrap(seg.header().seqno, _isn, _next_seqno) + seg.length_in_sequence_space() <= abs_ackno) {_bytes_in_flight -= seg.length_in_sequence_space();_segments_outstanding.pop();// Do not do the following operations outside while loop.// Because if the ack is not corresponding to any segment in the segment_outstanding,// we should not restart the timer._time_elapsed = 0;_rto = _initial_retransmission_timeout;_consecutive_retransmissions = 0;} else {break;}}if (!_segments_outstanding.empty()) {_receiver_free_space = static_cast<uint16_t>(abs_ackno + static_cast<uint64_t>(window_size) -unwrap(_segments_outstanding.front().header().seqno, _isn, _next_seqno) - _bytes_in_flight);}if (!_bytes_in_flight)_timer_running = false;// Note that test code will call it again.fill_window(); }// See test code send_window.cc line 113 why the commented code is wrong. bool TCPSender::_ack_valid(uint64_t abs_ackno) {return abs_ackno <= _next_seqno &&// abs_ackno >= unwrap(_segments_outstanding.front().header().seqno, _isn, _next_seqno) +// _segments_outstanding.front().length_in_sequence_space();abs_ackno >= unwrap(_segments_outstanding.front().header().seqno, _isn, _next_seqno); } //! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method void TCPSender::tick(const size_t ms_since_last_tick) {if (!_timer_running)return;_time_elapsed += ms_since_last_tick;if (_time_elapsed >= _rto) {_segments_out.push(_segments_outstanding.front());if (_receiver_window_size || _segments_outstanding.front().header().syn) {++_consecutive_retransmissions;_rto <<= 1;}_time_elapsed = 0;} }unsigned int TCPSender::consecutive_retransmissions() const { return _consecutive_retransmissions; }void TCPSender::send_empty_segment() {TCPSegment seg;seg.header().seqno = wrap(_next_seqno, _isn);_segments_out.push(seg); }

    總結(jié)

    以上是生活随笔為你收集整理的CS144 计算机网络实验 lab3 笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。