CS144 Lab Checkpoint 0: networking warm up

Set up GNU/Linux on your computer

我用的是Ubuntu,按照指导书上写的输入如下命令安装所需的软件包:

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

Networking by hand

主要任务包括:检索网页和发送电子邮件,这两者都依赖一种可靠的双向字节流

Fetch a Web page

没有账号密码,所以这部分只做记录。

Telnet是一种应用层协议,使用于互联网及局域网中,使用虚拟终端的形式,提供双向、以文字字符串为主的命令行接口交互功能。

HTTP协议(Hyper Text Transfer Trotocol)从万维网传输超文本到本地浏览器的传送协议。

bash 复制代码
telnet cs144.keithw.org http

建立连接

HTTP协议的GET请求,从指定的资源请求数据。

bash 复制代码
GET /hello HTTP/1.1

告诉服务器请求资源的URL

bash 复制代码
Host: cs144.keithw.org

告诉服务器URL的主机部分

bash 复制代码
Connection: close

关闭连接。

Send yourself an email

简单邮件发送协议(英语:SimpleMailTransferProtocol,缩写:SMTP)可用在发送和接收电子邮件的信息,但SMTP通常用作发送电子邮件信息,而不是接收。

bash 复制代码
telnet 148.163.153.234 smtp

Listening and connecting

netcat用于任意 TCP 与 UDP 连接和侦听:

-v 选项表示"verbose mode"(详细模式)

-l 选项表示"listen mode"(监听模式)。

-p 选项表示"port"或"local port"(本地端口)。

bash 复制代码
netcat -v -l -p 9090 

Writing a network program using an OS stream socket

目标 :使用Linux以及大多数其他操作系统都提供的功能:创建双向字节流、一个在自己的主机上运行,另一个在Internet的另一台主机上运行。

这个特性被叫做流套接字,但是Internet并不提供可靠的字节流服务,Internet做的唯一事情就是尽最大努力将数据报文发送到目的地。

本实验要实现一个名为"webget"的程序,创建一个TCP流套接字,连接到Web服务器并获取页面。

Let's get started---fetching and building the starter code

这里的编译需要注意C++用的是C++20,我的默认C++是17会报错。

cpp 复制代码
cmake -S .-B build #-S指定源代码目录 -B指定生成目录
cmake --build build # --build指定构建目录

Modern C++: mostly safe but still fast and low-level

  1. 不使用malloc()free()
  2. 不使用newdelete
  3. 不要使用裸指针,必要的时候使用智能指针 unique_ptr 或者 shared_ptr
  4. 避免使用模板、线程、锁和虚函数;
  5. 避免使用C风格的字符串,使用std::string
  6. 避免使用C风格的强制转换,如果必须使用,使用C++的static_cast
  7. 最好使用const引用传递参数 e.g.:const Address & address;
  8. 使每个变量都变const,除非它能被改变;
  9. 使每个函数都变const,除非它需要改变对象;
  10. 避免使用全局变量,让每个变量尽可能小的作用域;

Writing webget

实验指导书里说要用到TCPSocket和Address 类

Hint:

在HTTP中每行必须是以\r\n结尾。

在客户端请求包含"Connection: close",当客户端读取来自服务器端的"EOF",这表示服务器端已经回复完成。

确保读取并打印服务器的所有输出,直到套接字到达"EOF"(文件末尾),一次read调用是不够的。

大约十行代码。

lab0用到的函数在下面的网站都能查到。
官方文档

cpp 复制代码
void get_URL( const string& host, const string& path )
{
  cerr << "Function called: get_URL(" << host << ", " << path << ")\n";
  // cerr << "Warning: get_URL() has not been implemented yet.\n";
  // 创建TCPSocket
  TCPSocket sock;
  sock.connect( Address( host, "http" ) );
  // 构造 HTTP GET 请求
  string request = "GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n";
  sock.write( request );
  // 接收响应
  string res;
  while ( !sock.eof() ) {
    sock.read( res );
    cout << res;
  }
  sock.close();
}

在根目录下输入cmake --build build --target check_webget

结果如下:

An in-memory reliable byte stream

实验要求:

  1. 字节在输入端写入,并以相同顺序在输出端被读取;字节流是有限的,writer可以停止写入,reader在读到EOF后不再读入。
  2. 字节流需要流控制,容量将限制writer能够在给定时刻最多写入的数据量,当reader读取数据时,writer可以写的更多。(所以网上很多方案选择双端队列解决,当然deque可能会造成更大的内存开销,姑且这么实现)。

byte_stream.hh

cpp 复制代码
class ByteStream
{
public:
  explicit ByteStream( uint64_t capacity );

  // Helper functions (provided) to access the ByteStream's Reader and Writer interfaces
  Reader& reader();
  const Reader& reader() const;
  Writer& writer();
  const Writer& writer() const;

  void set_error() { error_ = true; };       // Signal that the stream suffered an error.
  bool has_error() const { return error_; }; // Has the stream had an error?

protected:
  // Please add any additional state to the ByteStream here, and not to the Writer and Reader interfaces.
  uint64_t capacity_;
  bool error_ {};

  // my code here
  bool closed = false;
  uint64_t total_bytes_pushed = 0;
  uint64_t total_bytes_poped = 0;
  std::deque<char> buffer = {};
};

byte_stream.cc

cpp 复制代码
#include "byte_stream.hh"

using namespace std;

ByteStream::ByteStream( uint64_t capacity ) : capacity_( capacity ) {}
// 返回stream是否关闭
bool Writer::is_closed() const
{
  // Has the stream been closed?
  // Your code here.
  return closed;
}
// Writer将数据放入stream中
void Writer::push( string data )
{
  // Your code here.
  // (void)data;
  uint64_t len = data.length();
  if ( len > capacity_ - buffer.size() ) {
    len = capacity_ - buffer.size();
  }
  for ( uint64_t i = 0; i < len; ++i ) {
    buffer.push_back( data[i] );
    total_bytes_pushed++;
  }
  return;
}
// 关闭stream
void Writer::close()
{
  closed = true;
  // Your code here.
}
// 返回capacity - 已经用过的stream大小
uint64_t Writer::available_capacity() const
{
  // Your code here.
  return capacity_ - buffer.size();
}
// 返回总的push进stream的字节数
uint64_t Writer::bytes_pushed() const
{
  // Your code here.
  return total_bytes_pushed;
}

// 返回stream是否关闭或者pop完所有的元素
bool Reader::is_finished() const
{
  // Your code here.
  return closed && buffer.empty();
}
// 返回总的pop的stream的字节数
uint64_t Reader::bytes_popped() const
{
  // Your code here.
  return total_bytes_poped;
}
// Peek at the next bytes in the buffer
// string_view: C++ 17引入,在不拷贝的情况下读取、查看和操作字符串
// peek函数作用:
string_view Reader::peek() const
{
  // Your code here.
  if ( !buffer.empty() ) {
    return std::string_view( &buffer.front(), 1 ); // 返回deque的front元素的string_view
  }
  return std::string_view(); // 返回一个默认构造的string_view(空的)
}
//
void Reader::pop( uint64_t len )
{
  // Your code here.
  if ( buffer.size() < len ) {
    len = buffer.size();
  }
  for ( uint64_t i = 0; i < len; ++i ) {
    buffer.pop_front();
    total_bytes_poped++;
  }
}
// Number of bytes currently buffered (pushed and not popped)
uint64_t Reader::bytes_buffered() const
{
  // Your code here.
  return buffer.size();
}

运行结果:

相关推荐
越甲八千3 分钟前
C++海康相机DEMO
开发语言·c++·数码相机
天若有情67321 分钟前
用 C++ 实现选择题答案随机生成器:从生活灵感走向代码实践
c++·算法·生活
L73S3727 分钟前
C/C++输入输出(1)
c++·笔记·学习·考研·蓝桥杯
兴达易控31 分钟前
ProfibusDP主站转ModbusTCP网关如何进行数据互换
网络协议·profibus dp
Halsey Walker1 小时前
QT实现简约美观的动画Checkbox
c++·qt·动画
炬火初现1 小时前
仿mudou库one thread oneloop式并发服务器
服务器·c++·muduo
乄北城以北乀1 小时前
muduo库源码分析:TcpConnection 类
linux·数据结构·c++·后端·网络协议·tcp/ip·visual studio
wen__xvn2 小时前
每日一题蓝桥杯P8598 [蓝桥杯 2013 省 AB] 错误票据c++
开发语言·数据结构·c++·算法
JuicyActiveGilbert2 小时前
【C++设计模式】第一篇:单例模式(Singleton)
c++·单例模式·设计模式
菜菜小蒙3 小时前
【Linux】http 协议
网络·网络协议·http