CS144(2023 Spring)Lab 0:networking warmup(环境搭建 webget bytestream)

news2024/11/24 13:51:00

文章目录

  • 前言
    • 其他笔记
    • 相关链接
  • 1. Set up GNU/Linux on your computer
  • 2. Networking by hand
  • 3. Writing a network program using an OS stream socket
    • 3.1 Linux配置
    • 3.2 C++规范
    • 3.3 Writing webget
      • 3.3.1 实现
      • 3.3.2 测试
  • 4. An in-memory reliable byte stream
    • 4.1 思路分析
    • 4.2 代码展示
    • 4.3 代码测试

前言

最近心情非常郁闷,搓一个CS144玩玩吧,正好2023 spring出新版了。。。CS144的头4个Lab(加上0是5个),一步步实现了一个TCP。在开始之前,我想贴一下Lab中的这句话:The lab documents aren’t “specifications”—meaning they’re not intended to be consumed in a one-way fashion. They’re written closer to the level of detail that a software
engineer will get from a boss or client.

这句话是说这个Lab的文档并不那么标准化,它看起来更像你的leader给你的需求文档,这话什么意思呢,这里我就不解释了,相信大家跟着这个Lab从头到尾做一遍就会有体会了(笑
在这里插入图片描述

其他笔记

Lab 0: networking warmup

相关链接

课程主页
lab 0

1. Set up GNU/Linux on your computer

在这里插入图片描述
官方推荐环境有vbox镜像、谷歌云,以及原生Ubuntu安装,我本来想下他那个镜像的就懒得配了,结果一看3G大,挂上魔法都只有100kb的龟速。。。还是用我的WSL吧,Ubuntu版本要求22.10,g++要求12.2

命令行运行它这一串就行了

sudo apt update && sudo apt install git cmake gdb build-essential clang \
clang-tidy clang-format gcc-doc pkg-config glibc-doc tcpdump tshark

这里点是
在这里插入图片描述
安装速度比下载镜像不知道高到哪里去了(21min是因为我挂着等截图,实际估计一两分钟)(
在这里插入图片描述
由于lab要求使用gcc12,而Ubuntu20的官方源没有gcc12,我选择将Ubuntu20直接更新到22,但是强烈不推荐,因为众所周知的原因,我在更新系统的途中耗时极多。。如图,花了得有几个小时。。建议去微软商店里面直接下载新的22。
在这里插入图片描述
安装好gcc12后,可以如图指定gcc各版本优先级以指定默认版本。
在这里插入图片描述

2. Networking by hand

这一节主要是叫你手动搭建网络感受一下里面的过程,头俩没啥好说的,2.3 Listening and connecting的比较有意思,是叫搭建一个双工通信:
首先我们在终端里跑一下netcat -v -l -p 9090
在这里插入图片描述
然后新开一个终端,在里面运行telnet localhost 9090
在这里插入图片描述
可以看到它建立了连接,这个时候我们回到刚才的终端,发现更新了:
在这里插入图片描述
我们随便输入一点东西,并切到隔壁去,会发现隔壁也能收到并显示:
在这里插入图片描述
在这里插入图片描述
这个操作在两边都能完成,可以看出我们建立了双工通信。

3. Writing a network program using an OS stream socket

Task3是叫用socket简单写一个网络编程。值得一提的是,Lab鼓励我们用上Modern C++,从代码上来看,Lab自己至少使用了C++20的内容,而我也会尽可能展示C++20乃至C++23的实现()
在这里插入图片描述

3.1 Linux配置

这一步我们先在linux里拉取仓库代码后编译,不过这个项目要求cmake 3.24.2以上 = =,我的是3.22.1,汗。。。又要更新,我直接更新到了3.27.0。
在这里插入图片描述
跟着流程走一遍编译:

git clone https://github.com/cs144/minnow
cd minnow
cmake -S . -B build
cmake --build build

发现报了俩错
在这里插入图片描述
把报错贴出来方便后来人搜索:

error: invalid return type ‘std::string’ {aka ‘std::__cxx11::basic_string’} of ‘constexpr’ function ‘static constexpr std::string ExpectationViolation::boolstr(bool)’
16 | static constexpr std::string boolstr( bool b ) { return b ? “true” : “false”; }
| ^~~~~~~

看一下报错说的是string不为constexpr类型,我记得constexpr string好像是C++20的内容,这里用的C++11,明显是编译器环境没整对,一查g++ -v,发现果然之前搞了gcc的默认忘改g++了,同样的操作软链接一下:
在这里插入图片描述
再跑一下编译,很成功:
在这里插入图片描述
然后我们还是把他推到我们现有的库里

git remote rm origin                 # 删除当前远程库
git remote add origin  + 远程仓库地址  # 连接到现有的自己的库
git branch -M main
git push -u origin main

然后在vs里导入这个仓库,不演示了。拿到手上先把构建项目的文件给加进gitignore里面:
在这里插入图片描述
我的工具链一般是VS上编辑完后,git到远程,然后wsl上拉取后编译运行。如果需要调试,就直接在VS上打断点在VS上运行调试,这个Lab有CMake,方便得多。

3.2 C++规范

在这里插入图片描述
在开始之前,我们来看一下他提到的一些规范要求:

  • 禁用动态内存分配(malloc&free、new&delete)
  • 禁用裸指针、如果要用指针就用智能指针,不过看提示说CS144不太用得到指针
  • 避免模板、线程、锁、虚函数,说CS144用不到这些
  • 避免使用C风格字符串和C的那一套str-函数,用std::string代替
  • 避免使用C风格cast,用static_cast代替,不过CS144一般用不到
  • 尽量使用const引用传参
  • 函数和变量能用const修饰则用const修饰
  • 避免使用全局变量,把变量限定在尽可能小的作用域里
  • 使用cmake --build build --target tidy获取代码建议,cmake --build build --target format去格式化

这几条都是比较常规的规范,没啥值得注意的。
在这里插入图片描述

然后他说Lab用类封装了各种各样的system function,并且叫你读一下socket.hhfile_descriptor.hh的公共接口,看它的描述,Socket是一种文件描述符,而TCPSocket是一种Socket。我们可以借助他的这个文档看,实际看实现就是逐层继承的关系。
继承关系
看一下接口,都是很常规的socket API,值得注意的一点是今年这里面有的API和往年的不一样!所以还是要以源码为准:
在这里插入图片描述

3.3 Writing webget

3.3.1 实现

终于要开始写代码了,在/apps/webget.cc里面写
在这里插入图片描述
首先实现这个getURL
在这里插入图片描述
其中注意发请求的时候要用\r\n,单纯\n是不够的,此外今年的FileDescriptor::read由直接返回读取的数据改成了传个string引用进去,我分析这么改的原因是为了与新的FileDescriptor::read( vector<unique_ptr<string>>& buffers )这个写入多值的函数形成API形式上的统一吧,不过这写着就丑陋一些了。

void get_URL( const string& host, const string& path )
{
  TCPSocket sock;
  sock.connect( Address( host, "http" ) );
  sock.write("GET " + path + " HTTP/1.1\r\n"    // 请求行
             "Host: " + host + "\r\n"           // 告知服务器主机名
             "Connection: close\r\n"            // 通知服务器关闭连接
             "\r\n");                           // 空行
  sock.shutdown( SHUT_WR ); // 关闭写端

  while ( !sock.eof() ) {   // 读取所有数据
    string tmp;
    sock.read( tmp );
    cout << tmp;
  }
  sock.close();
}

3.3.2 测试

我们在build文件夹下

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

在这里插入图片描述
可以看到成功返回并打印了一些信息!
接着继续在build下面跑make check_webget测试一下,这一步我最早出现了很奇葩的问题:
在这里插入图片描述
我把错误抠出来方便后来人搜索:

Test project /home/jmc/CS144/minnow/build
    Start 1: compile with bug-checkers
1/2 Test #1: compile with bug-checkers ........***Timeout  12.05 sec
[  6%] Built target minnow_debug
[ 18%] Built target util_sanitized
[ 25%] Built target minnow_sanitized
[ 29%] Built target minnow_testing_sanitized
[ 34%] Built target minnow_testing_debug
[ 45%] Built target util_debug
[ 47%] Building CXX object tests/CMakeFiles/byte_stream_capacity_sanitized.dir/byte_stream_capacity.cc.o
[ 50%] Linking CXX executable byte_stream_basics_sanitized
[ 52%] Building CXX object tests/CMakeFiles/byte_stream_two_writes_sanitized.dir/byte_stream_two_writes.cc.o
[ 54%] Building CXX object tests/CMakeFiles/byte_stream_one_write_sanitized.dir/byte_stream_one_write.cc.o
[ 59%] Building CXX object tests/CMakeFiles/byte_stream_stress_test_sanitized.dir/byte_stream_stress_test.cc.o
[ 59%] Building CXX object tests/CMakeFiles/byte_stream_many_writes_sanitized.dir/byte_stream_many_writes.cc.o
[ 61%] Building CXX object tests/CMakeFiles/byte_stream_stress_test.dir/byte_stream_stress_test.cc.o
[ 63%] Linking CXX executable byte_stream_basics
[ 65%] Built target byte_stream_basics_sanitized
[ 68%] Built target byte_stream_basics
[ 70%] Linking CXX executable byte_stream_capacity
[ 72%] Linking CXX executable byte_stream_one_write
[ 75%] Built target byte_stream_one_write
[ 77%] Linking CXX executable byte_stream_two_writes
[ 79%] Built target byte_stream_capacity
[ 81%] Linking CXX executable byte_stream_many_writes
[ 84%] Linking CXX executable byte_stream_stress_test
[ 86%] Linking CXX executable byte_stream_capacity_sanitized
[ 88%] Linking CXX executable byte_stream_many_writes_sanitized
[ 90%] Linking CXX executable byte_stream_two_writes_sanitized
[ 93%] Linking CXX executable byte_stream_stress_test_sanitized

    Start 2: t_webget
Failed test dependencies: compile with bug-checkers
2/2 Test #2: t_webget .........................***Not Run   0.00 sec

0% tests passed, 2 tests failed out of 2

Total Test time (real) =  12.06 sec

The following tests FAILED:
          1 - compile with bug-checkers (Timeout)
          2 - t_webget (Not Run)
Errors while running CTest
make[3]: *** [CMakeFiles/check_webget.dir/build.make:70:CMakeFiles/check_webget] 错误 8
make[2]: *** [CMakeFiles/Makefile2:1822:CMakeFiles/check_webget.dir/all] 错误 2
make[1]: *** [CMakeFiles/Makefile2:1829:CMakeFiles/check_webget.dir/rule] 错误 2
make: *** [Makefile:914:check_webget] 错误 2

研究半天,这个应该是性能问题,虚拟机性能不好,所以跑半天超时了,解决方法是多跑几遍,或者直接去etc/tests.cmake下面把超时时间改多一点,后面的也是同理:
在这里插入图片描述

在这里插入图片描述

4. An in-memory reliable byte stream

4.1 思路分析

这个task就是叫我们抽象出一个数据结构,可以在一边读一边写,同时用一个capacity限制一下缓冲区的大小,这本质是一个队列的抽象。今年的这个task相比往年的再抽象了一层专用于读与写的子类,比较有意思。

我们先分析一下这个框架,很显然,我们的底层需要维护一个什么数据结构,然后还有几个显然是标志位的东西,我们直接维护一个flg然后置位即可。
在这里插入图片描述

这个ByteStream的设计是有点类似于C++里基于流的IO的,和stringstream的功能差不多,都是实现基于char的流,不过bs和ss不同的是bs是FILO的,而ss是FIFO的,换言之我们的bs在这里像一个“滑动窗口”——-而且左右指针都始终向一个方向前进,这意味着如果我们像ss一样利用string这种简单的连续内存空间来维护我们的数据的话,将会造成左侧内存的浪费——写指针永远无法到达它以前到过的地方,因为它是单向运动的,此外它还无法实现完美转发,因为我们使用operator+=衔接串的话势必需要一次拷贝。

那么queue<char>如何呢?看起来它完美地实现了我们的需求:它是天然的FIFO数据结构,但是还是那个问题——它无法实现完美转发,我们在声明中就可以看到,push接受的参数是一个string值,而我们将string拆成char的这个过程势必涉及到拷贝,它是一个 O ( n ) O(n) O(n)的操作——这甚至还没有考虑零碎空间造成的开销。

那如果考虑完美转发呢?push收到的是连续空间,我们就直接完美转发就行了。这意味着我们需要维护一片一片的连续内存,很容易想到,我们需要维护一个queue<string>,对于每个收到的字符串,我们直接转发进这个队列就行了,这样可以让我们的push操作本身达到 O ( 1 ) O(1) O(1)时间复杂度,另外我们从测试程序可以看出,测试程序传给push的值本身就是个move来的右值,这意味着传参的过程也是 O ( 1 ) O(1) O(1)的,因此实际上我们的这个操作时间复杂度就是 O ( 1 ) O(1) O(1)
在这里插入图片描述
不过我们还需要考虑pop的实现,pop要求实现任意合法字符数量的弹出,一般思路是不断遍历并弹出队首,直到队首的长度不小于剩余需要遍历的数量的长度为止,然后用一个指针标记一下这个串这次遍历到的位置,下次就从这里开始遍历即可。因此我们需要维护队首字符串的起始位置,一种很优雅的实现是来自C++17的std::string_viewstd::string_view实质上就是维护了连续字符内存上的两个指针。当然,我们也可以直接维护队首字符串的起始位置坐标,性能其实应该差距不大,这里就展示一下string_view吧。

4.2 代码展示

// byte_stream.hh
#pragma once

#include <queue>
#include <stdexcept>
#include <string>
#include <string_view>

class Reader;
class Writer;

class ByteStream
{
protected:
  enum State { CLOSED, ERROR };
  uint64_t capacity_;
  uint64_t bytes_pushed_ {}; // 已写入的字节数
  uint64_t bytes_popped_ {}; // 已弹出的字节数

  unsigned char flag {};	// 0: normal, 1: closed, 2: error
  std::queue<std::string> buffer_data {};
  std::string_view buffer_view {};

public:
  explicit ByteStream( uint64_t capacity );

  // 提供ByteStream的 reader 和 writer 接口的辅助函数
  Reader& reader();
  const Reader& reader() const;
  Writer& writer();
  const Writer& writer() const;
};

class Writer : public ByteStream
{
public:
  void push( std::string data ) noexcept; // 在可用容量允许的范围内向流中写入数据

  void close() noexcept; // 关闭流,不允许再向流中写入数据
  void set_error() noexcept; // 流中出现错误,置位错误标志

  bool is_closed() const noexcept;      // 判断流是否已关闭
  uint64_t available_capacity() const noexcept; // 计算流中剩余可用容量
  uint64_t bytes_pushed() const noexcept;       // 计算流中已写入的字节数
};

class Reader : public ByteStream
{
public:
  std::string_view peek() const noexcept; // 返回流中下一个数据块的只读视图
  void pop( uint64_t len ) noexcept;      // 从流中弹出指定长度的数据块

  bool is_finished() const noexcept; // 判断流是否已关闭且所有数据块都已弹出
  bool has_error() const noexcept;   // 判断流是否出现错误

  uint64_t bytes_buffered() const noexcept; // 计算当前流中剩余的字节数
  uint64_t bytes_popped() const noexcept;   // 计算流中已弹出的字节数
};

/*
 * read: A (provided) helper function thats peeks and pops up to `len` bytes
 * from a ByteStream Reader into a string;
 */
void read( Reader& reader, uint64_t len, std::string& out );
// byte_stream.cc
#include <stdexcept>

#include "byte_stream.hh"

using namespace std;

ByteStream::ByteStream( uint64_t capacity ) : capacity_( capacity ) {}

void Writer::push( string data ) noexcept
{
  auto len = min( data.size(), available_capacity() );	// 确定可写入的数据长度
  if ( len == 0 ) { // 如果可写入的数据长度为0,说明已经写满了,返回
    return;
  } else if ( len < data.size() ) { // 如果可写入的数据长度小于 data 的长度,说明只能写入部分数据
    data.resize( len );             // 将 data 的长度截断为可写入的长度
  }
  // 将 data 写入到 buffer 中
  buffer_data.push( move( data ) );
  if ( buffer_data.size() == 1)  // 写入前为空时需要更新 buffer_view
    buffer_view = buffer_data.front();   
  // 更新已写入的数据长度
  bytes_pushed_ += len;
}

void Writer::close() noexcept
{
  flag |= ( 1 << CLOSED );
}

void Writer::set_error() noexcept
{
  flag |= ( 1 << ERROR );
}

bool Writer::is_closed() const noexcept
{
  return flag & ( 1 << CLOSED );
}

uint64_t Writer::available_capacity() const noexcept
{
  return capacity_ - reader().bytes_buffered();
}

uint64_t Writer::bytes_pushed() const noexcept
{
  return bytes_pushed_;
}

string_view Reader::peek() const noexcept
{
  return buffer_view;
}

bool Reader::is_finished() const noexcept
{
  return writer().is_closed() && ( bytes_buffered() == 0 );
}

bool Reader::has_error() const noexcept
{
  return flag & ( 1 << ERROR );
}

void Reader::pop( uint64_t len ) noexcept
{
  if ( len > bytes_buffered() ) {
    return;
  }
  // 更新已弹出的数据长度
  bytes_popped_ += len;

  // 将 buffer 中的数据弹出
  while ( len > 0 ) {
    if ( len >= buffer_view.size() ) {
      len -= buffer_view.size();
      buffer_data.pop();
      buffer_view = buffer_data.front(); // 最开始就保证了 buffer_data 不为空
    } else {
      buffer_view.remove_prefix( len );
      len = 0;
    }
  }
}

uint64_t Reader::bytes_buffered() const noexcept
{
  return writer().bytes_pushed() - bytes_popped();
}

uint64_t Reader::bytes_popped() const noexcept
{
  return bytes_popped_;
}

4.3 代码测试

build下运行make check0,可以看到我的电脑上跑了10.90Gbit/s的速度,这个是和电脑有关的,我的电脑比较拉,和运气也有关系,当然和算法关系更大。
在这里插入图片描述

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

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

相关文章

《C和指针》笔记14: 作用域和存储类型总结(例子说明)

文章目录 题目答案解释总结 本文是作用域和存储类型的总结&#xff0c;以一个例子来说明&#xff0c;如果不看解释可以很直接地回答每一条语句的作用域和存储类型&#xff0c;那么说明已经很熟练地掌握这个知识点了。 关于作用域和存储类型可以参考我前面的博客&#xff1a; …

LeetCode-56-合并区间

题目描述&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 可以使用 LinkedList&#xff0c;…

Cadence+硬件每日学习十个知识点(46)23.8.26 (运算放大器)

文章目录 1.电压跟随器的输入和输出是一样的&#xff0c;但是输入的是电压带有高阻抗&#xff0c;输出的电压带有低阻抗。2.比较器的迟滞&#xff08;这个电阻&#xff09;3.运放的压摆率4.运放-轨到轨5.输入失调电压Vos&#xff08;选一个低的器件就行&#xff0c;对于5V&…

Batbot电力云平台在智能配电室中的应用

智能配电室管理系统是物联网应用中的底层应用场景&#xff0c;无论是新基建下的智能升级&#xff0c;还是双碳目标下的能源管理&#xff0c;都离不开智能配电运维对传统配电室的智慧改造。Batbot智慧电力&#xff08;运维&#xff09;云平台通过对配电室关键电力设备部署传感器…

【学习笔记】求解线性方程组的G-S迭代法

求解线性方程组的G-S迭代法 // 运行不成功啊function [x,k,index] Gau_Seid(A,b,ep,it_max) % 求解线性方程组的G-S迭代法&#xff0c;其中 % A为方程组的系数矩阵 % b为方程组的右端项 % ep为精度要求&#xff0c;省缺为1e-5 % it_max为最大迭代次数&#xff0c;省缺为100 % …

1. 深度学习介绍

1.1 AI地图 ① 如下图所示&#xff0c;X轴是不同的模式&#xff0c;最早的是符号学&#xff0c;然后概率模型、机器学习。Y轴是我们想做什么东西&#xff0c;感知是我了解这是什么东西&#xff0c;推理形成自己的知识&#xff0c;然后做规划。 ② 感知类似我能看到前面有个屏…

微前端开发

微前端介绍 微前端的概念是由ThoughtWorks在2016年提出的&#xff0c;它借鉴了微服务的架构理念&#xff0c;核心在于将一个庞大的前端应用拆分成多个独立灵活的小型应用&#xff0c;每个应用都可以独立开发、独立运行、独立部署&#xff0c;再将这些小型应用融合为一个完整的…

【大数据】Linkis:打通上层应用与底层计算引擎的数据中间件

Linkis&#xff1a;打通上层应用与底层计算引擎的数据中间件 1.引言2.背景3.设计初衷4.技术架构5.业务架构6.处理流程7.如何支撑高并发8.用户级隔离度和调度时效性9.总结 Linkis 是微众银行开源的一款 数据中间件&#xff0c;用于解决前台各种工具、应用&#xff0c;和后台各种…

【git进阶使用】 告别只会git clone 学会版本控制 ignore筛选 merge冲突等进阶操作

git使用大全 基本介绍git 快速上手一 环境安装&#xff08;默认已安装&#xff09;二 远程仓库克隆到本地1 进入rep文件夹目录2 复制远程仓库地址3 git clone克隆仓库内容到本地4 修改后版本控制4.1 修改文件4.2 git status查看版本库文件状态4.3 git add将文件加入版本库暂存区…

NEOVIM学习笔记

GitHub - blogercn/nvim-config: A pretty epic NeoVim setup 一直使用vim&#xff0c;每次到了新公司都要配置半天&#xff0c;而且常常配置失败&#xff0c;很多插件过期不好用。偶然看到别人的NEO VIM&#xff0c;就试着用了一下&#xff0c;感觉还不错。 用来开发和阅读C代…

使用树莓派Pico、DHT11和SSD1306搭建一个温度湿度计(只使用官方库,以及官方案例代码的错误之处和解决方案)

最近想树莓派 Pico、DHT11 温湿度传感器和 SSD1306 OLED 屏幕做一个温度湿度计&#xff0c;树莓派官方案例也分别有这两个设备的案例&#xff0c;我就想做个简单的温度湿度计作为学习微控制器的开始&#xff0c;结果遇到了一个大坑&#xff0c;所以写本文记录一下整个过程。 本…

[完美解决]Vue项目运行时出现this[kHandle] = new _Hash(algorithm, xofLen)

vue项目运行bug解决办法 一、问题内容二、问题出现的原因三、解决方法1、方法一(推荐)2、方法二(可以解决&#xff0c;但不太推荐) 一、问题内容 在github寻找一些vue项目clone到本地时候&#xff0c;npm i没有问题&#xff0c;但是npm run serve 或者npm run dev的时候会出现…

计算机毕设 基于机器学习的餐厅销量预测 -大数据 python

文章目录 0 前言餐厅销量预测模型简介2.ARIMA模型介绍2.1自回归模型AR2.2移动平均模型MA2.3自回归移动平均模型ARMA 三、模型识别四、模型检验4.1半稳性检验(1)用途(1)什么是平稳序列?(2)检验平稳性 ◆白噪声检验(纯随机性检验)(1)用途(1)什么是纯随机序列?(2)检验纯随机性 五…

LLM本地知识库问答系统(一):使用LangChain和LlamaIndex从零构建PDF聊天机器人指南

随着大型语言模型&#xff08;LLM&#xff09;&#xff08;如ChatGPT和GPT-4&#xff09;的兴起&#xff0c;现在比以往任何时候都更容易构建比普通熊更智能的智能聊天机器人&#xff0c;并且可以浏览堆积如山的文档&#xff0c;为您的输入提供准确的响应。 在本系列中&#xf…

基于微信小程序的汽车租赁系统的设计与实现ljx7y

汽车租赁系统&#xff0c;主要包括管理员、用户二个权限角色&#xff0c;对于用户角色不同&#xff0c;所使用的功能模块相应不同。本文从管理员、用户的功能要求出发&#xff0c;汽车租赁系统系统中的功能模块主要是实现管理员后端&#xff1b;首页、个人中心、汽车品牌管理、…

LAMP介绍与配置

一.LAMP 1.1.LAMP架构的组成 CGI&#xff08;通用网关接口&#xff09;和FastCGI&#xff08;快速公共网关接口&#xff09;都是用于将Web服务器与后端应用程序&#xff08;如PHP、Python等&#xff09;进行交互的协议/接口。 特点 CGI FastCGI 运行方式 每个请求启动…

【C语言】2023.8.27C语言入学考试复盘总结

前言 本篇文章记录的是对于2023年8月27日的 C语言 的入学考试的整理总结 成绩&#xff1a;220/240 题目&#xff1a;9/12 错题整理 首先先对于我没做出来的三道题做一个整理 错题1&#xff1a;7-4 分段函数PLUS 题干 以下是一个二元分段函数&#xff0c;请你根据所给的函…

列式存储引擎-内核机制-Parquet格式

列式存储引擎-内核机制-Parquet格式 Parquet是一种开源的列式存储结构&#xff0c;广泛应用于大数据领域。 1、数据模型和schema Parquet继承了Protocol Buffer的数据模型。每个记录由一个或多个字段组成。每个字段可以是atomic字段或者group字段。Group字段包含嵌套的字段&…

软件工程(九) UML顺序-活动-状态-通信图

顺序图和后面的一些图,要求没有用例图和类图那么高,但仍然是比较重要的,我们也需要按程度去了解。 1、顺序图 顺序图(sequence diagram, 顺序图),顺序图是一种交互图(interaction diagram),它强调的是对象之间消息发送的顺序,同时显示对象之间的交互。 下面以一个简…

Python WEB框架之FastAPI

Python WEB框架之FastAPI 今天想记录一下最近项目上一直在用的Python框架——FastAPI。 个人认为&#xff0c;FastAPI是我目前接触到的Python最好用的WEB框架&#xff0c;没有之一。 之前也使用过像Django、Flask等框架&#xff0c;但是Django就用起来太重了&#xff0c;各种…