0%

计算机网络练习

语言考试的事情终于可以暂时放一下了,最近重新看起了计算机网络的网课,感觉自己算法这块还是得加强一二,得刷题啊。

12/25

网课笔记啥的我实在是没时间做(),就记录一下自己做的网课作业吧

作业要求在这里:实验0

1.实现一个webget程序,使用操作系统的TCP协助和流套接抽象获取网络上的网页

主要需要编写的就是这一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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.
TCPSocket sock1;
sock1.connect(Address(host, "80"));

string req = "GET ";
req.append(path);
//一开始写的时候根本没理解“每行都需要\r\n“的意思,
//只在整个链接的最后使用了\r\n,可把我难住了
req += " HTTP/1.1\r\nHost: ";
req.append(host);
//这里的Connection: close没有的话会持续输出空行,或者等待后关闭(取决于具体代码)
//后面的sock1.close();感觉在提供输出上没啥用,可能是作用于本机socket的运行状态
//req += "\r\nConnection: close\r\n\r\n";
req += "\r\n\r\n";


//auto recvd = sock1.read();
sock1.write(req);

// Then you'll need to print out everything the server sends back,
// (not just one call to read() -- everything) until you reach
//谢谢你的原注释这里把eof用双引号括起来哈,我还以为是字符串"eof"呢,结果不是
// the "eof" (end of file).
//看仓库文档的示例之后,我以为需要创建两个socket,一个读一个写,结果是我理解错了
//不过从原理来看也应该是一个socket才对
//auto recvd = sock2.read();
//cout << recvd << endl;

接下来输出的一个错误示例,网上的答案好像也跟这写得差不多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    for(;;){
if(!sock1.eof()){
auto recvd2 = sock1.read();
//在一个链接的过程中究竟读了几次呢?我测试了一下,
//第一次读出了状态码等东西(这个是叫请求头吧?)和网页内容
//第二次读了一个空行
//取决于传输内容的速率,传输过来的内容会随机换行(读的次数也不一样)
cout << recvd2 << endl;
}
else{
sock1.close();
break;
}

}
}

判定的程序会报错不匹配,于是我翻找了一下这个小程序的检验条件,在项目目录下的tests文件夹里,是一串固定的字符

所以接下来这段才是正确的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   string resp;
for(;;){
if(!sock1.eof()){
auto recvd = sock1.read();
//组合在一起就不会有薛定谔的空行了
resp.append(recvd);
}
else{
break;
}
}
string split = "Content-type:";
//要删掉请求头和后面的一个换行和回车,不过我这里如果Content-type的内容字符数变了就gg了
size_t pos = resp.find(split) + 26;
if(pos != string::npos){
//cout << "position:"<< pos << endl;
resp = resp.substr(pos);
}
size_t str_size = resp.size();
//结尾还有一个换行/回车
resp.erase(str_size - 1);
cout << resp << endl;
//记得注释掉原文件里的两行报错

2.在内存中实现一个可靠的字节流

头文件里主要是加一些私有变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class ByteStream {
private:
// Your code here -- add private members as necessary.
std::deque<char> _queue;
size_t _capacity_size;
size_t _written_size;
size_t _read_size;
bool _end_input;
bool _error{}; //!< Flag indicating that the stream suffered an error.
public:
ByteStream(const size_t capacity);

//! \name "Input" interface for the writer
//接口函数还没有试验过
class InputInterface {
private:
ByteStream* _input_stream;
public:
InputInterface(ByteStream* stream) : _input_stream(stream) {}
size_t write(const std::string &data){
return _input_stream->write(data);
}
};
//! 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
//接口函数还没有试验过
class OutputInterface {
private:
ByteStream* _output_stream;
public:
OutputInterface(ByteStream* stream) : _output_stream(stream){}
std::string peek_output(const size_t len){
return _output_stream->peek_output(len);
}
void pop_output(const size_t len){
_output_stream->pop_output(len);
}
};
//! 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;
//!@}
};

因为读写在数据中的位置在两端,需要使用双端队列的数据结构

这些代码只是实现基本功能,如何判断写了多少,没写进去的是否要重写的不用考虑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "byte_stream.hh"

//template <typename... Targs>
//void DUMMY_CODE(Targs &&... /* unused */) {}

using namespace std;
//别忘了初始化类中的变量
ByteStream::ByteStream(const size_t capacity)
: _queue(), _capacity_size(capacity), _written_size(0), _read_size(0), _end_input(false), _error(false){}

size_t ByteStream::write(const string &data) {
//先判断边界条件
if(_end_input)return 0;
//注意内存限制
size_t write_size = min(data.size(), _capacity_size - _queue.size());
_written_size += write_size;
for(size_t i = 0; i < write_size; i++){
_queue.push_back(data[i]);
}
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 pop_size = min(len, _queue.size());
return string(_queue.begin(), _queue.begin() + pop_size);
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
size_t pop_size = min(len, _queue.size());
_read_size += pop_size;
for(size_t i = 0; i < pop_size; i++){
_queue.pop_front();
}
}

//! 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) {
string data = this->peek_output(len);
this->pop_output(len);
return data;
}

void ByteStream::end_input() {_end_input = true;}
bool ByteStream::input_ended() const { return _end_input; }
size_t ByteStream::buffer_size() const { return _queue.size(); }
bool ByteStream::buffer_empty() const { return _queue.empty(); }
bool ByteStream::eof() const { return _end_input && _queue.empty(); }
size_t ByteStream::bytes_written() const { return _written_size; }
size_t ByteStream::bytes_read() const { return _read_size; }
size_t ByteStream::remaining_capacity() const { return _capacity_size - _queue.size(); }