CS144: Introduction to Computer Networking Lab 0
Contents
You can get the whole series from here
Writing webget
It is easy to write the webget
.
1 | void get_URL(const string &host, const string &path) { |
An in-memory reliable byte stream
First, you should carefully read the docs provided by the lab. The abstract requirement is below:
Bytes are written on the “input” side and can be read, in the same sequence, from the output “side”. The byte steam is finite: the writer can end the input, and then no bytes can be written. When the reader has read to the end of the stream, it will reach “EOF” and no more bytes can be read.
Your byte stream will also be flow-controlled to limit its memory consumption at any given time. The object is initialized with a particular “capacity”: the maximum number of bytes it’s willing to store in its own memory at any given point. The byte stream will limit the writer in how much it can write at any given moment, to make sure that the stream doesn’t exceed its storage capacity. As the reader reads bytes and drains them from the stream, the writer is allowed to write more. Your byte stream is for use in a single thread—you don’t have to worry about concurrent writers/readers, locking, or race conditions.
The idea I use is simple. I use vector<char>
to maintain a RingBuffer. In the class, I use two pointers _write_ptr
and read_ptr
to indicate the position. And some other fields to make the implementation easier.
1 | class ByteStream { |
Now we could go to implementation, the public interface of ByteStream
class is below:
ByteStream(const size_t capacity)
: Construct a stream with room forcapacity
bytes.size_t write(const std::string &data)
: Write a string of bytes into the stream. Write as many
as will fit, and return how many were written.size_t remaining_capacity() const
: Returns the number of additional bytes that the stream
has space for.void end_input()
: Signal that the byte stream has reached its ending.void set_error()
: Indicate that the stream suffered an error.std::string peek_output(const size_t len) const
: Peek at next “len” bytes of the stream.void pop_output(const size_t len)
: Remove bytes from the buffer.std::string read(const size_t len)
: Read the next “len” bytes of the stream.bool input_ended() const
: returnstrue
if the stream input has ended.bool error() const
: returnstrue
if the stream has suffered an errorsize_t buffer_size() const
: returns the maximum amount that can currently be read
from the stream.bool buffer_empty() const
: returnstrue
if the buffer is empty.bool eof() const
: returnstrue
if the output has reached the ending.size_t bytes_written() const
: Total number of bytes written.size_t bytes_read() const
: Total number of bytes popped.
There are some easy parts we can implement immediately.
Constructor
1 | ByteStream::ByteStream(const size_t capacity) : _capacity(capacity) { ringBuffer.resize(_capacity); } |
For the constructor, we set the _capacity
value and resize the ringBuffer
to a fixed size. And we will never change the size of the ringBuffer
.
Setter and Getter
There are some easy public interfaces.
1 | void set_error() { _error = true; } |
EOF
The tricky here is when the file will reach the end:
- Input is end.
- The buffer is empty.
1 | bool ByteStream::eof() const { return input_ended() && buffer_empty(); } |
Write Operation
It is easy to write. We just first get the maximum length we could write and write the data into the RingBuffer. And we need to do some error handling. When the input_ended()
is true, we should not write.
1 | size_t ByteStream::write(const string &data) { |
For peek_output
, we just copy it to a new string
.
1 | string ByteStream::peek_output(const size_t len) const { |
Read Operation
We first consider the pop_output
operation, we should do nothing about the RingBuffer itself. Just move the _read_ptr
, changes the _size
and adds the _read_bytes_count
.
1 | void ByteStream::pop_output(const size_t len) { |
For read
, we should do no error check even if the file is end. If the EOF is true, we just return an empty string. It’s important here because in reality this situation happens!
1 | std::string ByteStream::read(const size_t len) { |