CS144 Lab0 Networking Warmup

Lab0 如其名,warmup,就利用工具实际感受了一下 Bi-directional reliable byte stream。除了发送e-mail那个部分需要Stanford账号,其他的跟着步骤做就行。

官方让用虚拟机,但太卡了很烦,我直接用的docker。

有个坑是,Fetch a Web page 部分操作要快,不然很快就timeout了。

Writing Webget

代码部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 sock;
sock.connect(Address(host, "http"));
sock.write("GET " + path + " HTTP/1.1\r\n");
sock.write("Host: " + host + "\r\n");
sock.write("Connection: close\r\n\r\n");

while (!sock.eof()) {
cout << sock.read();
}
sock.close();
}

也就是模仿着之前 fetch a web page 你复制粘贴内容的格式,丢进socket,然后读socket传来的内容即可。

用到的Socket是由操作系统提供的,是一个介于Transport Layer和Application Layer的interface。利用Transport Layer获得bytes stream,提供给Application layer比如http来解析出内容,简化了Application 使用Transport的过程。

可以类比打电话:

打电话的人是Application Layer

电话线路是Transport Layer

而电话本身就是Socket

An in-memory reliable byte stream

这部分纯纯数据结构,不需要网络知识了。构建一个buffer,或者说一个队列queue,实现从一端写入一端读取。同时这个buffer是大小限定的,写入超出buffer内存的部分直接抛弃。

即然是queue,那么很自然的会想到用std::deque。这样实现会很容易,并且c++的deque不是linked list,其底层大概是分配一个个固定大小的blocks,用一个array来把不同分段的index映射到对应的block里。(有点像inode的文件系统)其尾插和头读都是O(1), 由于block内部是连续存储且其大小会适配cacheline,cache的利用率也不错。

std:deque是可以高效使用的,但std::deque内部还是会频繁申请和释放block(大部分的实现中,不需要的block会立刻释放而不会用池存起来),同时block与block之间的并不是连续的,cache利用率还有提升空间,所以更高效的方法还是用 std::vector构建一个circular buffer。

可以reserve一个大小为 capacity 的std::vector, 用双指针 start指向第一个读取的index,end指向下一个写入的index。这时候就有个问题:

当 start == end,既可以表示 buffer为空,也可以表示buffer满了。
需要再引入一个变量来判断到底是满的还是空的。

但有个奇技淫巧,将std::vector大小设置为capacity+1, 此时:

当 start == end, buffer是空的;
当 start + 1 == end, buffer是满的。

于是整个代码会变得更加优雅简洁。具体实现可见这篇博客

参考资料

https://lrl52.top/992/cs144-lablab-0/