Lab 0: networking warmup
1. 环境
依赖配置
sudo apt update && sudo apt install git cmake gdb build-essential clang \
clang-tidy clang-format gcc-doc pkg-config glibc-doc tcpdump tshark
g+±13配置
- ppa中科大源
# deb https://ppa.launchpadcontent.net/ubuntu-toolchain-r/test/ubuntu/ jammy main
deb https://launchpad.proxy.ustclug.org/ubuntu-toolchain-r/test/ubuntu/ jammy main
# deb-src https://ppa.launchpadcontent.net/ubuntu-toolchain-r/test/ubuntu/ jammy main
- 安装
g++-13 gcc-13
sudo apt install gcc-13
sudo apt install g++-13
- 设定优先级
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 11
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 11
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13
- 版本检查
g++ -v
gcc -v
- cmake版本安装
git clone https://gitee.com/mirrors/CMakesource.git
./configure
make && make install
更新apt源
sudo update-alternatives --install /usr/bin/cmake cmake /usr/local/bin/cmake 1 --force
2. 使用命令获取网页
2.1 http获取网页
telnet到服务器上
telnet cs144.keithw.org http
输入http方法,指定主机,输入完成后还需要按一次enter
GET /hello HTTP/1.1 \n
Host: cs144.keithw.ort \n
\n
输入成功后可以得到
Hello, CS144!
的字样。
2.2 smtp输入邮箱
没有内网权限,跳过这个实验
2.3 本地服务器
在本地使用 netcat
命令
- 服务端
netcat -v -l -p 9090
- 客户端
telnet localhost 9090
之后分别在客户端和服务端输入,两方都会分别显示。
相当于一个echo-server
。
3. 使用系统socket编写网络程序
如果你了解socket编写程序的步骤,加上你看懂了socket.cc
和address.cc
这两个类的代码;
直接调用它的接口就好了。
TCPSocket cln_skt;
Address peer_address( host, "http");
cln_skt.connect( peer_address );
// Address pa = cln_skt.peer_address();
// std::cout << pa.to_string() << std::endl;
std::string http_req_str("GET ");
http_req_str.append( path );
http_req_str.append(" HTTP/1.1\r\nHost: cs144.keithw.org\r\nConnection: close\r\n\r\n");
cln_skt.write( http_req_str);
std::string rsp;
while ( !cln_skt.eof()) {
rsp.clear();
cln_skt.read( rsp );
std::cout << rsp;
}
cln_skt.close();
4. 实现内存中的可靠字节流
4.1 实验描述
这个实验的目的是,在内存中实现和在上面网络通信中相类似的可靠字节流。
字节流是有限的,写入端可以终止输入;当读入端读到EOF
时,就不能再继续读了。
这个字节流在内存中是需要控制大小的,字节流会控制在任意时刻写入流的大小,它不会超过它的存储容量,直到当读入端新读出了一些数据,写入端才又可以重新写东西。这个字节流是在单线程下工作的,不用考虑读写竞争的问题。
4.2 实现
- bytestream.cc
#include "byte_stream.hh"
using namespace std;
ByteStream::ByteStream( uint64_t capacity ) : capacity_( capacity ) {}
void Writer::push( string data ) noexcept
{
// (void)data; // Your code here.
if ( is_closed_ ) {
this->error_ = true;
return ;
}
auto sz = data.size();
if ( sz > available_capacity()) {
//std::cout << "string greater than capacity!!!" << std::endl;
}
auto pushBytes = std::min( sz, available_capacity());
if ( 0 == pushBytes )
return;
// auto edit = std::find(data.begin(), data.begin() + pushBytes, EOF);
// if ( edit != data.begin() + pushBytes) {
// pushBytes = static_cast<uint64_t>( std::distance(data.begin(), edit ) );
// is_closed_ = true;
// }
for (uint64_t i = 0; i < pushBytes; ++i) {
if ( EOF == data[i] ) {
pushBytes = i;
is_closed_ = true;
break;
}
// pushStr.push_back( data[i] );
}
tot_bytes_pushed_ += pushBytes;
cur_bytes_buffered += pushBytes;
// buffers.push( data.substr(0, pushBytes));
// buffers.emplace( data.substr(0, pushBytes));
buffers.emplace( std::string_view(data.begin(), data.begin() + pushBytes));
}
void Writer::close() noexcept
{
is_closed_ = true;
// Your code here.
}
bool Writer::is_closed() const noexcept
{
return is_closed_; // Your code here.
}
uint64_t Writer::available_capacity() const noexcept
{
// Your code here.
return capacity_ - cur_bytes_buffered;
}
uint64_t Writer::bytes_pushed() const noexcept
{
return tot_bytes_pushed_; // Your code here.
}
string_view Reader::peek() const noexcept
{
if ( buffers.empty()) {
return "";
}
return std::string_view{buffers.front().begin() + lazy_pointer, buffers.front().end()};
// Your code here.
}
void Reader::pop( uint64_t len ) noexcept
{
if ( 0 == len ) {
return;
}
if ( buffers.empty()) {
return;
}
while ( len > 0 && cur_bytes_buffered > 0) {
std::string& s = buffers.front();
auto real_sz = s.size() - lazy_pointer;
auto pop_bytes = min( static_cast<uint64_t>( real_sz), len);
if ( len >= real_sz ) {
buffers.pop();
lazy_pointer = 0;
}
else {
lazy_pointer += pop_bytes;
// s.erase(0, len);
}
len -= pop_bytes;
cur_bytes_buffered -= pop_bytes;
tot_bytes_poped_ += pop_bytes;
}
// (void)len; // Your code here.
}
bool Reader::is_finished() const noexcept
{
return is_closed_ && buffers.empty(); // Your code here.
}
uint64_t Reader::bytes_buffered() const noexcept
{
return cur_bytes_buffered; // Your code here.
}
uint64_t Reader::bytes_popped() const noexcept
{
return tot_bytes_poped_; // Your code here.
}
- bytestream.h
#pragma once
#include <cstdint>
#include <string>
#include <string_view>
#include <queue>
#include <algorithm>
#include <iostream>
class Reader;
class Writer;
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() noexcept{ error_ = true; }; // Signal that the stream suffered an error.
bool has_error() const noexcept{ 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_ {};
std::queue<std::string> buffers{};
bool is_closed_{ false };
uint64_t cur_bytes_buffered{};
uint64_t tot_bytes_pushed_{};
uint64_t tot_bytes_poped_{};
size_t lazy_pointer{};
};
class Writer : public ByteStream
{
public:
void push( std::string data ) noexcept; // Push data to stream, but only as much as available capacity allows.
void close() noexcept; // Signal that the stream has reached its ending. Nothing more will be written.
bool is_closed() const noexcept; // Has the stream been closed?
uint64_t available_capacity() const noexcept; // How many bytes can be pushed to the stream right now?
uint64_t bytes_pushed() const noexcept; // Total number of bytes cumulatively pushed to the stream
};
class Reader : public ByteStream
{
public:
std::string_view peek() const noexcept; // Peek at the next bytes in the buffer
void pop( uint64_t len ) noexcept; // Remove `len` bytes from the buffer
bool is_finished() const noexcept; // Is the stream finished (closed and fully popped)?
uint64_t bytes_buffered() const noexcept; // Number of bytes currently buffered (pushed and not popped)
uint64_t bytes_popped() const noexcept; // Total number of bytes cumulatively popped from stream
};
/*
* read: A (provided) helper function thats peeks and pops up to `max_len` bytes
* from a ByteStream Reader into a string;
*/
void read( Reader& reader, uint64_t max_len, std::string& out );