HTTP/1.1 200 OK Date: Mon, 17 Jan 2022 06:46:16 GMT Server: Apache X-You-Said-Your-SunetID-Was: misaka X-Your-Code-Is: 712264 Content-length: 110 Vary: Accept-Encoding Connection: close Content-Type: text/plain
Hello! You told us that your SUNet ID was "misaka". Please see the HTTP headers (above) for your secret code. Connection closed by foreign host.
2.2 Send yourself an email
由于没有sunetid,采用163邮箱进行代替
1 2 3 4 5
telnet smtp.163.com smtp Trying 220.181.12.11... Connected to smtp.163.com. Escape character is '^]'. 220 163.com Anti-spam GT for Coremail System (163com[xxxx])
和163邮箱打个招呼
1
helo 163.com
返回250 OK
邮箱账户登陆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
auth login 334 dXNlcm5hbWU6 [输入163邮箱的base64编码格式] 334 UGFzc3dvcmQ6 [输入base64编码格式的邮箱密码] 235 Authentication successful MAIL FROM: <[email protected]> //表示发件人为 [email protected] 250 Mail OK RCPT TO: <[email protected]> //表示收件人为 [email protected] 250 Mail OK DATA //准备好发送数据了 354 End data with <CR><LF>.<CR><LF> [邮件内容] . 250 Mail OK queued as smtp14,EsCowAAX5WUxFuVhrJCNAg--.xxxxxx xxxxxxxxxx QUIT 221 Bye Connection closed by foreign host.
在收件邮箱中可以发现收到的邮件
2.3 Listening and connecting
在命令行中输入:
1
netcat -v -l -p 9090
出现如下问题
1
netcat: getnameinfo: Temporary failure in name resolution
解决方法:
1
sudo vim /etc/resolv.conf
添加以下内容并保存
1
namespace 8.8.8.8
在一个窗口输入:
1
netcat -v -l -p 9090
新的窗口输入
1
telnet localhost 9090
即可实现netcat窗口对telnet窗口的监听,关闭telnet窗口就可以关闭这个监听程序
3 Writing a network program using an OS stream socket
3.1 Let’s get started—fetching and building the starter code
1 2 3 4 5 6
git clone https://github.com/cs144/sponge git checkout -b master origin/master mkdir build && cd build cmake .. make format make -j4 && make check_lab0
//! Write a string, possibly blocking until all is written size_twrite(constchar *str, constbool write_all = true){ returnwrite(BufferViewList(str), write_all); }
//! Write a string, possibly blocking until all is written size_twrite(const std::string &str, constbool write_all = true){ returnwrite(BufferViewList(str), write_all); }
//! Write a buffer (or list of buffers), possibly blocking until all is written size_twrite(BufferViewList buffer, constbool write_all = true);
socket.hh:
1 2 3 4 5 6 7
//! Connect a socket to a specified peer address with [connect(2)](\ref man2::connect) //使用[CONNECT(2)](\ref Man2::Connect)将socket连接到指定的对等地址 voidconnect(const Address &address);
//! Shut down a socket via [shutdown(2)](\ref man2::shutdown) //通过[Shutdown(2)](\ref Man2::Shutdown)关闭socket voidshutdown(constint how);
address:
1 2 3 4 5 6 7 8 9 10 11
//! Construct by resolving a hostname and servicename. // 通过 主机名 如 “cs144.keithw.org” 和 服务名 如 “http” 进行构造 Address(const std::string &hostname, const std::string&service);
//! Construct from dotted-quad string ("18.243.0.1") and numeric port. // 通过 ip地址 和 端口号 进行构造 Address(const std::string &ip, const std::uint16_t port = 0);
//! Construct from a [sockaddr *](@ref man7::socket). // 通过 原始socket地址 和 地址长度 进行构造 Address(const sockaddr *addr, const std::size_t size);
3.4 Writing webget
It’s time to implement webget, a program to fetch Web pages over the Internet using the
operating system’s TCP support and stream-socket abstraction—just like you did by hand
earlier in this lab.
webget是一个使用操作系统的TCP支持和流套接字抽象在Internet上获取网页的程序,就像您在本实验室前面手动完成的一样。
voidget_URL(const string &host, const string &path){ // Your code here.
// 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).
// cerr << "Function called: get_URL(" << host << ", " << path << ").\n"; // cerr << "Warning: get_URL() has not been implemented yet.\n"; }
make
1
$ make
1 2 3 4 5 6 7 8 9 10 11
[ 33%] Built target sponge [ 40%] Built target webget [ 46%] Built target spongechecks [ 53%] Built target byte_stream_many_writes [ 60%] Built target byte_stream_capacity [ 66%] Built target byte_stream_two_writes [ 73%] Built target byte_stream_one_write [ 80%] Built target byte_stream_construction [ 86%] Built target parser_dt [ 93%] Built target socket_dt [100%] Built target address_dt
测试
1
$ ./apps/webget cs144.keithw.org /hello
结果:
1 2 3 4 5 6 7 8 9 10
HTTP/1.1 200 OK Date: Mon, 17 Jan 2022 09:16:30 GMT Server: Apache Last-Modified: Thu, 13 Dec 2018 15:45:29 GMT ETag: "e-57ce93446cb64" Accept-Ranges: bytes Content-Length: 14 Content-Type: text/plain
Hello, CS144!
make check_webget
1
$ make check_webget
结果
1 2 3 4 5 6 7 8 9
[100%] Testing webget... Test project /home/ubuntu/cs144/sponge/build Start 31: t_webget 1/1 Test #31: t_webget ......................... Passed 1.05 sec
100% tests passed, 0 tests failed out of 1
Total Test time (real) = 1.05 sec [100%] Built target check_webget
//! \brief A reference-counted read-only string that can discard bytes from the front classBuffer { private: std::shared_ptr<std::string> _storage{}; size_t _starting_offset{};
public: Buffer() = default;
//! \brief Construct by taking ownership of a string Buffer(std::string &&str) noexcept : _storage(std::make_shared<std::string>(std::move(str))) {}
//! \name Expose contents as a std::string_view //!@{ std::string_view str()const{ if (not _storage) { return {}; } return {_storage->data() + _starting_offset, _storage->size() - _starting_offset}; }
//! \brief Get character at location `n` uint8_tat(constsize_t n)const{ returnstr().at(n); }
//! \brief Size of the string size_tsize()const{ returnstr().size(); }
//! \brief Make a copy to a new std::string std::string copy()const{ return std::string(str()); }
//! \brief Discard the first `n` bytes of the string (does not require a copy or move) //! \note Doesn't free any memory until the whole string has been discarded in all copies of the Buffer. voidremove_prefix(constsize_t n);
//! \brief A reference-counted discontiguous string that can discard bytes from the front //! \note Used to model packets that contain multiple sets of headers //! + a payload. This allows us to prepend headers (e.g., to //! encapsulate a TCP payload in a TCPSegment, and then encapsulate //! the TCPSegment in an IPv4Datagram) without copying the payload. classBufferList { private: std::deque<Buffer> _buffers{};
public: //! \name Constructors //!@{
BufferList() = default;
//! \brief Construct from a Buffer BufferList(Buffer buffer) : _buffers{buffer} {}
//! \brief Construct by taking ownership of a std::string BufferList(std::string &&str) noexcept { Buffer buf{std::move(str)}; append(buf); } //!@}
//! \brief Access the underlying queue of Buffers const std::deque<Buffer> &buffers()const{ return _buffers; }
//! \brief Append a BufferList voidappend(const BufferList &other);
//! \brief Transform to a Buffer //! \note Throws an exception unless BufferList is contiguous operatorBuffer()const;
//! \brief Discard the first `n` bytes of the string (does not require a copy or move) voidremove_prefix(size_t n);
//! \brief Size of the string size_tsize()const;
//! \brief Make a copy to a new std::string std::string concatenate()const; };
//! \brief A non-owning temporary view (similar to std::string_view) of a discontiguous string classBufferViewList { std::deque<std::string_view> _views{};
public: //! \name Constructors //!@{
//! \brief Construct from a std::string BufferViewList(const std::string &str) : BufferViewList(std::string_view(str)) {}
//! \brief Construct from a C string (must be NULL-terminated) BufferViewList(constchar *s) : BufferViewList(std::string_view(s)) {}
//! \brief Construct from a BufferList BufferViewList(const BufferList &buffers);
//! \brief Construct from a std::string_view BufferViewList(std::string_view str) { _views.push_back({const_cast<char *>(str.data()), str.size()}); } //!@}
//! \brief Discard the first `n` bytes of the string (does not require a copy or move) voidremove_prefix(size_t n);
//! \brief Size of the string size_tsize()const;
//! \brief Convert to a vector of `iovec` structures //! \note used for system calls that write discontiguous buffers, //! e.g. [writev(2)](\ref man2::writev) and [sendmsg(2)](\ref man2::sendmsg) std::vector<iovec> as_iovecs()const; };
//! \brief Construct by taking ownership of a string BufferPlus(std::string &&str) noexcept : _storage(std::make_shared<std::string>(std::move(str))) {}
//! \brief Make a copy to a new std::string std::string copy()const{ return std::string(str()); }
//! \brief Discard the first `n` bytes of the string (does not require a copy or move) //! \note Doesn't free any memory until the whole string has been discarded in all copies of the Buffer. voidremove_prefix(constsize_t n); //! \brief Discard the last `n` bytes of the string (does not require a copy or move) voidremove_suffix(constsize_t n); };
BufferPlus 的 C++ 类。它是一个字符串类,用于表示字符串,并提供了许多有用的操作。下面是这个类的主要特性:
//! 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. classByteStream { 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.
bool _error{false}; //!< Flag indicating that the stream suffered an error. bool _input_ended{false}; size_t _capacity; size_t _bytes_written{0}; size_t _bytes_read{0}; std::deque<BufferPlus> _buffer{};
public: //! Construct a stream with room for `capacity` bytes. ByteStream(constsize_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 stream size_twrite(const std::string &data); size_twrite(std::string &&data); size_twrite(BufferPlus& data);
//! \returns the number of additional bytes that the stream has space for size_tremaining_capacity()const;
//! Signal that the byte stream has reached its ending voidend_input();
//! Indicate that the stream suffered an error. voidset_error(){ _error = true; } //!@}
//! \name "Output" interface for the reader //!@{
//! Peek at next "len" bytes of the stream //! \returns a string std::string peek_output(constsize_t len)const;
//! Remove bytes from the buffer voidpop_output(constsize_t len);
//! Read (i.e., copy and then pop) the next "len" bytes of the stream //! \returns a string std::string read(constsize_t len);
//! \returns `true` if the stream input has ended boolinput_ended()const;
//! \returns `true` if the stream has suffered an error boolerror()const{ return _error; }
//! \returns the maximum amount that can currently be read from the stream size_tbuffer_size()const;
//! \returns `true` if the buffer is empty boolbuffer_empty()const;
//! \returns `true` if the output has reached the ending booleof()const; //!@}
//! \name General accounting //!@{
//! Total number of bytes written size_tbytes_written()const;
//! Total number of bytes popped size_tbytes_read()const; //!@} };
if (bytes != data.size()) { data.remove_suffix(data.size() - bytes); }
if (data.size()) { _buffer.emplace_back(move(data)); }
return bytes; }
//! \param[in] len bytes will be copied from the output side of the buffer string ByteStream::peek_output(constsize_t len)const{ size_t bytes = min(len, buffer_size()); string res; res.reserve(bytes);
//! \param[in] len bytes will be removed from the output side of the buffer voidByteStream::pop_output(constsize_t len){ size_t bytes = min(len, buffer_size()); _bytes_read += bytes;
//! 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(constsize_t len){ string res = peek_output(len); pop_output(len); return res; }