2 Networking by hand
2.1 Fetch a Web page
2.1.1
访问http://cs144.keithw.org/hello,并观察结果
2.1.2
在命令行中输入
telnet cs144.keithw.org http
Trying 104.196.238.229...
Connected to cs144.keithw.org.
Escape character is '^]'.
可以先在一个文件中输入以下字段复制粘贴进命令行,防止输入速度过慢导致断连
GET /hello HTTP/1.1
Host: cs144.keithw.org
Connection: close
输入完的响应如下:
HTTP/1.1 200 OK
Date: Mon, 17 Jan 2022 06:35:25 GMT
Server: Apache
Last-Modified: Thu, 13 Dec 2018 15:45:29 GMT
ETag: "e-57ce93446cb64"
Accept-Ranges: bytes
Content-Length: 14
Connection: close
Content-Type: text/plain
Hello, CS144!
Connection closed by foreign host.
2.1.3 Assignment:
在命令行中输入
telnet cs144.keithw.org http
出现
Trying 104.196.238.229...
Connected to cs144.keithw.org.
Escape character is '^]'.
接着输入
GET /lab0/misaka HTTP/1.1
Host: cs144.keithw.org
Connection: close
得到如下响应
HTTP/1.1 200 OK
Date: Mon, 17 Jan 2022 06:46:16 GMT
Server: Apache
X-You-Said-Your-SunetID-Was: misaka
X-Your-Code-Is: 712264
Content-length: 110
Vary: Accept-Encoding
Connection: close
Content-Type: text/plain
Hello! You told us that your SUNet ID was "misaka". Please see the HTTP headers (above) for your secret code.
Connection closed by foreign host.
2.2 Send yourself an email
由于没有sunetid,采用163邮箱进行代替
telnet smtp.163.com smtp
Trying 220.181.12.11...
Connected to smtp.163.com.
Escape character is '^]'.
220 163.com Anti-spam GT for Coremail System (163com[xxxx])
和163邮箱打个招呼
helo 163.com
返回250 OK
邮箱账户登陆
auth login
334 dXNlcm5hbWU6
[输入163邮箱的base64编码格式]
334 UGFzc3dvcmQ6
[输入base64编码格式的邮箱密码]
235 Authentication successful
MAIL FROM: <xxxxx@163.com> //表示发件人为 xxxxx@163.com
250 Mail OK
RCPT TO: <xxxxx@qq.com> //表示收件人为 xxxxx@qq.com
250 Mail OK
DATA //准备好发送数据了
354 End data with <CR><LF>.<CR><LF>
[邮件内容]
.
250 Mail OK queued as smtp14,EsCowAAX5WUxFuVhrJCNAg--.xxxxxx xxxxxxxxxx
QUIT
221 Bye
Connection closed by foreign host.
在收件邮箱中可以发现收到的邮件
2.3 Listening and connecting
在命令行中输入:
netcat -v -l -p 9090
出现如下问题
netcat: getnameinfo: Temporary failure in name resolution
解决方法:
sudo vim /etc/resolv.conf
添加以下内容并保存
namespace 8.8.8.8
在一个窗口输入:
netcat -v -l -p 9090
新的窗口输入
telnet localhost 9090
即可实现netcat
窗口对telnet
窗口的监听,关闭telnet
窗口就可以关闭这个监听程序
3 Writing a network program using an OS stream socket
3.1 Let’s get started—fetching and building the starter code
git clone https://gitee.com/kangyupl/sponge
git checkout -b master origin/master
mkdir build && cd build
cmake ..
make format
make -j4 && make check_lab0
3.2
代码规范
3.3 Reading the Sponge documentation
- 初学者代码的文档:https://cs144.github.io/doc/lab0/
- Socket是FileDescriptor的一种类型,而TCPSocket是一种Socket
FileDescriptor:https://cs144.github.io/doc/lab0/class_file_descriptor.html
Socket :https://cs144.github.io/doc/lab0/class_socket.html
TCPSocket:https://cs144.github.io/doc/lab0/class_t_c_p_socket.html
Address:https://cs144.github.io/doc/lab0/class_address.html - 观察
libsponge/util
目录下的file descriptor.hh
,socket.hh
,address.hh
三个头文件是怎么定义接口的
file descriptor.hh
//! Write a string, possibly blocking until all is written
size_t write(const char *str, const bool write_all = true) { return write(BufferViewList(str), write_all); }
//! Write a string, possibly blocking until all is written
size_t write(const std::string &str, const bool write_all = true) { return write(BufferViewList(str), write_all); }
//! Write a buffer (or list of buffers), possibly blocking until all is written
size_t write(BufferViewList buffer, const bool write_all = true);
socket.hh
:
//! Connect a socket to a specified peer address with [connect(2)](\ref man2::connect)
//使用[CONNECT(2)](\ref Man2::Connect)将socket连接到指定的对等地址
void connect(const Address &address);
//! Shut down a socket via [shutdown(2)](\ref man2::shutdown)
//通过[Shutdown(2)](\ref Man2::Shutdown)关闭socket
void shutdown(const int how);
address
:
//! Construct by resolving a hostname and servicename.
// 通过 主机名 如 “cs144.keithw.org” 和 服务名 如 “http” 进行构造
Address(const std::string &hostname, const std::string&service);
//! Construct from dotted-quad string ("18.243.0.1") and numeric port.
// 通过 ip地址 和 端口号 进行构造
Address(const std::string &ip, const std::uint16_t port = 0);
//! Construct from a [sockaddr *](@ref man7::socket).
// 通过 原始socket地址 和 地址长度 进行构造
Address(const sockaddr *addr, const std::size_t size);
3.4 Writing webget
It’s time to implement webget, a program to fetch Web pages over the Internet using the
operating system’s TCP support and stream-socket abstraction—just like you did by hand
earlier in this lab.
webget是一个使用操作系统的TCP支持和流套接字抽象在Internet上获取网页的程序,就像您在本实验室前面手动完成的一样。
- 使用HTTP格式实现此文件中描述的简单Web客户端,您之前使用的(Web)请求。使用TCPSocket和Address类。
- 提示:
- 在HTTP中,每行必须以“\r\n”结尾(仅使用“\n”或endl是不够的)。
- 在客户请求中包含“Connection:Close”行。
- 确保读取并打印来自服务器的所有输出,直到socket到达“EOF”(文件结尾)-单个读取调用是不够的。
- 十行左右的代码
代码位置:
/cs144/sponge/apps/webget.cc
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).
Address address(host, "http");
TCPSocket socket;
// 和服务器连接
socket.connect(address);
// 利用字符串拼接,编写request请求
socket.write("GET " + path + " HTTP/1.1\r\n");
socket.write("HOST: " + host + "\r\n");
socket.write("\r\n");
// request结束
socket.shutdown(SHUT_WR);
// content
while (!socket.eof()) {
std::cout << socket.read(1);
}
// close
socket.close();
// cerr << "Function called: get_URL(" << host << ", " << path << ").\n";
// cerr << "Warning: get_URL() has not been implemented yet.\n";
}
- make
$ make
[ 33%] Built target sponge
[ 40%] Built target webget
[ 46%] Built target spongechecks
[ 53%] Built target byte_stream_many_writes
[ 60%] Built target byte_stream_capacity
[ 66%] Built target byte_stream_two_writes
[ 73%] Built target byte_stream_one_write
[ 80%] Built target byte_stream_construction
[ 86%] Built target parser_dt
[ 93%] Built target socket_dt
[100%] Built target address_dt
- 测试
$ ./apps/webget cs144.keithw.org /hello
结果:
HTTP/1.1 200 OK
Date: Mon, 17 Jan 2022 09:16:30 GMT
Server: Apache
Last-Modified: Thu, 13 Dec 2018 15:45:29 GMT
ETag: "e-57ce93446cb64"
Accept-Ranges: bytes
Content-Length: 14
Content-Type: text/plain
Hello, CS144!
- make check_webget
$ make check_webget
结果
[100%] Testing webget...
Test project /home/ubuntu/cs144/sponge/build
Start 31: t_webget
1/1 Test #31: t_webget ......................... Passed 1.05 sec
100% tests passed, 0 tests failed out of 1
Total Test time (real) = 1.05 sec
[100%] Built target check_webget
4 An in-memory reliable byte stream
说明
到现在为止,你已经看到了可靠的无序字节流的抽象是如何在互联网上进行通信的,尽管互联网本身只提供了”尽力而为”(不可靠)的数据报服务。
为了完成本周的实验,你将在一台计算机的内存中实现一个提供这种抽象的对象。(你可能在CS 110中做过类似的事情。) 字节在 “输入”端写入,并可以从”输出”端以同样的顺序读取。字节流是有限的:writer可以结束输入,然后就不能再写了。当reader读到流的末端时,它将到达”EOF”(结束),不再有更多的字节可以被读取。
你的字节流也会受到控制:它被初始化时有一个特定的容量:它愿意在自己的内存中存储的最大字节数。字节流将限制writer可以写多长的字符串,以确保字节流不会超过其存储容量。当reader读取字节并将其从流中耗尽时,writer被允许写入更多。
你的字节流是在单线程中使用的——你不必担心并发的writer/reader、锁定或竞争条件。
字节流是有限的,但在写入器结束输入和结束流之前几乎可以是任意长的时间。您的实现必须能够处理远远超过容量的流。容量限制了在给定点保存在内存中(写入但尚未读取)的字节数,但不限制流的长度。只有一个字节容量的对象仍然可以承载TB和TB长的流,只要写入器保持一次写入一个字节,并且读取器在允许写入器写入下一个字节之前读取每个字节。
文件位置:libsponge/byte stream.hh
和libsponge/byte stream.cc
采用string
实现
libsponge/byte stream.hh
:
#ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH
#define SPONGE_LIBSPONGE_BYTE_STREAM_HH
#include <string>
//! \brief An in-order byte stream.
//! Bytes are written on the "input" side and read from the "output"
//! side. The byte stream is finite: the writer can end the input,
//! and then no more bytes can be written.
class ByteStream {
private:
// Your code here -- add private members as necessary.
// Hint: This doesn't need to be a sophisticated data structure at
// all, but if any of your tests are taking longer than a second,
// that's a sign that you probably want to keep exploring
// different approaches.
std::string bytes = "";
size_t maxsize = 0, hasread = 0, haswritten = 0;
bool is_end = false;
bool _error = false; //!< Flag indicating that the stream suffered an error.
public:
//! Construct a stream with room for `capacity` bytes.
ByteStream(const size_t capacity);
//! \name "Input" interface for the writer
//!@{
//! Write a string of bytes into the stream. Write as many
//! as will fit, and return how many were written.
//! \returns the number of bytes accepted into the stream
size_t write(const std::string &data);
//! \returns the number of additional bytes that the stream has space for
size_t remaining_capacity() const;
//! Signal that the byte stream has reached its ending
void end_input();
//! Indicate that the stream suffered an error.
void set_error() { _error = true; }
//!@}
//! \name "Output" interface for the reader
//!@{
//! Peek at next "len" bytes of the stream
//! \returns a string
std::string peek_output(const size_t len) const;
//! Remove bytes from the buffer
void pop_output(const size_t len);
//! Read (i.e., copy and then pop) the next "len" bytes of the stream
//! \returns a string
std::string read(const size_t len);
//! \returns `true` if the stream input has ended
bool input_ended() const;
//! \returns `true` if the stream has suffered an error
bool error() const { return _error; }
//! \returns the maximum amount that can currently be read from the stream
size_t buffer_size() const;
//! \returns `true` if the buffer is empty
bool buffer_empty() const;
//! \returns `true` if the output has reached the ending
bool eof() const;
//!@}
//! \name General accounting
//!@{
//! Total number of bytes written
size_t bytes_written() const;
//! Total number of bytes popped
size_t bytes_read() const;
//!@}
};
#endif // SPONGE_LIBSPONGE_BYTE_STREAM_HH
libsponge/byte stream.cc
:
#include "byte_stream.hh"
// Dummy implementation of a flow-controlled in-memory byte stream.
// For Lab 0, please replace with a real implementation that passes the
// automated checks run by `make check_lab0`.
// You will need to add private members to the class declaration in `byte_stream.hh`
template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}
using namespace std;
ByteStream::ByteStream(const size_t capacity) {
// DUMMY_CODE(capacity);
maxsize = capacity;
}
size_t ByteStream::write(const string &data) {
// DUMMY_CODE(data);
// return {};
size_t size = (data.size() <= remaining_capacity()) ? data.size() : remaining_capacity();
bytes += data.substr(0, size);
haswritten += size;
return size;
}
//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
// DUMMY_CODE(len);
// return {};
size_t size = (len <= buffer_size()) ? len : buffer_size();
string str = bytes.substr(0, size);
return str;
}
//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
// DUMMY_CODE(len);
size_t size = (len <= buffer_size()) ? len : buffer_size();
bytes.erase(0, size);
hasread += 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) {
// DUMMY_CODE(len);
// return {};
size_t size = (len <= buffer_size()) ? len : buffer_size();
string ret = peek_output(size);
pop_output(size);
return ret;
}
void ByteStream::end_input() { is_end = true; }
bool ByteStream::input_ended() const { return is_end; }
size_t ByteStream::buffer_size() const { return bytes.size(); }
bool ByteStream::buffer_empty() const { return bytes.empty(); }
bool ByteStream::eof() const { return buffer_empty() && is_end; }
size_t ByteStream::bytes_written() const { return haswritten; }
size_t ByteStream::bytes_read() const { return hasread; }
size_t ByteStream::remaining_capacity() const { return maxsize - bytes.size(); }
测试
$ make -j4 && make check_lab0
结果
[ 33%] Built target sponge
[ 40%] Built target spongechecks
[ 50%] Built target parser_dt
[ 53%] Built target webget
[ 60%] Built target socket_dt
[ 66%] Built target address_dt
[ 73%] Built target byte_stream_many_writes
[ 80%] Built target byte_stream_capacity
[ 86%] Built target byte_stream_two_writes
[ 93%] Built target byte_stream_one_write
[100%] Built target byte_stream_construction
[100%] Testing Lab 0...
Test project /home/ubuntu/cs144/sponge/build
Start 26: t_byte_stream_construction
1/9 Test #26: t_byte_stream_construction ....... Passed 0.00 sec
Start 27: t_byte_stream_one_write
2/9 Test #27: t_byte_stream_one_write .......... Passed 0.00 sec
Start 28: t_byte_stream_two_writes
3/9 Test #28: t_byte_stream_two_writes ......... Passed 0.00 sec
Start 29: t_byte_stream_capacity
4/9 Test #29: t_byte_stream_capacity ........... Passed 0.53 sec
Start 30: t_byte_stream_many_writes
5/9 Test #30: t_byte_stream_many_writes ........ Passed 0.01 sec
Start 31: t_webget
6/9 Test #31: t_webget ......................... Passed 1.06 sec
Start 53: t_address_dt
7/9 Test #53: t_address_dt ..................... Passed 0.01 sec
Start 54: t_parser_dt
8/9 Test #54: t_parser_dt ...................... Passed 0.01 sec
Start 55: t_socket_dt
9/9 Test #55: t_socket_dt ...................... Passed 0.01 sec
100% tests passed, 0 tests failed out of 9
Total Test time (real) = 1.65 sec
[100%] Built target check_lab0
有什么资料搭配食用没
难得呀hh,第一次在acwing看到有人发CS144的lab
没看懂是什么
斯坦福计算机网络的Lab