- Lab0
- A network program using an OS stream socket
- A in-memory reliable byte stream
- Lab1
- Putting substrings in sequence
- Lab2
- Lab3
- Lab4
用操作系统的接口实现socket:创建一个TCP字节流socket,跟Web server发起连接,请求网页。socket编程是面向两台不同计算机的进程之间的字节流通信,socket类似于file descriptor。
// webget.cc
void get_URL(const string &host, const string &path) {
TCPSocket sock;
sock.connect(Address(host, "http"));
sock.write("GET "+path+" HTTP/1.1rn");
sock.write("Host: " + host + "rn");
sock.write("Connection: closernrn");
sock.shutdown(SHUT_WR);
while(!sock.eof()){
cout << sock.read();
}
sock.close();
}
A in-memory reliable byte stream
类似于实现一个管道,完成一个类的给定接口实现功能。
- 字节流在input被写入
- 字节流以相同的顺序在output被读
- 字节流是有限的,writer可以结束input,声明自己已写完了所有的数据;
- reader读完字节流将不再读
- 字节流的容量是有限的,容器满后writer不能在写入,直到数据被reader读出
实现
- 使用双向队列deque作为byte stream的容器:方便在头部移出数据,并且可以拷贝头部若干数据,queue无法实现第二点
- 添加两个成员变量:字节流的缓冲区和容量
- 阅读接口:需要添加两个成员变量记录已写和已读的字节数
//! Total number of bytes written
size_t bytes_written() const;
//! Total number of bytes popped
size_t bytes_read() const;
- 阅读接口:需要添加一个成员变量,标记是否字节流已写结束
//! Signal that the byte stream has reached its ending
void end_input();
- end_input的意思是整个stream bytes流结束了,eof的意思是,当writer写完了所有数据,并且reader读完了所有数据后返回1.
// byte_stream.hh #ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH #define SPONGE_LIBSPONGE_BYTE_STREAM_HH #include#include class ByteStream { private: std::deque _buffer{}; // 字节流缓冲 size_t _capacity = 0; // 缓冲容量,以字节为单位 size_t _written_bytes = 0; // 已写字节数 size_t _read_bytes = 0; // 已读字节数 bool _endinput = false; // 字节流是否已写结束 bool _error = false; //!< Flag indicating that the stream suffered an error. public: ... }; #endif // SPONGE_LIBSPONGE_BYTE_STREAM_HH
// byte_stream.cc
#include "byte_stream.hh"
using namespace std;
ByteStream::ByteStream(const size_t capacity):_capacity(capacity) { }
size_t ByteStream::write(const string &data) {
size_t written_size = 0;
for(size_t i = 0; i < data.size(); i++) {
if(remaining_capacity()) {
_buffer.push_back(data[i]);
written_size++;
} else break;
}
_written_bytes += written_size;
return written_size;
}
string ByteStream::peek_output(const size_t len) const {
string str = "";
size_t read_size = len>buffer_size()? buffer_size() : len;
for(size_t i = 0; i < read_size; i++){
str += _buffer[i];
}
return str;
}
void ByteStream::pop_output(const size_t len) {
for(size_t i = 0; i < len && buffer_size(); i++){
_buffer.pop_front();
_read_bytes += 1;
}
}
std::string ByteStream::read(const size_t len) {
if(eof()) {
return "";
} else {
string read_str = peek_output(len);
pop_output(len);
return read_str;
}
}
void ByteStream::end_input() { _endinput= true; }
bool ByteStream::input_ended() const { return _endinput; }
size_t ByteStream::buffer_size() const { return _buffer.size(); }
bool ByteStream::buffer_empty() const { return _buffer.empty(); }
bool ByteStream::eof() const { return input_ended() && buffer_empty(); }
size_t ByteStream::bytes_written() const { return _written_bytes; }
size_t ByteStream::bytes_read() const { return _read_bytes; }
size_t ByteStream::remaining_capacity() const { return _capacity-buffer_size(); }
Lab1
实现一个stream reassembler模块,把字节流中的substrings/segments按照正确顺序放回字节流。
Putting substrings in sequence- 整个stream的第一个字节的索引号是0;
- 默认不会出现不一致的子串;
- 尽可能早地把字节写进ByteStream;
- 收到的子串之间可能会有重叠的部分;
- 不要重复存储子串之间的重叠部分;
- 字节流索引号
- read_bytes:Lab0中ByteStream的已读字节数
- written_bytes:Lab0中ByteStream的已写字节数
| 应用程序缓冲区 | ByteStream | 允许暂存区 | 暂不接收区 |
|---|---|---|---|
| 0 ~ (read_bytes-1) | read_bytes ~ (written_bytes-1) | written_bytes ~ (capacity-read_bytes+written_bytes-1) | (capacity-read_bytes+written_bytes) ~ eof |
实现TCPReceiver:思考TCP怎么表示每个字节在字节流中的位置,Receiver负责告诉发送方接收到多少字节(ACK),发送方还能发送多少字节(flow control)。
Lab3实现TCPSender:当怀疑发送出去的数据丢失在路上了该怎么处理,什么时候该重发一个丢失的segment。
Lab4整合前面的实验实现TCPConnection。



