【斯坦福计网CS144项目】Lab1 实现一个流重组器

简介: 【斯坦福计网CS144项目】Lab1 实现一个流重组器

😘欢迎关注:👍点赞🙌收藏✍️留言

🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!

一、实验目的

  1. 实现一个流重组器——一个将字节流的小块 (称为子串或段 )按正确顺序组装成连续的字节流的模块
  2. 深入理解 TCP 协议的工作方式

二、实验说明

在lab0中,我们使用了Linux操作系统内置的传输控制协议(TCP)实现,并利用互联网流套接字从一个网站获取信息并发送电子邮件。尽管底层网络只提供"尽力而为"的数据报服务,但我们成功地通过在计算机内存中自行实现字节流的抽象来生成了一对可靠的按顺序排列的字节流,其中一个字节流是从主机到服务器的,另一个字节流是从服务器到主机的。

  1. 在lab1要求编写一个名为"StreamReassembler"的数据结构,它负责重新组装数据。该结构将接收子串(由一串字节和大数据流中该串的第一个字节的索引组成),并提供一个名为"ByteStream"的输出,其中所有的数据都被正确排序。

三、实验内容

  1. 输入"cd minnow/build"切换到实验作业的存储库中,运行”git fetch”来检索实验作业的最新版本。通过运行"git merge origin/check1-startercode",下载lab 1 的启动代码。如图 2-1 所示。

图 2-1 获取实验代码

注意:如果拉取合并代码后报错,可以先把之前的代码单独拷贝出来,然后从lab0那个网站拉取代码,直接合并lab1的代码,再把自己的代码拷贝进去即可。

  1. 在文件"./src/reassembler.hh ./src/reassembler.cc"中代码修改如图2-2所示。代码源码见附录。

图2-2 reassembler.hh 代码细节

图2-3 reassembler.cc 代码细节

  1. 确认文件无误后,在/build目录下输入 "make check1"对文件进行编译测试,测试结果如图2-4所示。可以看到所有的测试结果全部通过。

图2-4 测试结果

整体实现框架如图2-5所示。

图2-5 整体框架

四、实验体会

在这个实验中,我们的目的是实现一个流重组器,它能够将字节流的小块按正确顺序组装成连续的字节流。通过这个实验,我们不仅要达到技术上的理解和掌握,还要深入思考TCP协议的工作方式。

首先,在实验说明中,我们了解到在lab0中,使用了Linux操作系统内置的传输控制协议(TCP)实现,并利用互联网流套接字从一个网站获取信息并发送电子邮件。尽管网络只提供"尽力而为"的数据报服务,但通过在计算机内存中自行实现字节流的抽象,我们成功地生成了一对可靠的按顺序排列的字节流。这让我们对TCP协议的工作方式有了更深刻的理解。

接着,在实验内容中,我们需要编写一个名为"StreamReassembler"的数据结构,用于重新组装数据。这个数据结构接收子串,其中子串由一串字节和大数据流中该串的第一个字节的索引组成。我们需要确保输出的"ByteStream"中的数据按照正确的顺序进行排序。

为了完成这个实验,我们需要进行一系列步骤。首先,我们切换到实验作业的存储库中,并运行"git fetch"来检索实验作业的最新版本。然后,我们运行"git merge origin/check1-startercode"来下载lab 1的启动代码。在修改了相关代码后,我们在/build目录下进行编译测试,使用"make check1"命令。通过测试结果可以看到,所有的测试都通过了,证明我们的实现是正确的。

通过这个实验,我们得出了一些体会和总结。在理解性方面,我们深入了解了TCP协议的工作原理,包括连接建立、数据传输和连接关闭等过程。在总结性方面,我们意识到了网络编程的重要性,并学习了处理缓存数据、接收数据和组装字节流等实际开发中需要注意的问题。在知识性方面,我们掌握了TCP协议的相关知识和流重组器的实现方法。

综合来看,这个实验对于我们的学习和成长具有重要意义。通过实践,我们不仅掌握了网络编程和计算机科学的相关知识和技能,还提高了我们的实际开发能力。在今后的学习和工作中,我们可以更加熟练地处理网络数据和解决相关问题,为我们未来的职业发展打下坚实的基础。

五、代码附录

  1. 附录一:reassembler.hh
#pragma once

#include "byte_stream.hh"🔄  ❓ 

#include <string>
#include <list>
#include <tuple>

class Reassembler
{
  bool had_last_ {};  // 是否已经插入了最后一个字符串
  uint64_t next_index_ {};  // 下一个要写入的字节的索引
  uint64_t buffer_size_ {}; // buffer_中的字节数
  std::list<std::tuple<uint64_t, uint64_t, std::string>> buffer_ {};

  /**
   * \brief 将data推入output流.
   */
  void push_to_output(std::string data, Writer& output);

  /**
   * \brief 将data推入buffer暂存区.
   * \param first_index data的第一个字节的索引
   * \param last_index  data的最后一个字节的索引
   * \param data        待推入的字符串, 下标为[first_index, last_index]闭区间
   */
  void buffer_push( uint64_t first_index, uint64_t last_index, std::string data ); 🔄  ❓

  /**
   * 尝试将buffer中的串推入output流.
   */
  void buffer_pop(Writer& output);

public:
  /*
   * Insert a new substring to be reassembled into a ByteStream.
   *   `first_index`: the index of the first byte of the substring
   *   `data`: the substring itself
   *   `is_last_substring`: this substring represents the end of the stream
   *   `output`: a mutable reference to the Writer
   *
   * The Reassembler's job is to reassemble the indexed substrings (possibly out-of-order
   * and possibly overlapping) back into the original ByteStream. As soon as the Reassembler
   * learns the next byte in the stream, it should write it to the output.
   *
   * If the Reassembler learns about bytes that fit within the stream's available capacity
   * but can't yet be written (because earlier bytes remain unknown), it should store them
   * internally until the gaps are filled in.
   *
   * The Reassembler should discard any bytes that lie beyond the stream's available capacity 🔄  ❓
   * (i.e., bytes that couldn't be written even if earlier gaps get filled in).  🔄  ❓
   *
   * The Reassembler should close the stream after writing the last byte.
   */
  void insert( uint64_t first_index, std::string data, bool is_last_substring, Writer& output );

  // How many bytes are stored in the Reassembler itself?
  uint64_t bytes_pending() const;
};
  1. 附录二:reassembler.cc
#include "reassembler.hh"

#include <ranges>
#include <algorithm>

using namespace std;
void Reassembler::push_to_output( std::string data, Writer& output ) {
  next_index_ += data.size();
  output.push( move( data ) );
}

void Reassembler::buffer_push( uint64_t first_index, uint64_t last_index, std::string data )
{
  // 合并区间
  auto l = first_index, r = last_index;
  auto beg = buffer_.begin(), end = buffer_.end();
  auto lef = lower_bound( beg, end, l, []( auto& a, auto& b ) { return get<1>( a ) < b; } );
  auto rig = upper_bound( lef, end, r, []( auto& b, auto& a ) { return get<0>( a ) > b; } );
  if (lef != end) l = min( l, get<0>( *lef ) );
  if (rig != beg) r = max( r, get<1>( *prev( rig ) ) );
  
  // 当data已在buffer_中时,直接返回
  if ( lef != end && get<0>( *lef ) == l && get<1>( *lef ) == r ) {
    return;
  }

  buffer_size_ += 1 + r - l;
  if ( data.size() == r - l + 1 && lef == rig ) { // 当buffer_中没有data重叠的部分
    buffer_.emplace( rig, l, r, move( data ) );
    return;
  }
  string s( 1 + r - l, 0 );

  for ( auto&& it : views::iota( lef, rig ) ) {
  auto& [a, b, c] = *it;
  buffer_size_ -= c.size();
    ranges::copy(c, s.begin() + a - l);
  }
  ranges::copy(data, s.begin() + first_index - l);
  buffer_.emplace( buffer_.erase( lef, rig ), l, r, move( s ) );
}

void Reassembler::buffer_pop( Writer& output ) {
  while ( !buffer_.empty() && get<0>( buffer_.front() ) == next_index_ ) {
    auto& [a, b, c] = buffer_.front();
    buffer_size_ -= c.size();
    push_to_output( move( c ), output ); 
    buffer_.pop_front();
  }

  if ( had_last_ && buffer_.empty() ) {
    output.close();
  }
}

void Reassembler::insert( uint64_t first_index, string data, bool is_last_substring, Writer& output )
{
  if ( data.empty() ) {
    if ( is_last_substring ) {
      output.close();
    }
    return;
  }
  auto end_index = first_index + data.size();                  // data: [first_index, end_index)      
  auto last_index = next_index_ + output.available_capacity(); // 可用范围: [next_index_, last_index)  
  if ( end_index < next_index_ || first_index >= last_index ) {
    return; // 不在可用范围内, 直接返回
  }

  // 调整data的范围
  if ( last_index < end_index ) {
    end_index = last_index;
    data.resize( end_index - first_index );
    is_last_substring = false;
  }
  if ( first_index < next_index_ ) {
    data = data.substr( next_index_ - first_index );
    first_index = next_index_;
  }

  // 若data可以直接写入output, 则直接写入
  if ( first_index == next_index_ && ( buffer_.empty() || end_index < get<1>( buffer_.front() ) + 2 ) ) {
    if ( buffer_.size() ) { // 若重叠, 则调整data的范围
      data.resize( min( end_index, get<0>( buffer_.front() ) ) - first_index );
    }
    push_to_output( move( data ), output );
  } else { // 否则, 将data插入buffer_
    buffer_push( first_index, end_index - 1, data );
  }
  had_last_ |= is_last_substring;
  
  // 尝试将buffer_中的数据写入output
  buffer_pop(output);
}

uint64_t Reassembler::bytes_pending() const
{
  return buffer_size_;
}

相关文章
|
3月前
|
人工智能 安全 机器人
LLM对齐数据全自动合成!UW华人博士生提出Magpie方法,Macbook Air即可运行
【8月更文挑战第11天】在AI领域,大型语言模型(LLM)的行为对齐一直是个挑战。华盛顿大学研究人员提出名为Magpie的新方法,能自动高效生成高质量指令数据,减少人工干预,提升LLM的对齐效果。通过输入模板,Magpie利用已对齐LLM生成能力自动生成指令数据,仅需少量GPU资源即可创建大规模数据集。实验显示,使用Magpie数据集微调的模型性能媲美传统监督方法。尽管如此,Magpie仍需进一步优化以生成特定领域指令并确保数据安全性。[论文](https://arxiv.org/abs/2406.08464)
161 60
|
3月前
|
机器学习/深度学习 人工智能 数据可视化
斯坦福博士图解AlphaFold 3:超多细节+可视化还原ML工程师眼中的AF3
【8月更文挑战第8天】AlphaFold 3作为AI领域的重大突破,革新了蛋白质结构预测。斯坦福博士通过图解详析了其内部机制,展示了多尺度建模与图神经网络技术如何提升预测精度。尽管存在数据依赖性和计算成本等挑战,AlphaFold 3仍极大地加速了生物学研究与药物开发进程。论文详情参见:https://www.nature.com/articles/s41586-024-07487-w
87 4
|
6月前
|
网络协议 索引
【斯坦福计网CS144项目】Lab2 实现一个简单的 TCP 接收类
【斯坦福计网CS144项目】Lab2 实现一个简单的 TCP 接收类
41 0
|
6月前
|
网络协议 开发工具 网络架构
【斯坦福计网CS144】Lab2终结笔记
【斯坦福计网CS144】Lab2终结笔记
70 0
|
6月前
|
缓存 网络协议 开发工具
【斯坦福计网CS144】Lab0终结笔记
【斯坦福计网CS144】Lab0终结笔记
114 0
|
6月前
|
安全 网络协议 网络安全
【斯坦福计网CS144】Lab6终结笔记
【斯坦福计网CS144】Lab6终结笔记
78 0
|
6月前
|
网络协议 开发工具 git
【斯坦福计网CS144】Lab4终结笔记
【斯坦福计网CS144】Lab4终结笔记
89 0
|
6月前
|
网络协议 Linux 网络性能优化
【斯坦福计网CS144】Lab5终结笔记
【斯坦福计网CS144】Lab5终结笔记
62 0
|
6月前
|
网络协议 安全 网络安全
【斯坦福计网CS144】Lab7终结笔记
【斯坦福计网CS144】Lab7终结笔记
76 0
|
6月前
|
存储 网络协议 算法
【斯坦福计网CS144】Lab3终结笔记
【斯坦福计网CS144】Lab3终结笔记
57 0