最近秋招太难了,已经没几个面试了,目前只有byd一个保底的offer。论文送审还没出结果。
最近面试问网络问的还挺多的,闲的没事在这里开一个新坑吧,cs144,实现网络协议栈相关的东西。
1. 体验网络的使用
1.1 获取网页
- 访问 http://cs144.keithw.org/hello 可以看到 Hello, CS144! 字样
- 使用 telnet 体验http交互
telnet cs144.keithw.org http
GET /hello HTTP/1.1
Host: cs144.keithw.org
Connection: close
结果和上面一样
3. 使用同样方法访问 http://cs144.keithw.org/lab0/, sunetid是一个任意id,输入的和上面类似
telnet cs144.keithw.org http
GET /lab0/123 HTTP/1.1
Host: cs144.keithw.org
Connection: close
响应头中包含的X-Your-Code-Is 为 729256
1.2 发送邮件
这部分也是用telnet体验下smtp协议,没有stanford邮箱,也懒得用其他邮箱了,直接跳过
1.3 使用netcat进行监听和连接
这部分就是用 netcat -v -l -p 9000
监听并用 telnet localhost 9000
进行连接,比较简单,跳过
2. 编写webget获取http数据
只需要用TcpSocket和Address类即可,创建Socket并连接到访问的地址,并发送HTTP数据,然后从socket读取数据并打印即可。
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).
TCPSocket s;
s.set_reuseaddr();
Address addr(host, "http");
s.connect(addr);
string data = "GET " + path + " HTTP/1.1\r\n" +
"HOST: " + host + "\r\n" +
"Connection: close\r\n" +
"\r\n";
s.write(data);
while (true) {
auto datagram = s.read();
if (!datagram.length()) {
break;
} else {
cout << datagram;
}
}
}
3. 编写字节流类
字节流类是用于通信的一个类,保存了一些字节流的数据。其具有最大容量,写满了就不能再写,等待数据被读取一部分才能继续写入。
这部分不难,我新增的成员如下:
std::string buf_; // 环形缓冲区
size_t read_idx_; // 累计读取的字节数
size_t write_idx_; // 累计写入的字节数
size_t capacity_; // 缓冲区大小上限
bool input_ended_; // 后续是否不再写入
定义好成员后,后面的相关函数实现就比较简单了,如下:
static size_t rounddown(size_t size, size_t factor) {
return size - size % factor;
}
ByteStream::ByteStream(const size_t capacity):
buf_(), read_idx_(0), write_idx_(0), capacity_(capacity), input_ended_(false)
{}
size_t ByteStream::write(const string &data) {
if (input_ended_) {
return 0;
}
// 这里让buf的实际大小随需要增长,也可以直接初始化为capacity_大小
if (buf_.length() < capacity_ && write_idx_ + data.length() >= buf_.length()) {
buf_ = buf_ + string(min(max(buf_.length(), data.length()), capacity_ - buf_.length()), ' ');
}
// 计算实际能写入的字节数,并且因为是环形的,如果跨越了缓冲区的首尾,那么需要分成两段,分别使用memcpy复制字节
size_t write_size = min(remaining_capacity(), data.length());
size_t roundup_size = rounddown(write_idx_ + capacity_, capacity_);
size_t seg1_size = min(roundup_size, write_idx_ + write_size) - write_idx_;
memcpy(getidx(write_idx_), data.c_str(), seg1_size);
memcpy(getidx(0), data.c_str() + seg1_size, write_size - seg1_size);
write_idx_ += write_size;
return write_size;
}
//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
size_t read_size = min(len, buffer_size());
if (!read_size) {;
return "";
}
string ans;
size_t roundup_size = rounddown(read_idx_ + capacity_, capacity_);
size_t seg1_size = min(roundup_size, read_idx_ + read_size) - read_idx_;
ans += buf_.substr(read_idx_ % capacity_, seg1_size);
ans += buf_.substr(0, read_size - seg1_size);
return ans;
}
//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
read_idx_ += min(len, buffer_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) {
auto ans = peek_output(len);
pop_output(ans.length());
return ans;
}