参考链接: BYO Linux installation
额外安装的包: clang-tidy-6
, clang-format-6.0
, libpcap-dev
,mininet
, wireshark-qt
, doxygen
, graphviz
, cppcheck
clang-format-7
, 直接安装后执行 cmake ..
后运行 make format
会显示找不到 clang-format. 根据 clang_format.cmake
中的代码, 需要添加环境变量 CLANG_FORMAT
.clang-format-6.0
, 则无需配置环境变量.clang_tidy.cmake
中的代码, 必须设置 CLANG_TIDY
环境变量在终端输入以下指令:
$ telnet cs144.keithw.org http
GET /hello HTTP/1.1
Host: cs144.keithw.org
Connection: close
此处由于没有斯坦福大学的邮箱账号, 故选用 QQ 的 SMTP 服务, 其配置具体可参考: 利用Telnet登录qq邮箱发送邮件——SMTP协议学习_Fossette_lhx的博客-CSDN博客
输入过程截图如下:
在 DATA
中输入 .
表示结束后, 邮件就会被发送:
<
和 >
括起.一个终端作为服务端, 输入以下指令(注意与实验指导不同, 需要额外添加参数 -n
):
$ netcat -v -l -n -p 9090
另一个终端作为客户端, 输入一下命令来连接服务端:
$ telnet localhost 9090
webget
实现 apps/webget.cc
中的 get_URL()
.
实现思路基本按照实验指导的提示和代码注释, 建立连接后发送 HTTP 请求报文. 然后打印回复报文的内容. 由于回复报文可能不止一个, 因此需要通过检查 EOF 标志位来判断是否接收完毕.
apps/webget.cc
void get_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).
const Address addr(host, "http");
TCPSocket tcpSocket;
tcpSocket.connect(addr); // connect the HTTP server
// construct the HTTP header and send to the server
string httpHeader;
httpHeader.append("GET ").append(path).append(" HTTP/1.1\r\n")
.append("Host: ").append(host).append("\r\n")
.append("Connection: close\r\n")
.append("\r\n");
if (tcpSocket.write(httpHeader) != httpHeader.size()) {
cerr << "HTTP request failed.\n";
return;
}
// read and print the output from the server until reaching “EOF”
while (!tcpSocket.eof()) {
cout << tcpSocket.read();
}
tcpSocket.close(); // close the TCP connection
// cerr << "Function called: get_URL(" << host << ", " << path << ").\n";
// cerr << "Warning: get_URL() has not been implemented yet.\n";
}
该部分是实现一个内存中可靠字节流的缓冲区数据结构. 根据实验指导的描述, 该数据结构有一个数据写入者和一个数据读取者, 是一个典型的队列模型. 而考虑到其需要实现的接口中写入和读出的可以是一段字节流, 且有只读不取的 peek_output()
方法, 因此自己实现基于循环队列的缓冲区是比较麻烦的, 因此考虑使用 STL 中的模板, 但由于 queue
队首不能多元素只读不取, 因此可以想到使用双端队列模板 deque
.
此外, 由于是基于字节的, 因此容易想到直接使用 string
, 虽然 string
的实现类似于 vector
, 但在此处和 deque
的差别不大, 且在 Lab4 中证实性能要优于 deque
.
整体上, 目前代码能够完成测试, 但考虑到实验指导在一些地方说明的不是很详尽, 后续仍可能需要改动.
libsponge/byte_stream.hh
#ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH
#define SPONGE_LIBSPONGE_BYTE_STREAM_HH
#include
//! \brief 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.
const size_t _cap; //! The capacity of the stream buffer
std::string _buffer{}; //!< Byte stream buffer
size_t _total_read = 0; //!< Total number of bytes written
size_t _total_written = 0; //!< Total number of bytes popped
bool _end = false; //!< Flag indicating that the byte stream has reached its ending.
bool _error = false; //!< Flag indicating that the stream suffered an error.
public:
//! Construct a stream with room for `capacity` bytes.
ByteStream(const size_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_t write(const std::string &data);
//! \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; }
//!@}
//! \name "Output" interface for the reader
//!@{
//! Peek at next "len" bytes of the stream
//! \returns a string
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
//! \returns a string
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;
//!@}
//! \name General accounting
//!@{
//! Total number of bytes written
size_t bytes_written() const;
//! Total number of bytes popped
size_t bytes_read() const;
//!@}
};
#endif // SPONGE_LIBSPONGE_BYTE_STREAM_HH
libsponge/byte_stream.cc
#include "byte_stream.hh"
// 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`
template <typename... Targs>
void DUMMY_CODE(Targs &&.../* unused */) {}
using namespace std;
ByteStream::ByteStream(const size_t capacity) : _cap(capacity), _buffer() {}
size_t ByteStream::write(const string &data) {
const size_t size = min(data.size(), _cap - _buffer.size());
_buffer.append(data, 0, size);
_total_written += size;
return size;
}
//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
return {_buffer, 0,min(len, _buffer.size())};
}
//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
const size_t size = min(len, _buffer.size());
_buffer.erase(0, size);
_total_read += size;
}
//! 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) {
const size_t size = min(len, _buffer.size());
string str = _buffer.substr(0, size);
_buffer.erase(0, size);
_total_read += size;
return str;
}
void ByteStream::end_input() { _end = true; }
bool ByteStream::input_ended() const { return _end; }
size_t ByteStream::buffer_size() const { return _buffer.size(); }
bool ByteStream::buffer_empty() const { return _buffer.empty(); }
bool ByteStream::eof() const { return _end && _buffer.empty(); }
size_t ByteStream::bytes_written() const { return _total_written; }
size_t ByteStream::bytes_read() const { return _total_read; }
size_t ByteStream::remaining_capacity() const { return _cap - _buffer.size(); }
在 build
目录下执行 make
后执行 make check_lab0
:
cs144.keithw.org
和谷歌官网两个外网, 所以耗时较长.