【斯坦福计网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_;
}

相关文章
|
Java Maven
maven篇4:pom文件详解
maven篇4:pom文件详解
837 3
如何将Markdown文章轻松地搬运到微信公众号并完美地呈现代码内容
相信有很多童鞋跟我一样,热衷于用Markdown来编写文章。由于其简单的语法和清晰的渲染效果,受到广大码农朋友们的推崇。但是,当我们想维护起自己的公众号时,公众号编辑器往往让我们费劲了脑汁。本人尝试了各种工具,比如:秀米一些在线提供多种不同样式的编辑器。虽然这些编辑器都能够完成编辑任务,但是效果并不理想。与我们所追求的简洁、清晰风格总是格格不入,尤其是对于代码的展示非常的不友好。所以,这里给大家推荐一个本站的在线工具,可以帮助大家快速地把Markdown文章转换成微信公众号支持的漂亮格式。
610 0
如何将Markdown文章轻松地搬运到微信公众号并完美地呈现代码内容
|
域名解析 网络协议 测试技术
[插件使用] SwitchHosts自动更新Github Hosts文件
[插件使用] SwitchHosts自动更新Github Hosts文件
3297 0
|
存储 数据采集 前端开发
用Requests+Cookie,轻松获取淘宝商品数据!
大家好,我是志斌! 最近身边一直有朋友说用Selenium无法爬取淘宝的商品数据了,问问有没有其他的爬取方式,来获取淘宝的商品数据。方法当然有了,下面我就给大家介绍一个Requests+Cookie来获取淘宝数据的方法。
1273 0
用Requests+Cookie,轻松获取淘宝商品数据!
解决办法:defined but not used [-Werror=unused-variable]
解决办法:defined but not used [-Werror=unused-variable]
2387 0
|
6月前
|
人工智能 边缘计算 前端开发
人工智能平台 PAI DistilQwen2.5-DS3-0324发布:知识蒸馏+快思考=更高效解决推理难题
DistilQwen 系列是阿里云人工智能平台 PAI 推出的蒸馏语言模型系列,包括DistilQwen2、DistilQwen2.5、DistilQwen2.5-R1 等。DistilQwen2.5-DS3-0324 系列模型是基于 DeepSeek-V3-0324 通过知识蒸馏技术并引入快思考策略构建,显著提升推理速度,使得在资源受限的设备和边缘计算场景中,模型能够高效执行复杂任务。实验显示,DistilQwen2.5-DS3-0324 系列中的模型在多个基准测试中表现突出,其32B模型效果接近参数量接近其10倍的闭源大模型。
|
机器学习/深度学习
注意力机制(三)(不同注意力机制对比)
主要介绍了注意力机制的基本思想,以及注意力机制中一个常见的类型——自注意力机制。前面两篇文章为了帮助大家理解注意力机制的思想用了非常多的类比,以及联系生活实际。 然而,不管类比和联系多么恰当,这些做法多多少少都会让事物本身的特性被类比、联系后的事物所掩盖。
|
9月前
|
JSON Cloud Native API
API 规范和设计
今天主要和大家分享的是如何给予 Open API 3.0 标准来设计一套 API 规范。那么整体我们在讲的过程中,大约有以下五方面。 1. 大环境介绍 2. API与服务开放 3. API定义 4. 模型 5. 总结
896 5
|
11月前
|
安全 前端开发 云计算
Waline:一款开源、安全、简介的评论系统
阿里云计算巢提供了一键部署waline的功能,无需下载代码或安装复杂依赖,通过简单步骤即可搭建waline —— 一款带后端的极简风评论系统。
Waline:一款开源、安全、简介的评论系统
|
应用服务中间件 Apache 开发工具
【斯坦福计网CS144项目】环境配置 & Lab0: ByteStream
【斯坦福计网CS144项目】环境配置 & Lab0: ByteStream
324 0