CS 144 Lab Zero

news2024/11/27 0:37:10

CS 144 Lab Zero

  • 环境搭建
  • 使用socket写一个网络程序
  • In-memory reliable byte stream


对应课程视频: 【计算机网络】 斯坦福大学CS144课程

Lab 0 对应的PDF: Lab Checkpoint 0: networking warmup

Lab 0 会省去Telnet部分内容。


环境搭建

  • Run Ubuntu version 22.10, then install the required packages:
sudo apt update && sudo apt install git cmake gdb build-essential clang \
clang-tidy clang-format gcc-doc pkg-config glibc-doc tcpdump tshark

使用socket写一个网络程序

在IP层,数据包传输的原则是“best-effort”,即尽最大努力传输数据包,但不保证一定送达。数据包可能会丢失、重传、出错、乱序到达。把这种混乱的数据包变成可靠的字节流,则是TCP传输层的责任。

本节我们将写一个“webget”程序,创建一个TCP stream socket,去和一个web server建立连接。你可以认为你的socket是一个双向的可靠的字节流传输服务,这种可靠性是TCP协议所保证的。

git clone https://gitee.com/DaHuYuXiXi/cs144-sponge.git

cd cs144-sponge
mkdir build
cd build

cmake ..
make

实验指导书建议我们使用Modern C++的特性,来写这个实验。并建议我们使用git来管理项目。

建议仔细阅读一下文档:

  1. https://cs144.github.io/doc/lab0
  2. FileDescriptor, Socket, TCPSocket, and Address classes

各个类的继承关系如下:

在这里插入图片描述
看上去比较重要的是TCPSocket这个类,读完文档之后,我们就可以去实现webget程序了,代码量预计 10 行左右,位于apps/webget.cc,实现代码时务必借助 libsponge 中的 TCPSocketAddress 类来完成。

需要注意的是

  • HTTP 头部的每一行末尾都是以\r\n结尾,而不是\n

  • 需要包含Connection: close 的HTTP头部,以指示远程服务器在处理完当前请求后直接关闭。

  • 除非获取到EOF,否则必须循环从远程服务器读取信息。

  • 因为网络数据的传输可能断断续续,需要多次 read。

具体代码实现如下:

void get_URL(const string &host, const string &path) {
    // You will need to connect to the "http" service on
    // the computer whose name is in the "host" string,
    // then request the URL path given in the "path" string.

    // Then you'll need to print out everything the server sends back,
    // (not just one call to read() -- everything) until you reach
    // the "eof" (end of file).
    TCPSocket socket;
    socket.connect(Address(host, "http"));
    // message template
    string messages;
    messages += "GET " + path + " HTTP/1.1\r\n";
    messages += "Host: " + host + "\r\n";
    messages += "Connection: close\r\n\r\n";
    // write message
    socket.write(messages);
    cout << "Message: \r" << messages;
    while (!socket.eof()) {
        cout << socket.read();
    }
    socket.close();
}

实现完之后,在/biuld目录下,构建,并运行

make
./apps/webget cs144.keithw.org /hello

它的行为应该和上述2.1小节的行为保持一致。

最后它应该能够通过测试

make check_webget

在这里插入图片描述


In-memory reliable byte stream

要求

  • 字节流可以从写入端写入,并以相同的顺序,从读取端读取
  • 字节流是有限的,写者可以终止写入。而读者可以在读取到字节流末尾时,产生EOF标志,不再读取。
  • 所实现的字节流必须支持流量控制,以控制内存的使用。当所使用的缓冲区爆满时,将禁止写入操作。直到读者读取了一部分数据后,空出了一部分缓冲区内存,才让写者写入。
  • 写入的字节流可能会很长,必须考虑到字节流大于缓冲区大小的情况。即便缓冲区只有1字节大小,所实现的程序也必须支持正常的写入读取操作。

在单线程环境下执行,因此不用考虑各类条件竞争问题。

这是在内存中的有序可靠字节流,接下来的实验会让我们在不可靠网络中实现一个这样的可靠字节流,而这便是传输控制协议(Transmission Control Protocol,TCP)

以下是实现的代码:

  • byte_stream.hh
#include <deque>
#include <string>
#include <vector>

//!  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:
    // Your code here -- add private members as necessary.

    // Hint: This doesn't need to be a sophisticated data structure at
    // all, but if any of your tests are taking longer than a second,
    // that's a sign that you probably want to keep exploring
    // different approaches.

    std::deque<char> buffer_;
    size_t capacity_;
    bool is_ended_, is_eof_;
    size_t bytes_written_, bytes_read_;

    bool _error{};  //!< Flag indicating that the stream suffered an error.

  public:
    //! Construct a stream with room for `capacity` bytes.
    ByteStream(const size_t capacity);
    //! Write a string of bytes into the stream. Write as many
    //! as will fit, and return how many were written.
    size_t write(const std::string &data);
    //! Write one character into the stream.
    bool write_char(char datum);
    //! \returns the number of additional bytes that the stream has space for
    size_t remaining_capacity() const;
    //! Signal that the byte stream has reached its ending
    void end_input();
    //! Indicate that the stream suffered an error.
    void set_error() { _error = true; }
    //! Peek at next "len" bytes of the stream
    std::string peek_output(const size_t len) const;
    //! Remove bytes from the buffer
    void pop_output(const size_t len);
    //! Read (i.e., copy and then pop) the next "len" bytes of the stream
    std::string read(const size_t len);
    //! \returns `true` if the stream input has ended
    bool input_ended() const;
    //! \returns `true` if the stream has suffered an error
    bool error() const { return _error; }
    //! \returns the maximum amount that can currently be read from the stream
    size_t buffer_size() const;
    //! \returns `true` if the buffer is empty
    bool buffer_empty() const;
    //! \returns `true` if the output has reached the ending
    bool eof() const;
    //! Total number of bytes written
    size_t bytes_written() const;
    //! Total number of bytes popped
    size_t bytes_read() const;
};
  • byte_stream.cc
#include "byte_stream.hh"

#include <iostream>

// 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`

using namespace std;

ByteStream::ByteStream(const size_t capacity)
    : buffer_()
    , capacity_(capacity)
    , is_ended_(false)
    , is_eof_(false)
    , bytes_written_(0)
    , bytes_read_(0)
    , _error(false) {}

size_t ByteStream::write(const string &data) {
    size_t write_amount = remaining_capacity() < data.length()
                              ? remaining_capacity()
                              : data.length();  // min(remaining_capacity(), data.length());
    for (size_t i = 0; i < write_amount; i++) {
        buffer_.push_back(data[i]);
    }
    bytes_written_ += write_amount;
    return write_amount;
}

bool ByteStream::write_char(char datum) {
    if (remaining_capacity() == 0)
        return false;
    buffer_.push_back(datum);
    bytes_written_++;
    return true;
}

//! \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 peek_length = len < buffer_size() ? len : buffer_size();
    string out_string(peek_length, ' ');
    for (size_t i = 0; i < peek_length; i++) {
        out_string[i] = buffer_[i];
    }
    return out_string;
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
    size_t pop_length = len < buffer_size() ? len : buffer_size();
    for (size_t i = 0; i < pop_length; i++)
        buffer_.pop_front();
    bytes_read_ += pop_length;
    if (is_ended_ && buffer_empty())
        is_eof_ = true;
}

//! 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) {
    string out = peek_output(len);
    pop_output(len);
    return out;
}

void ByteStream::end_input() {
    if (buffer_.empty())
        is_eof_ = true;
    is_ended_ = true;
}

bool ByteStream::input_ended() const { return is_ended_; }
size_t ByteStream::buffer_size() const { return buffer_.size(); }
bool ByteStream::buffer_empty() const { return buffer_.empty(); }
bool ByteStream::eof() const { return is_eof_; }
size_t ByteStream::bytes_written() const { return bytes_written_; }
size_t ByteStream::bytes_read() const { return bytes_read_; }
size_t ByteStream::remaining_capacity() const { return capacity_ - buffer_.size(); }

实现完之后,使用如下命令进行测试:

cd build
make
make check_lab0 

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/759984.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于linux下的高并发服务器开发(第二章)- 2.3 进程创建

/*#include <sys/types.h>#include <unistd.h>pid_t fork(void)函数的作用&#xff1a;用于创建子进程返回值&#xff1a;fork()的返回值会返回两次。一次是在父进程中&#xff0c;一次是在子进程中在父进程中返回创建的子进程的ID&#xff0c;在子进程中返回0如何…

《微服务架构设计模式》第十三章 微服务架构的重构策略

微服务架构的重构策略 一、重构到微服务需要考虑的问题1、为什么重构2、重构形式3、重构策略 二、设计服务与单体的协作方式三、总结 一、重构到微服务需要考虑的问题 1、为什么重构 单体地狱造成的业务问题&#xff1a; 交付缓慢充满故障的软件交付可扩展性差 2、重构形式 …

itheima苍穹外卖项目学习笔记--Day6: 微信登录 / 商品浏览

Day6 a. HttpClientGET方式请求&#xff1a;POST方式请求&#xff1a; b. 微信登录 a. HttpClient HttpClient 是Apache Jakarta Common 下的子项目&#xff0c;可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包&#xff0c;并且它支持 HTTP 协议最新…

chrome插件reading-time开发

本插件开发文档翻译于Google官方文档Chrome Extensions Tutorial: Reading time - Chrome Developers 一、reading-time reading-time项目的功能是 将预期的阅读时间添加到任何Chrome extension 和 Chrome Web Store documentation 页面里面 通过这个项目&#xff0c;我们可以…

拓扑序列及其排序

目录 一、拓扑序列及其排序的相关概念拓扑序列的定义拓扑序列的性质出度、入度、度数拓扑排序 二、拓扑序列及其排序的应用有向图的拓扑序列代码实现 一、拓扑序列及其排序的相关概念 拓扑序列的定义 拓扑序列是对一个 有向无环图(DAG)&#xff08;也称为拓扑图&#xff09;而…

SpringBoot整合gRPC -- 简单明了

项目结构 pom引入(parent中引入即可) <properties><net-devh-grpc.version>2.14.0.RELEASE</net-devh-grpc.version><os-maven-plugin.version>1.6.0</os-maven-plugin.version><protobuf-maven-plugin.version>0.5.1</protobuf-mave…

Python绘制一个圆

代码 import matplotlib.pyplot as plt import numpy as npradius 10 # 半径的长度 center np.array([0,0]) # 圆心的坐标 t np.linspace(0, 2 * np.pi, 100) x center[0] radius * np.cos(t) y center[1] radius * np.sin(t)plt.figure(figsize(5,5)) plt.plot(x,y) p…

C# Modbus通信从入门到精通(5)——Modbus RTU(0x03功能码)

1、03(0x03)读保持寄存器 使用该功能码能从远程地址中读取1到125个保持寄存器的值,每个保持寄存器都占两个字节,读取的保持寄存器数量由主站读取时指定。 2、发送报文格式 更详细的格式如下: 从站地址+功能码+起始地址高位+起始地址低位+寄存器数量高位+寄存器数量低位+…

Redis进阶源码 - 主流程源码

redis底层是C语言编写的一个可执行程序&#xff0c;主方法为server.c 中main方法 主要包括&#xff1a;从初始化 到 EventLoop整个流程源码内容介绍 画的分析图太大了&#xff0c;需要的直接下载看吧.....想提醒大家的是&#xff0c;看源码时一定要带着目标去看&#xff0c;比…

《微服务架构设计模式》第十一章 开发面向生产环境的微服务应用

内容总结自《微服务架构设计模式》 开发面向生产环境的微服务应用 一、开发安全的服务1、安全性概述2、单体应用安全性3、微服务应用安全性 二、如何使用外部化配置模式三、如何使用可观测性模式四、使用微服务基底模式开发服务1、使用微服务基地2、从微服务到服务网格 五、总结…

前端开发面试题及答案整理(合集)

前端开发面试题及答案 1、对Web标准以及W3C的理解与认识? 答&#xff1a;标签闭合、标签小写、不乱嵌套、提高搜索机器人搜索几率、使用外链CSS和JS脚本、结构行为表现的分离、文件下载与页面速度更快、内容能被更多的用户所访问、内容能被更广泛的设备所访问、更少的代码和组…

2023年NOC决赛-加码未来编程赛项决赛模拟题-Python模拟题--卷1

第一题: 小码君在和朋友们玩密室逃脱。他们被困在一个封闭的房间中,只有破解密码,才能逃出密室。密码提示如下:“将 1 到 100 以内,能被 7 整除的数的个数以及这些数累加起来的和拼在一起即是打开房门的密码” 。请帮小码君算出密码。(注:只需显示表示结果的纯数字,不…

动手学深度学习——softmax回归的从零开始(代码详解)

目录 1. softmax回归的从零开始实现1.1 初始化模型参数1.2 定义softmax操作1.3 定义模型1.4 定义损失函数1.5 分类精度1.6 训练1.7 预测1.8 小结 1. softmax回归的从零开始实现 引入Fashion-MNIST数据集&#xff0c; 并设置数据迭代器的批量大小为256。 import torch from IP…

快速实现单数据表编辑

目录 1 简介 2 准备电子表格 3 初始化 4 根据电子表格生成数据库表并导入数据 5 使刚创建的表显示在待编辑列表中 6 开始编辑单表 7 一段代码用于实现中文字段名、调整列宽、只读字段、隐藏字段 1 简介 单数据表是最容易实现的表。由于不需要从零开始一个一个地增加字段…

pwm呼吸灯

文章目录 一、呼吸灯二、代码实现三、引脚分配 一、呼吸灯 呼吸灯是指灯光在微电脑的控制之下完成由亮到暗的逐渐变化&#xff0c;使用开发板上的四个led灯实现1s间隔的呼吸灯。 二、代码实现 c module pwm_led( input clk ,input rst_n ,output reg [3:0] led ); …

当你输入URL并按下回车后,发生了什么?

大概发生了八步的事情&#xff1a; URL解析->DNS解析->服务器建立连接->发送HTTP请求->服务器处理请求->服务器响应->接收和渲染页面->关闭连接 URL解析 URL&#xff0c;统一资源定位符&#xff0c;是用来表示从互联网上得到的资源位置和访问这些资源的…

ARM DynamIQ简介

DynamIQ是ARM一个新的底层solution&#xff0c;用于连接在一个芯片上的不同core&#xff0c;将多个core连接起来。 有了DynamIQ&#xff0c;我们可以将不同类型的core放到一个cluster中。比如&#xff0c;将性能高的core&#xff0c;和功耗低的core放进一个cluster&#xff0c;…

【uni-app】常用图标、头像汇总

在做小程序的时候&#xff0c;有时候需要各种图标和头像素材&#xff0c;而百度一堆基本都是收费的。所以&#xff0c;整理一些免费好用的图标&#xff0c;头像库&#xff1a; 1、iconfont-阿里巴巴矢量图标库 基本上常用的矢量图标&#xff0c;在这儿都有&#xff0c;而且可…

基于SSM框架的汽车在线销售系统设计与实现

博主介绍&#xff1a;专注于Java技术领域和毕业项目实战。专注于计算机毕设开发、定制、文档编写指导等&#xff0c;对软件开发具有浓厚的兴趣&#xff0c;工作之余喜欢钻研技术&#xff0c;关注IT技术的发展趋势&#xff0c;感谢大家的关注与支持。 技术交流和部署相关看文章…

Eureka的使用手册

一、导入依赖&#xff08;服务端和客户端导入的依赖不一样&#xff09; 服务端&#xff1a; <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependenc…