CS144 Lab0 Networking Warmup
Lab0 如其名,warmup,就利用工具实际感受了一下 Bi-directional reliable byte stream。除了发送e-mail那个部分需要Stanford账号,其他的跟着步骤做就行。
官方让用虚拟机,但太卡了很烦,我直接用的docker。
有个坑是,Fetch a Web page 部分操作要快,不然很快就timeout了。
Writing Webget
代码部分:
1 | void get_URL(const string &host, const string &path) { |
也就是模仿着之前 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是满的。
于是整个代码会变得更加优雅简洁。具体实现可见这篇博客