CS144-Lab0-networking warmup

lab 地址 :lab0-doc
代碼實現:lab0-code

1. 目標

1.1 獲取網頁內容

實現 webget。一個使用操作系統的TCP支持和 Socket 抽象通過 Internet 獲取網頁的程序。步驟大致如下:
1.從構建目錄中,打開文件/文本編輯器或IDE中的 apps/webget.cc
2.在getURL函數中,找到以 “//Your code here” 開頭的註釋
3.使用前面使用的HTTP(Web)請求格式實現本文件中描述的簡單Web客戶端。使用 TCPSocketAddress 類。

  1. HTTP中,每行必須以 “\r\n” 結尾
  2. 請求中需要包含“Connection:Close”一行。這告訴服務器,在這一次之後,它不應該等待您的客戶端再發送任何請求。相反,服務器將發送一個回覆,然後立即結束其輸出字節流(從服務器套接字到套接字的字節流)。您將發現傳入的字節流已經結束,因爲當您讀取來自服務器的整個字節流時,套接字將到達“EOF”(文件結尾)。這就是您的客戶機如何知道服務器已完成其回覆。
  3. 確保讀取並打印服務器的所有輸出,直到套接字到達“EOF”(文件結尾)-僅調用一次讀取是不夠的。

1.2 可靠字節流

爲了完成本週的實驗,您將在一臺計算機的內存中實現一個提供這種抽象的對象:ByteStream

  1. 字節寫在“輸入”端,可以從“輸出”端以相同的順序讀取。
  2. ByteStream 是有限的:寫入器可以結束輸入,然後再也不能寫入字節。
  3. 當讀取器讀取到流的末尾時,它將到達“EOF”(文件末尾),無法讀取更多字節。
  4. ByteStream 被初始化爲具有特定的“容量”:在任何給定時間點,它願意存儲在自己的內存中的最大字節數。ByteStream 將限制寫入器在任何給定時刻的寫入量,以確保流不會超過其存儲容量。
  5. 當讀取器讀取字節並將其從流中排出時,寫入器可以寫入更多字節。
  6. ByteStream 用於單個線程,您不必擔心併發寫入器/讀取器、鎖定或競爭條件。

2. 實現

2.1 webget

實現上比較簡單,主要就是:

  • 創建 socket
  • 連接目標 host,port 爲默認的 http 端口
  • write 寫入 http header
  • read socket 並打印到終端
// should use TCPClient
void get_URL(const string &host, const string &path) {
	// Your code here.
	// 1. init socket
	TCPSocket socket;
	
	// 2. connect to host
	socket.connect(Address(host, "http"));
	
	// 3. send request path
	socket.write("GET " + path + " HTTP/1.1\r\n");
	socket.write("HOST: " + host + "\r\n");
	socket.write("Connection: close\r\n");
	socket.write("\r\n");
	
	// 4. read response data
	while(!socket.eof())
		cout << socket.read();

// 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).
}

2.2 ByteSteam

實現一個可靠的字節流類,支持 寫入 & 讀出,有固定的 capacity,從功能上來說,ByteStream 與 隊列的特性非常相似,先進先出,寫入的時候順序排列在字節流後面,讀取的時候從頭部開始讀取並且 pop 出來。
這裏選擇使用 vector 作爲字節流容器(感覺用 queue 就太簡單了其次感覺效率沒有 vector 高),初始化時直接 reserve 分配好空間,防止 push_back 時 copy 數據造成額外消耗。
讀取和寫入的接口如下:

// Write a string of bytes into the stream. Write as many // as will fit, and return the number of bytes written.
size_t write(const std::string &data);
// Returns the number of additional bytes that the stream has space for

std::string read(const size_t len);

寫入和讀取的思路如下,添加 begin 和 end 兩個變量用來維護區間,寫入時正常 push_back 字符,讀取時只需要移動 end 即可,使用 beginend 的時候再對 capacity 取 mod 即可:

核心代碼如下(完整參考 lab0-code

//! 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 read_result;
	int read_size = min(buffer_size(), len);
	for (int i = _end; i < _end + read_size; i++) {
		int real_index = i % _capacity;
		read_result += _data[real_index];
	}
	_total_read += read_size;
	_end += read_size;
	return read_result;
}

size_t ByteStream::write(const string &data) {
	int write_size = min(data.size(), remaining_capacity());
	for (int i = _begin, j = 0; i < _begin + write_size; i++, j++) {
		int real_index = i % _capacity;
		_data[real_index] = data[j];
	}
	_begin += write_size;
	_total_written += write_size;
	return write_size;
}

其次有一點需要注意的是,ByteStreameof 判斷條件是需要 不會再有寫入(調用了 end_input),且緩存都被讀取完畢,纔會返回 true

3. 測試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章