实现流量有限的bytestream,支持读写.
是TCPSender和TCPReceiver与用户层的接口.
app通过bytestream向下TCPSender写入
TCPReceiver通过bytestream向上app交付数据
part0
- telnet : a client program that makes outgoing connections
to programs running on other computers - telnet cs144.keithw.org http
- 打开 本主机和主机cs144.keithw.org之间的 可靠字节流(即建立一条TCP连接)
- 且 双方采用的 应用层协议为 HTTP协议
- telnet之后
1
2
3
4
5GET /hello HTTP/1.1 # 告知server the path part of the url
Host: cs144.keithw.org # 告诉server the host part of the url
Connection: close # 告知server我已经描述完自己的请求,且 server应当立刻关闭这条tcp连接当响应了用户的请求
empty line 回车 # 告知server我已完成HTTP请求
接下来等待server响应即可 - Assignment
part2
- netcat : 建立一个server
- netcat -v -l -p 9090:创建一个可提供telnet服务的tcpserver
webget
get_URL
1
2
3
4
5
6
7
8
9
10
11
12
13
14void get_URL(const string &host, const string &path) {
// get ip and port
Address addr(host,"http"); // getaddrinfo : host -> ip ; http -> port
// connect
TCPSocket tcp_socket;
tcp_socket.connect(addr);
// send req
string request("GET " + path + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n" + "\r\n");
tcp_socket.write(request);
while(!tcp_socket.eof())
{
cout<<tcp_socket.read();
}
}class Address如何解析出要连接的ip和port
- 核心API : getaddrinfo
- 解析hostname 如 cs144.keithw.org 获取IP
- 解析servicename 如 /hello 获取port (/etc/service中记录了servicename和port之间的对应关系)
getaddrinfo
- 通过主机名获取ip:gethostbyname
- 通过服务名获取port:getservbyname
getaddrinfo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
void freeaddrinfo(struct addrinfo *res);
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr; // (关键) 即 IP地址
char *ai_canonname;
struct addrinfo *ai_next;
};
关于hints参数,可以设置其ai_flags,ai_family,ai-socktype,ai_protocol。其余字段须设置为NULL
ByteStream
一言以蔽之: 实现一个容量有限的管道 , 一端为读端一端为写端
要求
- An in-memory reliable byte stream : 在内存中的可靠字节流
- 就是实现个管道pipe,且无需考虑多线程
- 该字节流特性如下
- The byte stream is finite: (the writer can end the input, and then no more bytes can be written). but it can be almost arbitrarily long before the writer ends the input and finishes the stream.
- 有限:写者可以关闭写端,关闭写端后,就不能再向管道中写入字节了。
- EOF:写端已经关闭,且管道中没有未弹出的字节。(这就是所谓的,读到末尾了,即 reach the end of the stream)
- When the reader has read to the end of the stream, it will reach “EOF” (end of file) and no more bytes can be read
- Flow Control
- tcp通过该bytestream向上层app传输字节. 故该bytestream需要具有的flow control流量控制功能:确保发送端不在接收端缓冲区(这里就是stream)已满的情况下发送数据。
- 设定stream的capacity:stream在任意时刻最多有capacity个字节,当字节数量达到capacity时,写端不可继续写入;需要等待读端将字节从stream中读出。读出后写端即可继续写入。
- The byte stream is finite: (the writer can end the input, and then no more bytes can be written). but it can be almost arbitrarily long before the writer ends the input and finishes the stream.
实现
我所做的工作如下
bytestream功能如图
由于需要使用迭代器,且需要快速的pop_front。则byte stream底层数据结构:deque
主要接口如下size_t write(const std::string &data);
- 写data,写入顺序data[0][1][2]… 。Write a string of bytes into the stream. Write as many as will fit, and return the number of bytes written.
1
2
3
4
5
6
7
8size_t ByteStream::write(const string &data) {
size_t bytes_to_write = min(data.size(), _capacity - _stream.size()); // 最多写多少bytes
_bytes_pushed += bytes_to_write;
for (size_t i = 0; i < bytes_to_write; ++i) {
_stream.push_back(data[i]);
}
return bytes_to_write;
}
- 写data,写入顺序data[0][1][2]… 。Write a string of bytes into the stream. Write as many as will fit, and return the number of bytes written.
std::string peek_output(const size_t len) const
- 读取len bytes,但不弹出。bytes will be copied from the output side of the buffer
1
2
3
4
5string ByteStream::peek_output(const size_t len) const{
assert(_stream.size() <= _capacity);
size_t bytes_to_read = min(len, _stream.size());
return string(_stream.begin(),_stream.begin()+bytes_to_read);
}
- 读取len bytes,但不弹出。bytes will be copied from the output side of the buffer
void pop_output(const size_t len);
- 从读端弹出len bytes。len bytes will be removed from the output side of the buffer
1
2
3
4
5
6
7
8void ByteStream::pop_output(const size_t len) {
assert(_stream.size() <= _capacity);
size_t bytes_to_pop = min(len, _stream.size()); // 最多全部弹出
_bytes_popped += bytes_to_pop;
while (bytes_to_pop--) {
_stream.pop_front();
}
}
- 从读端弹出len bytes。len bytes will be removed from the output side of the buffer
eof
1
bool ByteStream::eof() const { return _end && _stream.empty(); }
- pass