【斯坦福计网CS144项目】Lab2 实现一个简单的 TCP 接收类

2024-01-27 05:28

本文主要是介绍【斯坦福计网CS144项目】Lab2 实现一个简单的 TCP 接收类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

🕺作者: 主页

我的专栏
C语言从0到1
探秘C++
数据结构从0到1
探秘Linux

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

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

文章目录

  • 一、实验目的
  • 二、实验说明
  • 三、实验内容
  • 四、实验体会
  • 五、代码附录

一、实验目的

1 实现一个简单的 TCP 接收类
2 对 TCP 数据传输有更深的理解

二、实验说明

  1. 我们在lab0中实现了字节流(ByteStream)的流控制抽象化。随后,在lab1中,我们创建了一个名为StreamReassembler的结构体,它可以接收同一字节流的子字符串序列,并将它们重新组装到原始流中。

  2. 尽管这些模块已经能够满足实验要求,但它们并没有涵盖TCP传输控制协议的细节。因此,在lab2中,我们打算实现一个名为TCPReceiver的模块,其主要功能是处理传入字节流的数据。换句话说,它能够正确读取IP数据报中所携带的信息。

  3. TCPReceiver将具备以下能力:对于正确接收且按序到达的数据报,它能够向发送方发送确认号,发送方可以根据此确认号来调整自身的发送行为。此外,TCPReceiver还能够告知发送方自身的接收窗口大小,以便发送方相应地进行发送调整。

三、实验内容

  1. 拉取lab2的代码,合并到当前目录中,然后在build目录下输入“make”对代码进行编译,结果无误开始下一步编写代码。
  2. 这次实验需要编写的文件如下图3-1所示。

image.png
图3-1 需要编写的文件

  1. 使用vscode远程连接虚拟机,可以很方便的编写代码,下面都是在vscode编辑器下进行。
  2. 实现在64位索引和32位序列号之间转换,编写"wrapping_integers.cc""wrapping_integers.hh"文件如图3-2、3-3所示。源代码见附录。

image.png
图 3-2 wrapping_integers.cc
image.png
图 3-3 wrapping_integers.hh

  1. 实现TCP接收器,编写tcp_receiver.cc tcp_receiver.hh文件如图3-4、3-5所示。源代码见附录。

image.png
图 3-4 tcp_receiver.cc
image.png
图3-5 tcp_receiver.hh

  1. 在build 目录下输入命令"make check2" 对lab2进行检查,结果如3-6所示,可以看到所有的测试样例全部通过。

image.png
图3-6 测试结果

四、实验体会

在本次实验中,我们实现了一个简单的TCP接收类。通过这个实验,我对TCP数据传输有了更深的理解,并学会了如何处理传入字节流的数据。
在实验过程中,我们创建了几个关键的模块来实现TCP接收类的功能。首先,在lab0中,我们实现了字节流的流控制抽象化,以便后续的操作。然后,在lab1中,我们创建了一个名为StreamReassembler的结构体,它可以接收同一字节流的子字符串序列,并将它们重新组装到原始流中。虽然这些模块已经能够满足实验要求,但它们并没有涵盖TCP传输控制协议的细节。
因此,在lab2中,我们打算实现一个名为TCPReceiver的模块,它的主要功能是处理传入字节流的数据。具体而言,TCPReceiver具备以下能力:

  1. 对于正确接收且按序到达的数据报,它能够向发送方发送确认号,发送方可以根据此确认号来调整自身的发送行为。
  2. TCPReceiver还能够告知发送方自身的接收窗口大小,以便发送方相应地进行发送调整。

在实现TCPReceiver模块的过程中,我们使用了Wrap32类来处理32位无符号整数的包装和解包装操作。Wrap32类具有一个起始值(zero point),当整数达到2^32 - 1时会重新回到起始值,实现循环计数的功能。
通过实现TCPReceiver模块,我们可以根据接收到的数据报的序列号(seqno)来处理数据,并将其插入到Reassembler中的正确流索引位置。同时,我们还可以根据已经接收到的数据量来确定应该发送的确认号(ackno)以及接收窗口大小(window size)。
总的来说,通过完成这个实验,我对TCP数据传输有了更深入的理解,特别是在处理接收端的数据时。我学会了如何使用TCPReceiver来处理传入字节流,并与发送方进行正确的数据交互。这个实验提供了一个很好的实践机会,使我更加熟悉TCP协议的工作原理和相关概念。

五、代码附录

  1. wrapping_integers.hh
#pragma once#include <cstdint>/** The Wrap32 type represents a 32-bit unsigned integer that:*    - starts at an arbitrary "zero point" (initial value), and*    - wraps back to zero when it reaches 2^32 - 1.*/class Wrap32
{
protected:uint32_t raw_value_ {};public:explicit Wrap32( uint32_t raw_value ) : raw_value_( raw_value ) {}/* Construct a Wrap32 given an absolute sequence number n and the zero point. */static Wrap32 wrap( uint64_t n, Wrap32 zero_point );/** The unwrap method returns an absolute sequence number that wraps to this Wrap32, given the zero point* and a "checkpoint": another absolute sequence number near the desired answer.** There are many possible absolute sequence numbers that all wrap to the same Wrap32.* The unwrap method should return the one that is closest to the checkpoint.*/uint64_t unwrap( Wrap32 zero_point, uint64_t checkpoint ) const;Wrap32 operator+( uint32_t n ) const { return Wrap32 { raw_value_ + n }; }bool operator==( const Wrap32& other ) const { return raw_value_ == other.raw_value_; }
};
  1. wrapping_integers.cc
#include "wrapping_integers.hh"using namespace std;Wrap32 Wrap32::wrap( uint64_t n, Wrap32 zero_point )
{// Your code here.// 当模是进制数的倍数时,取模就等效与截断,例如十进制:123 % 100,直接截断前面,保留最后两位得到23// 因此这里uint64_t n直接强转成uint32_t,截断前面32位,只保留低32位,等效于%2^32Wrap32 seqno( zero_point + static_cast<uint32_t>( n ) );(void)n;(void)zero_point;return seqno;
}
// seqno -> absolute seqno
uint64_t Wrap32::unwrap( Wrap32 zero_point, uint64_t checkpoint ) const
{// Your code here.uint64_t abs_seqno = static_cast<uint64_t>( this->raw_value_ - zero_point.raw_value_ );// 现在的abs_seqno是mod之后的值,还需要把它还原回mod之前的// 由于还原之后的abs_seqno需要离checkpoint最近,因此先找出checkpoint前后的两个可还原的abs_seqno,那个近选哪个就好// checkpoint / 2^32得到商,也即mod 2^32的次数uint64_t times_mod = checkpoint >> 32; // >>32等价于除以2^32// mod 2^32的余数,<<32实现截断前面32位,>>32实现保留低32位uint64_t remain = checkpoint << 32 >> 32; // 总体等效于%2^32,uint64_t bound;// 先取得离checkpoint最近的边界的mod次数(times_mod是左边界mod次数)if ( remain < 1UL << 31 ) // remain属于[0,2^32-1],mid=2^31(即1UL << 31)bound = times_mod;elsebound = times_mod + 1;// 以该边界的左右边界作为base,还原出2个mod之前的abs_seqno值// <<32等价于乘上2^32uint64_t abs_seqno_l = abs_seqno + ( ( bound == 0 ? 0 : bound - 1 ) << 32 ); // 注意bound=0的特殊情况uint64_t abs_seqno_r = abs_seqno + ( bound << 32 );// 判断checkpoint离哪个abs_seqno值近就取那个if ( checkpoint < ( abs_seqno_l + abs_seqno_r ) / 2 )abs_seqno = abs_seqno_l;elseabs_seqno = abs_seqno_r;(void)zero_point;(void)checkpoint;return abs_seqno;
}
  1. tcp_receiver.hh
#pragma once#include "reassembler.hh"
#include "tcp_receiver_message.hh"
#include "tcp_sender_message.hh"class TCPReceiver
{
public:TCPReceiver() : isn( 0 ), fin( 0 ), is_isn_set( 0 ), is_last_substring( 0 ) {}/** The TCPReceiver receives TCPSenderMessages, inserting their payload into the Reassembler* at the correct stream index.*/void receive( TCPSenderMessage message, Reassembler& reassembler, Writer& inbound_stream );/* The TCPReceiver sends TCPReceiverMessages back to the TCPSender. */TCPReceiverMessage send( const Writer& inbound_stream ) const;
private:Wrap32 isn;             // 记录流的ISN位置Wrap32 fin;             // 记录流的FIN位置bool is_isn_set;        // 标记ISN是否被设置了bool is_last_substring; // 标记是否到达流的末尾
};
  1. tcp_receiver.cc
#include "tcp_receiver.hh"using namespace std;void TCPReceiver::receive( TCPSenderMessage message, Reassembler& reassembler, Writer& inbound_stream )
{// 当设置了SYN标志时,记录ISN,标记is_isn_set,此标记一旦设置就一直有效if ( message.SYN == true ) {isn = Wrap32( message.seqno );is_isn_set = true;message.seqno = message.seqno + 1; // 此时seqno指向ISN,因此需要重定向为有效载荷的第一个字符}// 当设置FIN标志时,记录FIN,标记is_last_substring,这只针对一个数据,因此没有FIN标志时,要移除is_last_substring标记if ( message.FIN == true ) {is_last_substring = true;fin = Wrap32( message.seqno + message.payload.size() );} elseis_last_substring = false;// 把数据送进reassembler,first_index需要转换// first_index = absolute seqno - 1 , absolute seqno = seqno.unwrap// unwrap时的checkpoint使用_first_unassembled_index = Writer.bytes_pushed()if ( is_isn_set == true ) // ISN被设置后才能转换seqno,因此ISN没设置之前不能pushreassembler.insert( message.seqno.unwrap( isn, inbound_stream.bytes_pushed() ) - 1,message.payload,is_last_substring,inbound_stream );(void)reassembler;(void)inbound_stream;
}
TCPReceiverMessage TCPReceiver::send( const Writer& inbound_stream ) const
{TCPReceiverMessage tcpReceiverMessage;// 当ISN被设置之后,才设置acknoWrap32 ackno= Wrap32::wrap( inbound_stream.bytes_pushed() + 1, isn ); // _first_unassembled_index先+1转成abs seqno,再wrapif ( is_isn_set == true )tcpReceiverMessage.ackno = ackno == fin ? ackno + 1 : ackno; // 特殊情况,ackno需要越过FINtcpReceiverMessage.window_size= inbound_stream.available_capacity() > UINT16_MAX ? UINT16_MAX : inbound_stream.available_capacity(); // window_size的上限是UINT16_MAX(void)inbound_stream;return tcpReceiverMessage;
}

这篇关于【斯坦福计网CS144项目】Lab2 实现一个简单的 TCP 接收类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/649177

相关文章

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1