当前位置: 代码迷 >> 综合 >> Stanford University CS144 Lab0 Networking Warmup
  详细解决方案

Stanford University CS144 Lab0 Networking Warmup

热度:99   发布时间:2023-11-17 17:28:28.0

文章目录

    • 前引
    • 二次编辑补引
    • Lab0 Networking Warmup
      • 获取实验文档
      • Ubuntu20.04安装(Vmware Workstation)
      • Ubuntu20.04安装G++8 切换G++版本
      • 1、Fetch a Web page(取得网页)
      • 2、Send yourself an email(给自己发一封电子邮件)
      • 3、Writing a network program using an OS stream socket(使用操作系统流套接字编写网络程序)
        • 1、Let’s get started—fetching and building the starter code(让我们开始-获取和构建启动代码)
        • 2、Modern C++: mostly safe but still fast and low-level(编写代码注意事项)
        • 3、Reading the Sponge documentation(阅读相关文档)
        • 4、Writing webget(写网络获取)
      • 4、An in-memory reliable byte stream(实现内存中可靠的字节流)
        • 1、熟悉实验环境(byte_stream.cc byte_stream.hh)
        • 2、了解实验目的
        • 3、函数介绍(注意事项)
        • 4、简易代码实现(byte_stream.cc byte_stream.hh)(string效率不高)
          • 代码实现(byte_stream.hh)
          • 代码实现(byte_stream.cc)
        • 4、编译运行 测试用例检验程序 (100% tests passed)
        • 5、优化代码实现(byte_stream.cc byte_stream.hh)(deque效率较高)
          • 代码实现(byte_stream.hh)
          • 代码实现(byte_stream.cc)
        • 5、编译运行 测试用例检验程序 (100% tests passed)


前引


各位好 鉴于刚刚看完《计算机网络自顶向下》 完成了课后基本上所有的Lab 包括 Socket Lab + Wireshark Lab + Miscellaneous Lab 但是仍感觉到少了点什么

于是去逛知乎 逛各种的论坛 看看还有什么好学的 哈哈哈 接下来自然而然就是你所看到的 对的 接下来的一段时间内可能就要与CS144做做接触啦

但是呢 就像原来我学习操作系统那样 在看了基础书 在学习了顶尖高校的网课并把课后所有的Lab完成后 仍然感觉身处迷雾中 所以就自己去看了《操作系统真象还原》 自己从零开始完成了一个操作系统 但那个时候在各种地方搜索 相关的博客记录 都没有发现有很细致的记录- - 于是自己就做了全流程记录

对于CS144 我刚开始看到各种各样的文档和各种东西都需要处理 比如在什么地方下载代码 什么地方能够下载到资源 都没有什么博客很详细的介绍了 基本上都是直截了当的介绍Lab内容 对于像我们这种懒人还是挺不友好地 哈哈 开玩笑
我也是刚刚才把文档都下载了下来 然后重新配置了一下虚拟机 哈哈 感觉其实前需工作还是挺多的 但是入门了之后就快了 那下面就一步步来吧

对了 关于获取实验测试用例 后面会有地方报错 那个地方后面进行到那一步再说啦 各位不要急


二次编辑补引


各位能够看到这篇文章的时候 或者说这篇文章还在 或者是我还在持续的完成CS144的话 其实是由于CSDN有一个回收站的功能

其实在写这个二次编辑的补引的前一天晚上 我是把这篇博客删了 并且打算不再完成CS144了的 因为挺受打击的
这段时间大二开学 不知道为什么 忽然一下子课程就超级超级多 我本来大一的时候就觉得课程已经够多了 大一学完了线代 高数 大物上 还有各种各样的课程 尽管大一一年 计算机专业课 我们大学只有一门C语言 + python 其他一门多余的专业课也没有了 大二上只有仅仅一门数据结构 在这里 我是不得不吐槽一下专业课 真的无大语

课程设计者真的会计算机吗 还是因为大类课才是计算机的核心?之前 高数 大物一周两节 c语言只配一周一节?

好了好了 我在这里念叨两句 只是因为大二上我的课实在是太多了 而且一些课时必须要去上的 如果一两节不上的话直接挂科 数电 大物 大物实验 离散 概率 而且还不谈那些其他形式主义的课了 马原 英语 体育 每科都有作业 压力真的还是挺大的

回到正题 为什么我会删除这篇博客呢 因为我在实现Lab1的时候 花了大概两天 知道这个时候我才做出来 - - 不知道是不是因为这段时间没有休息好 还是自己对于函数需求没有了解清楚的情况下 直接开敲 还是因为自己确实是没有想到怎么实现的 我大概尝试了四种数据结构 直到看了别人做的思路 昨晚睡前真的给我整emo了 就打算放弃这个Lab

但为什么现在还在坚持呢 主要是我觉得 我暑假的时候 自己能够能静下心来 自己实现一个操作系统 那个调试难度和编程难度至少比这个大吧 而且我是一个比较有强迫症的人 如果我有什么事情没有完成 我就会一直记在心里面 而且会顾念很久 如果我现在放弃了 直接去学数据库 那我很清楚我自己 到最后学数据库的时候 仍然会念及这个时候的CS144 我是一个比较喜欢善始善终的人 所以可能这才让我真的能够坚持下来吧

好了 话说了那么多 对于刚开始看博客的hxd 连内容都一点没看 就在这里看我啰啰嗦嗦的自言自语 实在抱歉 下面的我写的还是非常仔细的 希望能够帮助到大家 那请各位看官 继续往下面看吧


Lab0 Networking Warmup


获取实验文档


CS144实验文档获取链接
CS144 Lab Assignments

我们直接下载即可 并我做了PDF Translate 就先把八个文档全部下载下来了
关于Test

在这里插入图片描述


Ubuntu20.04安装(Vmware Workstation)


首先的话 我们需要一个Linux的环境 因为之前我自己做了个操作系统 就是需要一个虚拟机 这边我没有看VirtualBox 而是沿用到之前的Vmware WorkStation Ubuntu 20.04 关于Vmware Workstation的下载 Ubuntu 20.04安装 Vmware tool的安装 麻烦大家移步到我的博客链接处看一下吧

安装Vmware Station 安装Ubuntu
WIN10虚拟机安装 UBuntu Vmware tools Lab入门测试 环境配置 安装摸索之路


Ubuntu20.04安装G++8 切换G++版本


因为之前写操作系统所需版本需要是G++-4 所以这次需要换成G++-8 然后更改一下版本
我们可以先gcc -v看一下版本 发现是gcc 4.4.7
在这里插入图片描述


我们输入命令sudo apt install g++-8
y
在这里插入图片描述


update-alternatives --config gcc 查看gcc优先级选择
在这里插入图片描述


sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/g++-8 150 增加g++-8优先级 后面数值越高 越优先使用

查看update-alternatives --config gcc gcc -v 查看优先级 查看版本 如下则我们成功更换
在这里插入图片描述gcc -v得到的版本
在这里插入图片描述


1、Fetch a Web page(取得网页)


从这里我们就正式进入CS144辣 一步步慢慢来
我们主要目的通过telnet 利用TCP/IP协议 先与网站创建TCP连接 取得信息

我们先输入telnet cs144.keithw.org http采用http协议
之后就会创建连接
当出现了cs144.keithw.org时 我们输入GET /hello HTTP/1.1 /hello则是当前文件夹的相对路径 得到cs144.keithw.org/hellohtml文件
Host则是说明目的主机域名
Connection:表示获取文件后即断开连接


输入代码如下

telnet cs144.keithw.org httpGET /hello HTTP/1.1
Host: cs144.keithw.org
Connection: close

建议每次输入代码时 放到一个txt文件中 如下 用的时候cv过去即可
在这里插入图片描述


效果图如下
在这里插入图片描述


2、Send yourself an email(给自己发一封电子邮件)


由于原来在学习《计算机网络自顶向下方法》那个地方 写过这个类似的Lab 那个Lab我也写了很久 我这边就直接贴一下链接吧
但由于我们不是斯坦福的学生qwq 所以就用这个代替
这个Lab是我用python写的 我直接把代码贴在下面 因为现在与邮箱服务器交互都有认证 所以难度不低

《计算机网络自顶向下》Socket Lab3 SMTP Lab


实现代码如下 交互由网易163邮箱qq邮箱

from socket import *
import sys
import base64msg = "\r\n I love computer networks!"
endmsg = "\r\n.\r\n"# Choose a mail server (e.g. Google mail server) and call it mailserver
mailserver_163 = 'smtp.163.com'
mailserver_163_SMTP_Port = 25
mailserver_163_SMTP_LoginID  = base64.b64encode(b'lov******@163.com').decode() + '\r\n'
mailserver_163_SMTP_Password = base64.b64encode(b'ZLVZ**********').decode() + '\r\n'From = 'lov******@163.com'
To = '22*********@qq.com'# Create socket called clientSocket and establish a TCP connection with mailserver
clientSocket = socket(AF_INET,SOCK_STREAM)
clientSocket.connect((mailserver_163,mailserver_163_SMTP_Port))
recv = clientSocket.recv(1024).decode()
print(recv,end = '')
if recv[:3] != '220':print('220 reply not received from server.')clientSocket.close()exit(0)# Send HELO command and print server response.
heloCommand = 'HELO Love6\r\n'
clientSocket.send(heloCommand.encode())
recv1 = clientSocket.recv(1024).decode()
print(recv1,end = '')
if recv1[:3] != '250':print('250 reply not received from server.')clientSocket.close()exit(0)logCommand = 'AUTH LOGIN\r\n'
clientSocket.send(logCommand.encode())
recv2 = clientSocket.recv(1024).decode()
print(recv2,end = '')
if recv2[:3] != '334':print('334 login server goes wrong')clientSocket.close()exit(0)clientSocket.send(mailserver_163_SMTP_LoginID.encode())
recv3 = clientSocket.recv(1024).decode()
print(recv3,end = '')
if recv3[:3] == '535':print('Login ID wrong')clientSocket.close()exit(0)clientSocket.send(mailserver_163_SMTP_Password.encode())
recv4 = clientSocket.recv(1024).decode()
print(recv4,end = '')
if recv4[:3] == '535':print('Password wrong')clientSocket.close()exit(0)fromCommand = 'MAIL FROM ' + '<' + From + '>' + '\r\n'
clientSocket.send(fromCommand.encode())
recv = clientSocket.recv(2048).decode()
print(recv,end = '')
if recv[:3] != '250':print('Mail From server goes wrong')clientSocket.close()exit(0)toCommand = 'RCPT TO: ' + '<' + To + '>' + '\r\n'
clientSocket.send(toCommand.encode())
recv6 = clientSocket.recv(1024).decode()
print(recv6,end = '')
if recv6[:3] != '250':print('Mail to server goes wrong')clientSocket.close()exit(0)beginCommand = 'DATA\r\n'
clientSocket.send(beginCommand.encode())
recv7 = clientSocket.recv(1024).decode()
print(recv7,end = '')
if recv7[:3] != '354':print('Data Begin server goes wrong')clientSocket.close()exit(0)send = "From: " + From + '\r\n'
send += "To: " + To + '\r\n'
send += "Subject: " + "First Web Mail Test From Love6" + '\r\n'
send += msg
clientSocket.send(send.encode())
clientSocket.send(endmsg.encode())
recv8 = clientSocket.recv(1024).decode()
print(recv8, end='')
if recv8[:3] != '250':print('Data Transport goes wrong')clientSocket.close()exit(0)endCommand = 'QUIT\r\n'
clientSocket.send(endCommand.encode())
recv9 = clientSocket.recv(1024).decode()
print(recv9, end='')
if recv9[:3] != '221':print('server end goes wrong')clientSocket.close()exit(0)
clientSocket.close()

实现效果
163发送端
在这里插入图片描述


qq邮箱接受端
在这里插入图片描述


python 响应效果

在这里插入图片描述


3、Writing a network program using an OS stream socket(使用操作系统流套接字编写网络程序)


1、Let’s get started—fetching and building the starter code(让我们开始-获取和构建启动代码)


这部分呢 对于一些不同的模拟机可能会出现各种不一样的情况
这里呢 我们什么都不用做 直接复制运行官方给的代码 我们就能从网页上面把测试用例复制下来

下面是代码

git clone https://github.com/cs144/sponge
cd sponge
mkdir build
cd build
cmake ..
make

如果你直接选好一个位置 并打开终端 运行这些代码 运行截图如下的话 那恭喜你成功了(成功截图) 可以直接跳到下面的实验部分继续辣

在这里插入图片描述


但如果你和我一样出现了各种各样的问题的话 那我们继续往下走
1、没有安装cmake sudo apt-get install cmake即可解决 环境linux apt命令

2、运行cmake .. C compiler identification is unknown或者CXX compiler identification is unknown
那这个问题就稍微需要多花点功夫了 因为我这里也出现了问题
下面是我出现问题的截图

在这里插入图片描述


还记得我们在文章的开头说了需要把编译器弄成G++-8cmake的默认寻找我们的编译器位置应该是usr/bin/gcc usr/bin/g++ 而我去打开了usr/bin文件夹 发现我们之前安装的g++ gcc编译器的名字是gcc-8 g++-8 所以我们需要更改编译器的寻找路径 gcc g++编译器我们所需要的 我在相同位置找到了 如图所示

在这里插入图片描述


我们只需要把代码由cmake .. 改成
cmake .. -DCMAKE_CXX_COMPILER=/usr/bin/g++-8 -DCMAKE_C_COMPILER=/usr/bin/gcc-8 即可 大家自己找一下自己之后安装的编译器位置就行了 然后下面是成功cmake的截图
接着就跟上面一样的make即可

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


2、Modern C++: mostly safe but still fast and low-level(编写代码注意事项)


下面是原文 我直接贴下来

●使用上的语言文档https://en.cppreference.com作为资源。
●永远不要使用malloc()或free的()。
●永远不要使用新的或删除的。
●基本上永远不要使用原始指针(),而是使用“智能”指针(唯一的ptr或
只有在必要时才能共享。(您不需要在CS144中使用这些功能。)
●避免使用模板、线程、锁和虚拟功能。(您不需要在CS144中使用这些功能。)
●避免c型字符串(char str)或字符串函数(strlen(),strcpy())。这些都很容易出错。使用std::字符串代替。
●永远不要使用C样式的类型转换(例如,(FILE)x)。如果需要,请使用C++静态强制转换(通常在CS144中不需要这样)。
●优先通过const引用传递函数参数。:连续的地址和地址)。
●使每个变量都保持不变,除非它需要发生突变。
●使每个方法都保持不变,除非它需要修改对象。
●避免全局变量,并给每个变量尽可能小的范围。
●在提交作业之前,请运行制作格式来规范编码样式。

关于使用Git:实验室作为Git(版本控制)存储库分发——一种记录更改、检查指向版本以帮助调试和跟踪源代码来源的方法。请在工作时经常进行小提交,并使用确定更改和原因。柏拉图式的理想是,每个提交都应该编译,并应该稳步走向通过越来越多的测试。让小“语义”提交有助于调试(更容易调试如果每个提交编译和消息描述了一个明确的提交),保护你免受作弊的索赔记录稳定的进步——这是一个有用的技能,将帮助在任何职业生涯,包括软件发展。评分人员将阅读您的提交信息,以了解您是如何为实验室开发解决方案的。如果您还没有学会如何使用Git,请在CS144办公时间寻求帮助或咨询教程(例如,https://guides.github.com/introduction/git-handbook).最后,欢迎您将代码存储在GitHub、GitLab、bi桶等的私人存储库中,但请确保您的代码不能公开访问。


3、Reading the Sponge documentation(阅读相关文档)


下面是原文 但是链接已经打不开了 - - 这里挺重要的 所以下面就不放塑料机翻了 直接放的是原文英文

To support this style of programming, Sponge’s classes wrap operating-system functions(which can be called from C) in “modern” C++.

1. Using a Web browser, read over the documentation to the starter code athttps://cs144.github.io/doc/lab0.
2. Pay particular attention to the documentation for the FileDescriptor, Socket,TCPSocket, and Address classes. (Note that a Socket is a type of FileDescriptor,and a TCPSocket is a type of Socket.
3. Now, find and read over the header files that describe the interface to these classes in the libsponge/util directory: file_descriptor.hh, socket.hh, and address.hh.


4、Writing webget(写网络获取)


终于进入第一个动手开始写的编程了 ^^

我们直接少说其他的话 我们先直接进入sponge/apps/webget.cc看看 由于我用不惯vim 而且因为之前编写操作系统都是在下面这种文本编辑器下编辑的 所以在打开的时候选择了是下面这种来编辑的

打开后图如下

在这里插入图片描述


我第一次拿到手的时候 还以为很多东西都是从零开始做 - - 但是还记得我上面在上一节中 专门标识了这些头文件 很明显的也可以看得见 我们也是引用了两个看起来很奇怪的文件 但又很熟悉socket.hh util.hh

那我们就进入编写的环节了
和以往做的Lab不同 我们现在需要编写的程序 需要用Lab 提供的接口 这里我先说一下 做下面Lab首先得会看的懂C++ 如果不会的话 很多地方都没有办法理解 - - 我的C++基础并不是很好 尽管刷完了北大的C++题 但是仅限于使用在刷力扣上面 其他地方几乎没有用过 所以做这个Lab我也是去看了很多关于 继承 Public Private Protected 类的相关知识

对于这部分我就不详细一点点的分析了 大家在编程的时候 建议打开socket.cc socket.hh address.cc address.hh file_descriptor.cc file_descriptor.hh仔细的看一下TCPSocket相关类的函数 我也是仔细看了很久 才了解了大部分的函数用法 - -

这部分让我唯一不理解的是 socket发送消息 我们需要write进入 这个其实都还好 关键是对于UDPSocket里面都有send sendto TCPSocket没有 - - 我还是推断+看别人写的才知道send我们是用write写进去的

还有一点 需要注意在HTTP格式 结束后需要再加一行\r\n 我在尝试了10分钟一直408后 我就发现是最后还少了\r\n


代码如下 直接给咯

void get_URL(const string &host, const string &path) {
    Address addr(host,"http");TCPSocket client_socket;client_socket.connect(addr);string send_msg = "GET "+ path +" HTTP/1.1\r\n"+"Host: "+host+"\r\nConnection: close\r\n\r\n";client_socket.write(send_msg);string recv_msg;while(!(recv_msg = client_socket.read()).empty() && !client_socket.eof())cout << recv_msg;cerr << "Function called: get_URL(" << host << "," << path << ").\n";cerr << "Warning: get_URL() has not been implemented yet.\n";
}

我们编写好后 进入sponge/build目录 打开终端
make编译 如果有错的话 去把错误改了重新编译即可
./apps/webget cs144.keithw.org /hello 测试 里面的域名、文件可以换成 其他的

自己感觉测试的都没什么问题后 进入 其他测试用例的test 如果这个pass的话 基本上程序是没有什么错误的 就像leetcode 你尝试刚开始的测试用例 感觉都是对的之后 最后提交 大量的测试用例如果都对的话 才是真的程序正确了
还是在build路径 输入make check_webget 就能看到结果 下面是我的测试结果 那这一部分就结束啦

在这里插入图片描述


4、An in-memory reliable byte stream(实现内存中可靠的字节流)


1、熟悉实验环境(byte_stream.cc byte_stream.hh)


当你看到这段话的时候 我已经完成了这个Lab了 但是这个编程我竟然编了大概有2-3小时 而且中间一直出错

其实思路相当相当的简单 我写这么久的原因是因为
1、没有考虑到完全分层
2、对部分函数的功能没有完全熟悉 导致一直出错
3、对整个Lab 要求完成的功能有些地方还不是很熟悉- -

其实刚开始我看到Lab的时候 我就知道整体思路了 而且能够马上开始敲 - - 真没想到会写这么久 无语 今天晚上还要写明天的大物实验报告 呜呜 但是为了对CS144的首篇开一个好头 我也相信很多hxdCS144开始都很懵比 也是想看看其他人是怎么做的 所以这里我会写的很详细很详细 那就走起吧


我们需要修改 或者叫做 需要编写的程序是byte_stream.ccbyte_stream.hh 那就让我们在下面看看他的真面目吧!

byte_stream.cc
在这里插入图片描述


byte_stream.hh
在这里插入图片描述


看到它 大家不要害怕 哈哈哈 其实我们需要编写的部分没有那么多 看到它很懵是很正常的 我刚开始看着的时候也懵 下面就先介绍一下我们的实验内容和最后要达到的效果


2、了解实验目的


下面放一下机翻 文档中的实验目的吧

到目前为止,您已经看到了可靠字节流的抽象是如何在跨互联网的通信中有用的,即使互联网本身只提供了“最大努力”(不可靠的)数据图的服务。

要完成本周的实验室,您将在单台计算机上的内存中实现一个提供这种抽象的对象。(您可能在CS110中也做过类似的事情。)字节被写在“输入”端,可以以相同的顺序从“输出”端读取。字节流是有限的:写入者可以结束输入,然后不能再写入更多的字节。当阅读器读到流末尾时,将达到“EOF”(文件末尾),不再读取字节。
字节流还将被流控制,以限制在任何给定时间的其内存消耗。对象用一个特定的“容量”初始化:它在任何给定点愿意存储在自己的内存中的最大字节数。字节流将限制写入器在任何给定时刻的写入量,以确保该流不会超过其存储容量。当读者读取字节并从流中提取字节时,作者可以写更多内容。字节流可用于在单个线程中使用——您不必担心并发写入器/读取器、锁定或竞争条件。

需要说明的是:字节流是有限的,但它几乎可以任意长 在作者结束输入并完成流之前。您的实现必须能够处理远远大于容量的流。容量限制在给定点内存中(写入但尚未读取)的字节数,但不限制流的长度。容量仅为一个字节的对象仍然可以携带t字节和t字节长的流只要写入器每次继续写一个字节并且阅读器在允许写入器写下一个字节之前读取每个字节。


我知道肯定会有hxd看的很懵 本来还有点感觉的 看了半天更懵了 哈哈哈 那我就用我的简单的寥寥几句来介绍一下吧

我们需要做的 就是在一个类中 就是我们的byte_stream中 实现字节的输入和输出 当然输入其实就是用一个数据结构来存储起来 输出就是返回 但是我们返回的不能够乱返回 需要一定的次序 且输入存储也不是无限存储 存储的大小 我们在初始化类的时候就需要指定

看了上面的话 如果之前刷过力扣或者算法题的小伙伴 是不是感觉仿佛有点熟悉的感觉了 哈哈哈 对的 其实就是你们想的那样 我们创建一个类 里面可以调用函数存储数据进去 也可以吐数据出来 只不过要按照一定的规则

pay attention: 我们不允许修改除了指定区域(增添函数部分、类private成员)其他以外的地方 不允许增加其他地头文件

好啦 如果我这样讲完 你已经开始迫不及待地打算大展拳脚的进去 开始你的实验的话 那就去吧 下面的部分是我对于每个函数 解释含义 和 一些注意点的温馨提醒 如果你做到中间发现总是出问题 那可以再回来看看下面部分


3、函数介绍(注意事项)


那下面就进入到函数介绍时间了 其实我写那么久 主要还是因为很多函数不知道要完成什么功能 注意 这里的函数 每一部分基本上都是完全分层 除了一些函数在注释中给了你提示 要完成其他功能的- -

注意 建议大家思考之后 实在卡住了再看下面的函数介绍 因为下面的函数介绍也含了很多的实现思路 如果思考了很久还是没思路的话 各位看下面的可以做思路启发

那我就一个个讲了
其实我写到现在我能够理解很多地方我之前不理解 为什么要这样做 现在能够理解了 对于一些地方 我会用计算机网络相关术语来解释函数实现
对了 我们数据结构存储是用string 因为上面只include了一个string 那我们就只用这个就行了 用其他的也可以 但其实只用一个string的话 效率也并不是很高 - - 大家可以在上面添加其他数据结构的头文件 比如#include <vector> #include<deque> 类似的

最下面我提供了两种方法 一种是刚开始写的 第二种是完成了后面的一两个Lab后回来改进的 用的是deque存储 这里思路介绍用stringdeque大同小异 只是deque处理string稍微复杂了那么一丢丢


void ByteStream(const size_t capacity)

ByteStream::ByteStream(const size_t capacity) {
     DUMMY_CODE(capacity); }

初始化我们数据最大地存储大小 对于string 则是限制size


size_t write(const string &data)

size_t ByteStream::write(const string &data) {
    DUMMY_CODE(data);return {
    };
}

注意 这里的功能是输入数据 也就是把数据放入的 但是你设置的最大大小只有那么多 如果你可用大小只剩下1了 但是放入的数据现在需要15的大小 我们则只放入1的大小 剩余的14全部丢弃
哪怕没有可用大小了 我们仍然可以输入数据 只是全部丢弃而已


string peek_output(const size_t len)

string ByteStream::peek_output(const size_t len) const {
    DUMMY_CODE(len);return {
    };
}

透视 哈哈 这样讲不是很好 但可以这样理解 返回我们接下来要输出的数据 即字符串 透视看的数据不计入已读数据大小


void pop_output(const size_t len)

void ByteStream::pop_output(const size_t len) {
     DUMMY_CODE(len); }

缓冲区假设为15的大小 例如路由器的缓存区为15 我们这里的抛弃数据 不是像数据来到 缓冲区已满而抛弃 而是主动抛弃 即把缓冲区前面的数据抛弃最大len大小 抛弃的数据算入已读数据


std::string read(const size_t len)

std::string ByteStream::read(const size_t len) {
    DUMMY_CODE(len);return {
    };
}

注意这里 功能是读取 但是读取的话 我们需要把读取的内容废弃 也就是从缓冲区扔出去 read之后 已读数据需要加上读取内容的大小


void end_input()
bool input_ended() const

void ByteStream::end_input() {
    }
bool ByteStream::input_ended() const {
     return {
    }; }

上面两个函数我就一起说 这两个函数不是我们主动调用的 也就是我们的函数里面不应该主动调用 而是我们的测试用例调用 调用end_input表示停止输入了 则如果测试用例仍然输入数据的话 测试用例那边会调用input_ended看是否结束了 我感觉我讲的也太详细了点 哈哈


size_t buffer_size() const
bool buffer_empty() const

size_t ByteStream::buffer_size() const {
     return bytes.size(); }
bool ByteStream::buffer_empty() const {
     return bytes.empty(); }
bool ByteStream::eof() const {
     return buffer_empty() && is_end; }

buffer_size是说明你的缓冲区还有多大 其实这个缓冲区不是说的输入的全部大小还剩下多少 而是你的数据还有多少可以输出的 比如路由器缓冲区全大小15 你的路由器数据里面放了10大小 调用这个函数则返回10 而不是5

buffer_empty结合上面的函数介绍 理解一下 不多说了
eof 是否到了文件末尾 对于输入的话 测试用例看input_end 对于输出数据的话 是看eof 注意哪怕缓冲区为0 仍然是可以输出的 只不过输出的数据为0而已 因为接下来仍然可以输入数据 然后再输出 后面还有点话 我就省下来不说了 因为说了 大家对于这个地方的理解就太少了 哈哈


size_t bytes_written() const
size_t bytes_read() const
size_t remaining_capacity() const

size_t ByteStream::bytes_written() const {
     return haswritten;}
size_t ByteStream::bytes_read() const {
     return hasread;}
size_t ByteStream::remaining_capacity() const {
     return maxsize - bytes.size(); }

对于bytes_writtenbytes_read 看名字即可 就是函数名字的意思 返回累积写入字数和累积读取字数 remaining_capacity才是 比如路由器大小15 缓冲区占了10 这个函数返回5 - -

好了 对于要编写的函数我就全部介绍完了 应该找不到比我介绍的还详细的博客了吧 哈哈 其实我介绍完了 对于各位已经难度小了很大一截了 我认为已经容易很多了 如果之前我看了这些函数的介绍 理解的比较清楚的话 应该花不到30分钟就可以编写调试完了


4、简易代码实现(byte_stream.cc byte_stream.hh)(string效率不高)


下面就是我的全部函数实现了 这个实现的时间超级长 因为stringerase效率因为不高 是一个连续的内容空间 还有string +=的效率也很低 所以就导致时间很长 对于没有一点点思路的好兄弟 可以先看一下string的实现

代码实现(byte_stream.hh)

#ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH
#define SPONGE_LIBSPONGE_BYTE_STREAM_HH#include <string>//! \brief An in-order byte stream.//! Bytes are written on the "input" side and read from the "output"
//! side. The byte stream is finite: the writer can end the input,
//! and then no more bytes can be written.
class ByteStream {
    private:std::string bytes = "";size_t maxsize = 0,hasread = 0,haswritten = 0;bool is_end = false;bool _error = false;  //!< Flag indicating that the stream suffered an error.public://! Construct a stream with room for `capacity` bytes.ByteStream(const size_t capacity);//! \name "Input" interface for the writer//!@{
    //! Write a string of bytes into the stream. Write as many//! as will fit, and return how many were written.//! \returns the number of bytes accepted into the streamsize_t write(const std::string &data);//! \returns the number of additional bytes that the stream has space forsize_t remaining_capacity() const;//! Signal that the byte stream has reached its endingvoid end_input();//! Indicate that the stream suffered an error.void set_error() {
     _error = true; }//!@}//! \name "Output" interface for the reader//!@{
    //! Peek at next "len" bytes of the stream//! \returns a stringstd::string peek_output(const size_t len) const;//! Remove bytes from the buffervoid pop_output(const size_t len);//! Read (i.e., copy and then pop) the next "len" bytes of the stream//! \returns a stringstd::string read(const size_t len);//! \returns `true` if the stream input has endedbool input_ended() const;//! \returns `true` if the stream has suffered an errorbool error() const {
     return _error; }//! \returns the maximum amount that can currently be read from the streamsize_t buffer_size() const;//! \returns `true` if the buffer is emptybool buffer_empty() const;//! \returns `true` if the output has reached the endingbool eof() const;//!@}//! \name General accounting//!@{
    //! Total number of bytes writtensize_t bytes_written() const;//! Total number of bytes poppedsize_t bytes_read() const;//!@}
};#endif // SPONGE_LIBSPONGE_BYTE_STREAM_HH

代码实现(byte_stream.cc)

#include "byte_stream.hh"// Dummy implementation of a flow-controlled in-memory byte stream.// For Lab 0, please replace with a real implementation that passes the
// automated checks run by `make check_lab0`.// You will need to add private members to the class declaration in `byte_stream.hh`template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {
    }using namespace std;ByteStream::ByteStream(const size_t capacity)
{
    maxsize = capacity;
}size_t ByteStream::write(const string &data)
{
    size_t size = (data.size() <= remaining_capacity()) ? data.size() : remaining_capacity();bytes += data.substr(0,size);haswritten += size;return size;
}//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const
{
    size_t size = (len <= buffer_size()) ? len : buffer_size();string str = bytes.substr(0,size);return str;
}//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) 
{
     size_t size = (len <= buffer_size()) ? len : buffer_size();bytes.erase(0,size);hasread += size;
}//! Read (i.e., copy and then pop) the next "len" bytes of the stream
//! \param[in] len bytes will be popped and returned
//! \returns a string
std::string ByteStream::read(const size_t len)
{
    size_t size = (len <= buffer_size()) ? len : buffer_size(); string ret = peek_output(size);pop_output(size);return ret;
}void ByteStream::end_input() {
    is_end = true;}bool ByteStream::input_ended() const {
     return is_end; }size_t ByteStream::buffer_size() const {
     return bytes.size(); }bool ByteStream::buffer_empty() const {
     return bytes.empty(); }bool ByteStream::eof() const {
     return buffer_empty() && is_end; }size_t ByteStream::bytes_written() const {
     return haswritten;}size_t ByteStream::bytes_read() const {
     return hasread;}size_t ByteStream::remaining_capacity() const {
     return maxsize - bytes.size(); }

4、编译运行 测试用例检验程序 (100% tests passed)


老样子 我们还是在sponge/build 里面make
如果编写好了 编译也通过了 想看一下的话 直接输入make check_lab0

下面是我最后的测试结果 100% tests passed
在这里插入图片描述


5、优化代码实现(byte_stream.cc byte_stream.hh)(deque效率较高)


代码实现(byte_stream.hh)

#ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH
#define SPONGE_LIBSPONGE_BYTE_STREAM_HH#include <string>
#include <deque>//! \brief An in-order byte stream.//! Bytes are written on the "input" side and read from the "output"
//! side. The byte stream is finite: the writer can end the input,
//! and then no more bytes can be written.
class ByteStream {
    private:std::deque<char> bytes{
    };size_t maxsize = 0,hasread = 0,haswritten = 0;bool is_end = false;bool _error = false;  //!< Flag indicating that the stream suffered an error.public://! Construct a stream with room for `capacity` bytes.ByteStream(const size_t capacity);//! \name "Input" interface for the writer//!@{
    //! Write a string of bytes into the stream. Write as many//! as will fit, and return how many were written.//! \returns the number of bytes accepted into the streamsize_t write(const std::string &data);//! \returns the number of additional bytes that the stream has space forsize_t remaining_capacity() const;//! Signal that the byte stream has reached its endingvoid end_input();//! Indicate that the stream suffered an error.void set_error() {
     _error = true; }//!@}//! \name "Output" interface for the reader//!@{
    //! Peek at next "len" bytes of the stream//! \returns a stringstd::string peek_output(const size_t len) const;//! Remove bytes from the buffervoid pop_output(const size_t len);//! Read (i.e., copy and then pop) the next "len" bytes of the stream//! \returns a stringstd::string read(const size_t len);//! \returns `true` if the stream input has endedbool input_ended() const;//! \returns `true` if the stream has suffered an errorbool error() const {
     return _error; }//! \returns the maximum amount that can currently be read from the streamsize_t buffer_size() const;//! \returns `true` if the buffer is emptybool buffer_empty() const;//! \returns `true` if the output has reached the endingbool eof() const;//!@}//! \name General accounting//!@{
    //! Total number of bytes writtensize_t bytes_written() const;//! Total number of bytes poppedsize_t bytes_read() const;//!@}
};#endif // SPONGE_LIBSPONGE_BYTE_STREAM_HH

代码实现(byte_stream.cc)

#include "byte_stream.hh"// Dummy implementation of a flow-controlled in-memory byte stream.// For Lab 0, please replace with a real implementation that passes the
// automated checks run by `make check_lab0`.// You will need to add private members to the class declaration in `byte_stream.hh`template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {
    }using namespace std;ByteStream::ByteStream(const size_t capacity)
{
    maxsize = capacity;
}size_t ByteStream::write(const string &data)
{
    size_t size = (data.size() <= remaining_capacity()) ? data.size() : remaining_capacity();string insert_bytes = data.substr(0,size);bytes.insert(bytes.end(),insert_bytes.begin(),insert_bytes.end());haswritten += size;return size;
}//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const
{
    size_t size = (len <= buffer_size()) ? len : buffer_size();string str;str.assign(bytes.begin(),bytes.begin()+size);return str;
}//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) 
{
     size_t size = (len <= buffer_size()) ? len : buffer_size();bytes.erase(bytes.begin(),bytes.begin()+size);hasread += size;
}//! Read (i.e., copy and then pop) the next "len" bytes of the stream
//! \param[in] len bytes will be popped and returned
//! \returns a string
std::string ByteStream::read(const size_t len)
{
    size_t size = (len <= buffer_size()) ? len : buffer_size(); string ret = peek_output(size);pop_output(size);return ret;
}void ByteStream::end_input() {
    is_end = true;}bool ByteStream::input_ended() const {
     return is_end; }size_t ByteStream::buffer_size() const {
     return bytes.size(); }bool ByteStream::buffer_empty() const {
     return bytes.empty(); }bool ByteStream::eof() const {
     return buffer_empty() && is_end; }size_t ByteStream::bytes_written() const {
     return haswritten;}size_t ByteStream::bytes_read() const {
     return hasread;}size_t ByteStream::remaining_capacity() const {
     return maxsize - bytes.size(); }

5、编译运行 测试用例检验程序 (100% tests passed)


下面是截图了 终于写完了 希望能够有一些帮助 那各位下一个Lab再见了
在这里插入图片描述

  相关解决方案