CS 144: Introduction to Computer Networking, Fall 2020 https://cs144.github.io/
LAB0 在 master 分支,LAB1 - 7 在对应名字的分支。
参照 lab0.pdf 2.1 Fetch a Web page, 如下所示。
其效果等同于
参考 API 文档 https://cs144.github.io/doc/lab0/class_t_c_p_socket.html。
注意 lab0.pdf 中的几点提示:
- Please note that in HTTP, each line must be ended with “\r\n” (it’s not sufficient to use just “\n” or endl).
- Don’t forget to include the “Connection: close” line in your client’s request. This tells the server that it shouldn’t wait around for your client to send any more requests after this one. Instead, the server will send one reply and then will immediately end its outgoing bytestream (the one from the server’s socket to your socket). You’ll discover that your incoming byte stream has ended because your socket will reach “EOF” (end of file) when you have read the entire byte stream coming from the server. That’s how your client will know that the server has finished its reply.
- Make sure to read and print all the output from the server until the socket reaches “EOF” (end of file) — a single call to read is not enough.
在 GET 请求中写明 Connection: close
可以让服务器马上进入连接释放的过程(发一个 FIN 过来),我们在发送完 GET 同样需要 shutdown(SHUT_WR)
,进入连接释放过程(发一个 FIN 过去)。(不太理解的话,写完 Lab4,就能理解了。)
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).
// cerr << "Function called: get_URL(" << host << ", " << path << ").\n";
// cerr << "Warning: get_URL() has not been implemented yet.\n";
TCPSocket sock1;
sock1.connect(Address(host, "http"));
sock1.write("GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
while (!sock1.eof()) {
cout << sock1.read();
}
sock1.shutdown(SHUT_WR);
}
注意下面代码中的 buffer_size
为缓冲的内容大小(等于 _stream.size()
),capacity
才是缓冲的大小。只有当 input_ended
为真并且 buffer_size
为 0 时,才是 EOF。
byte_stream.hh
:
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.
bool _error = false; //!< Flag indicating that the stream suffered an error.
bool _input_ended = false;
size_t _capacity;
size_t _buffer_size = 0;
size_t _bytes_written = 0;
size_t _bytes_read = 0;
std::list<char> _stream{};
public:
byte_stream.cc
:
#include "byte_stream.hh"
#include <string>
// 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) : _capacity(capacity) {}
size_t ByteStream::write(const string &data) {
size_t write_count = 0;
for (const char c : data) {
// not very efficient to do conditional in loop
if (_capacity - _buffer_size <= 0)
break;
else {
_stream.push_back(c);
++_buffer_size;
++_bytes_written;
++write_count;
}
}
return write_count;
}
//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
const size_t peek_length = len > _buffer_size ? _buffer_size : len;
list<char>::const_iterator it = _stream.begin();
advance(it, peek_length);
return string(_stream.begin(), it);
}
//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
size_t pop_length = len > _buffer_size ? _buffer_size : len;
_bytes_read += pop_length;
_buffer_size -= pop_length;
while (pop_length--)
_stream.pop_front();
}
//! 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 string result = peek_output(len);
pop_output(len);
return result;
}
void ByteStream::end_input() { _input_ended = true; }
bool ByteStream::input_ended() const { return _input_ended; }
size_t ByteStream::buffer_size() const { return _buffer_size; }
bool ByteStream::buffer_empty() const { return _stream.size() == 0; }
bool ByteStream::eof() const { return _input_ended && buffer_empty(); }
size_t ByteStream::bytes_written() const { return _bytes_written; }
size_t ByteStream::bytes_read() const { return _bytes_read; }
size_t ByteStream::remaining_capacity() const { return _capacity - _buffer_size; }
BELOW IS THE ORIGINAL README OF THIS LAB.
For build prereqs, see the CS144 VM setup instructions.
To set up your build directory:
$ mkdir -p <path/to/sponge>/build
$ cd <path/to/sponge>/build
$ cmake ..
Note: all further commands listed below should be run from the build
dir.
To build:
$ make
You can use the -j
switch to build in parallel, e.g.,
$ make -j$(nproc)
To test (after building; make sure you've got the build prereqs installed!)
$ make check_lab0
or
$ make check_lab1
etc.
The first time you run a make check
, it may run sudo
to configure two
TUN devices for use during testing.
You can specify a different compiler when you run cmake:
$ CC=clang CXX=clang++ cmake ..
You can also specify CLANG_TIDY=
or CLANG_FORMAT=
(see "other useful targets", below).
Sponge's build system supports several different build targets. By default, cmake chooses the Release
target, which enables the usual optimizations. The Debug
target enables debugging and reduces the
level of optimization. To choose the Debug
target:
$ cmake .. -DCMAKE_BUILD_TYPE=Debug
The following targets are supported:
Release
- optimizationsDebug
- debug symbols and-Og
RelASan
- release build with ASan and UBSanRelTSan
- release build with ThreadSanDebugASan
- debug build with ASan and UBSanDebugTSan
- debug build with ThreadSan
Of course, you can combine all of the above, e.g.,
$ CLANG_TIDY=clang-tidy-6.0 CXX=clang++-6.0 .. -DCMAKE_BUILD_TYPE=Debug
Note: if you want to change CC
, CXX
, CLANG_TIDY
, or CLANG_FORMAT
, you need to remove
build/CMakeCache.txt
and re-run cmake. (This isn't necessary for CMAKE_BUILD_TYPE
.)
To generate documentation (you'll need doxygen
; output will be in build/doc/
):
$ make doc
To lint (you'll need clang-tidy
):
$ make -j$(nproc) tidy
To run cppcheck (you'll need cppcheck
):
$ make cppcheck
To format (you'll need clang-format
):
$ make format
To see all available targets,
$ make help