CS144 - Lab 0
telnet 發送請求
如圖,很簡單,但是注意輸入時間太久會超時
發郵箱
首先我們需要用命令行去發郵箱,這里我用企業微信郵箱給自己的 qq 郵箱發送~
整個命令如下!
對于其中的參數,其實從英文就可以看出來,首先連接到企業微信郵箱的發送郵箱的服務器,然后輸入 HELO smtp.exmail.qq.com 表示開始進行和 smtp 服務器的會話,然后我們可以輸入 AUTH LOGIN 來鑒權,首先輸入你的郵箱的對應的 base64 的編碼,比如我的郵箱是 rinai@g-rinai.cn 就將他轉換為 base64 編碼就可以了,然后我們還需要一個登錄密碼,注意,這個密碼雖然也需要 base64 編碼,但是并不是需要你的登錄密碼,而是一個密鑰,基本每個郵箱客戶端都會有這樣一個位置給我們提供這樣一個密鑰,比如我是企業微信郵箱,獲取密鑰的位置在這里:
點擊生成新密碼,然后把對應的密碼輸入到 base64 編碼器即可,注意,如果你的碼暴露給別人了,請及時刪除,比如我現在用完就刪了😇
然后鑒權成功,我們可以輸入發送方,接收方,輸入內容,然后輸入 “換行” + “.” 就可以結束了。
結果是成功發送,通過這個方法,你可以給別人發郵件😍😍😍,那么,Next。
netcat 啟動服務器
我們可以使用 netcat -v -l -p 9090
在本地啟動一個服務器,端口為 9090,我們在另一個窗口可以通過 telnet localhost 9090 來連接本地的這個服務器。
編寫 webget
這里就是真正涉及到代碼了,我們需要用套接字實現我們剛剛第一個 telnet 發送請求的功能,這里感覺并不是很難,就是我對套接字編程不太熟悉,很多系統調用都不知道😭,然后我去看了一下 linux 高性能服務器編程里面的套接字編程,就看了幾個 API 就寫出來了,哈哈。
我們首先通過 gethostbyname
來獲取域名對應的 ip 信息,然后創建一個套接字,并與對應的 ip 建立連接,發送我們的請求的內容,這里很簡單,就是我們剛剛 telnet 之后寫的內容,只需要注意一下換行符就可以了,然后從套接字里面循環讀取數據到緩沖區并打印就可以了!
void get_URL(const string& host, const string& path)
{cerr << "Function called: get_URL(" << host << ", " << path << ")\n";// 通過域名解析獲取 ipstruct hostent *server = gethostbyname(host.c_str());if (server == NULL) {cerr << "Error: Could not get host information for " << host << "\n";return;}// 這是一個用來表示 ipv4 地址信息的結構體// 用于套接字編程struct sockaddr_in server_addr;// 填充這個結構體server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = *(u_long*)server->h_addr_list[0];server_addr.sin_port = htons(80);// 本地創建一個套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if ( sockfd < 0 ) {cerr << "Error: Could not create socket\n";return;}// 連接到遠程服務器if (connect(sockfd, (struct sockaddr*) &server_addr, sizeof(server_addr)) < 0 ) {cerr << "Error: Could not connect to server\n";return;}string request = "GET " + path + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n\r\n";if (send(sockfd, request.c_str(), request.size(), 0) < 0) {cerr << "Error: Could not send request\n";return;}char buffer[1024];int bytes_read = 0;while ((bytes_read = recv(sockfd, buffer, sizeof(buffer), 0)) > 0 ) {write(1, buffer, bytes_read);}if (bytes_read < 0) {cerr << "Error: Could not read response\n";return;}close(sockfd);
}
結果如下:
root@r-linux:/home/rinai/project/CS144# cmake --build build --target check_webget
Test project /home/rinai/project/CS144/buildStart 1: compile with bug-checkers
1/2 Test #1: compile with bug-checkers ........ Passed 0.20 secStart 2: t_webget
2/2 Test #2: t_webget ......................... Passed 1.10 sec100% tests passed, 0 tests failed out of 2Total Test time (real) = 1.30 sec
Built target check_webget
內存可靠字節流
這里很簡單,就是寫一個讀寫緩沖區模型,其實跟咱消費者生產者模型還是挺像的哈,主要就是一個緩沖區的狀態管理,這里我不太熟悉 cpp 的生態,這里直接用了 string 類型來表示字節流了。
我的實現:
首先我們需要查看注釋,為所謂的 bytestream 類添加成員字段:
class ByteStream
{
public:explicit ByteStream( uint64_t capacity );// Helper functions (provided) to access the ByteStream's Reader and Writer interfacesReader& reader();const Reader& reader() const;Writer& writer();const Writer& writer() const;void set_error() { error_ = true; }; // Signal that the stream suffered an error.bool has_error() const { return error_; }; // Has the stream had an error?protected:// Please add any additional state to the ByteStream here, and not to the Writer and Reader interfaces.uint64_t capacity_;bool error_ {};// added state:std::string buffer_; uint64_t read_cnt_;uint64_t write_cnt_;bool closed_ {};
};
然后我們需要為 Writer 和 Reader 實現構造函數和對應的讀寫方法。
#include "byte_stream.hh"using namespace std;ByteStream::ByteStream( uint64_t capacity ): capacity_(capacity),buffer_(), read_cnt_(0),write_cnt_(0)
{
}
void Writer::push( string data )
{if (Writer::is_closed()) return;Writer::write_cnt_ += min(Writer::available_capacity(), data.size());Writer::buffer_.append( data );Writer::buffer_.resize( min(Writer::buffer_.size(), capacity_) );return;
}void Writer::close()
{Writer::closed_ = true;
}bool Writer::is_closed() const
{return Writer::closed_;
}uint64_t Writer::available_capacity() const
{return capacity_ - Writer::buffer_.size();
}uint64_t Writer::bytes_pushed() const
{return Writer::write_cnt_;
}string_view Reader::peek() const
{std::string_view view(Writer::ByteStream::buffer_.data(), Writer::ByteStream::buffer_.size());return view;
}void Reader::pop( uint64_t len )
{if (Reader::is_finished())return;if (len > Reader::bytes_buffered())return;printf("before buffer size: %lu\n", Reader::buffer_.size());Reader::buffer_.erase(0, len);printf("after buffer size: %lu\n", Reader::buffer_.size());Reader::read_cnt_ += len;return;
}bool Reader::is_finished() const
{return Reader::closed_ && Reader::buffer_.empty();
}uint64_t Reader::bytes_buffered() const
{return Reader::buffer_.size();
}uint64_t Reader::bytes_popped() const
{return Reader::read_cnt_;
}
寫的時候也需要熟悉一下 cpp 的語法了,感覺這種面向對象的結構還挺新鮮的,跟 go 的結構完全不一樣。
最后:
root@r-linux:/home/rinai/project/CS144# cmake --build build --target check0
Test project /home/rinai/project/CS144/buildStart 1: compile with bug-checkers1/11 Test #1: compile with bug-checkers ........ Passed 0.85 secStart 2: t_webget2/11 Test #2: t_webget ......................... Passed 1.27 secStart 3: byte_stream_basics3/11 Test #3: byte_stream_basics ............... Passed 0.02 secStart 4: byte_stream_capacity4/11 Test #4: byte_stream_capacity ............. Passed 0.01 secStart 5: byte_stream_one_write5/11 Test #5: byte_stream_one_write ............ Passed 0.01 secStart 6: byte_stream_two_writes6/11 Test #6: byte_stream_two_writes ........... Passed 0.02 secStart 7: byte_stream_many_writes7/11 Test #7: byte_stream_many_writes .......... Passed 0.10 secStart 8: byte_stream_stress_test8/11 Test #8: byte_stream_stress_test .......... Passed 0.03 secStart 37: no_skip9/11 Test #37: no_skip .......................... Passed 0.01 secStart 38: compile with optimization
10/11 Test #38: compile with optimization ........ Passed 3.15 secStart 39: byte_stream_speed_testByteStream throughput (pop length 4096): 11.88 Gbit/sByteStream throughput (pop length 128): 2.13 Gbit/sByteStream throughput (pop length 32): 0.56 Gbit/s
11/11 Test #39: byte_stream_speed_test ........... Passed 0.36 sec100% tests passed, 0 tests failed out of 11Total Test time (real) = 5.84 sec
Built target check0
clear