stanford cs144 networks lab 0 networking warmup

Networking by hand

Fetch a Web page

在虚拟机上运行 telnet cs144.keithw.org http ,这句话的意思是让 telnet 程序在你的计算机和另外一个计算机(名字叫 cs144.keithw.org)之间建立一个可靠的字节流连接,并且该计算机上运行着特定的服务:http (Hyper-Text Transfer Protocol 超文本传输服务)

在这里插入图片描述
键入 GET /hello HTTP/1.1 ,这句话告诉服务器 URL 的路径(path)

键入 Host: cs144.keithw.org 这告诉服务器 URL 的主机(host)

然后再敲一次回车,则这个 HTTP 请求就会被发出。

效果如下:

在这里插入图片描述
这等价于获取网页 http://cs144.keithw.org/hello 的信息

Listening and connecting

现在我们已经知道 telnet 可以作为客户端程序,现在可以试一下服务器端:可以等待用户连接。

运行:netcat -v -l -p 9090netcat 是一个能够读写TCP、UDP连接的工具, -v 是 verbose ,显示详细信息,-l 表示 listen,是在监听是否有客户端连接,-p 指的是监听端口)

效果如下:
在这里插入图片描述
打开另一个终端,运行 telnet localhost 9090

效果如下:
在这里插入图片描述
而之前的服务器端会显示:

在这里插入图片描述
现在从客户端发消息,服务器端便会接收到。

在这里插入图片描述
在这里插入图片描述

Writing a network program using an OS stream socket

下面来写一个程序来读一个网页,用的是操作系统提供的 stream socket ,这个 socket 看起来就像一个普通的文件描述子(和磁盘上的文件相似,也和 stdin,stdout 相似)当两个 stream sockets 连接起来时,写到其中一个 socket 中的字节会以相同的顺序从另一个 socket 中出来。

但是现实里因特网并不提供一个可靠的字节传输,Internet 做的事情叫做尽最大可能(best effort)地向目的地交付短数据,叫做 Internet datagrams 。每个数据报文都包含一些元信息(headers),指定了源地址和目标地址,以及一些净荷载(payload)(最大约为 1500 字节)。

尽管网络尽可能地去发所有地报文,但是这些报文会:

  1. 丢失
  2. 失序
  3. 出错
  4. 重复

通常是由源和目标处地计算机操作系统来将这些“尽可能”交付地报文转化为“可靠的字节流传输”。

两个计算机必须共同协作来确保流中的每个字节都最终能够按照顺序交付。并且他们要告诉彼此自己准备接收的数据大小,这些是通过 1981 年建立的 Transmission Control Protocol (TCP)来实现的。

Let’s get started—fetching and building the starter code

首先 git clone https://github.com/cs144/sponge ,假如慢的话可以先把仓库移到 gitee.com 上,然后再 clone。注意 cs144 提供的虚拟机是可以通过 ssh -p 2222 cs144@localhost 从外面连上的,配合 xshell 这类的工具,用起来不要太爽hhh

然后是一系列的命令:

在这里插入图片描述
在这里插入图片描述

Modern C++: mostly safe but still fast and low-level

作业主要用的是现代的 C++ 风格,参考 http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines

其基础思想是使每一个对象都有尽可能少的公有接口(public interface),其内部有很多安全检查,使得很难被误用,并且能够自己释放资源(而不是需要成对的 malloc/freenew/delete

这种风格叫做:“Resource acquisition is initialization” (RAII,资源获取即为初始化 )

在这里插入图片描述

Reading the Sponge documentation

https://cs144.github.io/doc/lab0

其中 Socket 是一种 FileDescriptorTCPSocket 是一种 Socket

FileDescriptor

这个类是一个 reference-count handle to a file descriptor

继承关系:

在这里插入图片描述

Socket

这是网络 socket(TCP,UDP,…) 的基类,继承关系如下:

在这里插入图片描述

TCPSocket

是 TCP socket 的一个 wrapper ,继承关系如下:

在这里插入图片描述

Address

Wrapper around IPv4 addresses and DNS operations.

Inside libsponge/util

file_descriptor.hh
 #ifndef SPONGE_LIBSPONGE_FILE_DESCRIPTOR_HH
 #define SPONGE_LIBSPONGE_FILE_DESCRIPTOR_HH
 
 #include "buffer.hh"
 
 #include <array>
 #include <cstddef>
 #include <limits>
 #include <memory>
 
 class FileDescriptor {
     class FDWrapper {
       public:
         int _fd;                    
         bool _eof = false;          
         bool _closed = false;       
         unsigned _read_count = 0;   
         unsigned _write_count = 0;  
 
         explicit FDWrapper(const int fd);
         ~FDWrapper();
         void close();
 
 
         FDWrapper(const FDWrapper &other) = delete;
         FDWrapper &operator=(const FDWrapper &other) = delete;
         FDWrapper(FDWrapper &&other) = delete;
         FDWrapper &operator=(FDWrapper &&other) = delete;
     };
 
     std::shared_ptr<FDWrapper> _internal_fd;
 
     // private constructor used to duplicate the FileDescriptor (increase the reference count)
     explicit FileDescriptor(std::shared_ptr<FDWrapper> other_shared_ptr);
 
   protected:
     void register_read() { ++_internal_fd->_read_count; }    
     void register_write() { ++_internal_fd->_write_count; }  
 
   public:
     explicit FileDescriptor(const int fd);
 
     ~FileDescriptor() = default;
 
     std::string read(const size_t limit = std::numeric_limits<size_t>::max());
 
     void read(std::string &str, const size_t limit = std::numeric_limits<size_t>::max());
 
     size_t write(const char *str, const bool write_all = true) { return write(BufferViewList(str), write_all); }
 
     size_t write(const std::string &str, const bool write_all = true) { return write(BufferViewList(str), write_all); }
 
     size_t write(BufferViewList buffer, const bool write_all = true);
 
     void close() { _internal_fd->close(); }
 
     FileDescriptor duplicate() const;
 
     void set_blocking(const bool blocking_state);
 
     int fd_num() const { return _internal_fd->_fd; }                         
     bool eof() const { return _internal_fd->_eof; }                          
     bool closed() const { return _internal_fd->_closed; }                    
     unsigned int read_count() const { return _internal_fd->_read_count; }    
     unsigned int write_count() const { return _internal_fd->_write_count; }  
 
     FileDescriptor(const FileDescriptor &other) = delete;             
     FileDescriptor &operator=(const FileDescriptor &other) = delete;  
     FileDescriptor(FileDescriptor &&other) = default;                 
     FileDescriptor &operator=(FileDescriptor &&other) = default;      
 };
 
 
 #endif  // SPONGE_LIBSPONGE_FILE_DESCRIPTOR_HH

socket.hh
 #ifndef SPONGE_LIBSPONGE_SOCKET_HH
 #define SPONGE_LIBSPONGE_SOCKET_HH
 
 #include "address.hh"
 #include "file_descriptor.hh"
 
 #include <cstdint>
 #include <functional>
 #include <string>
 #include <sys/socket.h>
 
 class Socket : public FileDescriptor {
   private:
     Address get_address(const std::string &name_of_function,
                         const std::function<int(int, sockaddr *, socklen_t *)> &function) const;
 
   protected:
     Socket(const int domain, const int type);
 
     Socket(FileDescriptor &&fd, const int domain, const int type);
 
     template <typename option_type>
     void setsockopt(const int level, const int option, const option_type &option_value);
 
   public:
     void bind(const Address &address);
 
     void connect(const Address &address);
 
     void shutdown(const int how);
 
     Address local_address() const;
     Address peer_address() const;
 
     void set_reuseaddr();
 };
 
 class UDPSocket : public Socket {
   protected:
     explicit UDPSocket(FileDescriptor &&fd) : Socket(std::move(fd), AF_INET, SOCK_DGRAM) {}
 
   public:
     UDPSocket() : Socket(AF_INET, SOCK_DGRAM) {}
 
     struct received_datagram {
         Address source_address;  
         std::string payload;     
     };
 
     received_datagram recv(const size_t mtu = 65536);
 
     void recv(received_datagram &datagram, const size_t mtu = 65536);
 
     void sendto(const Address &destination, const BufferViewList &payload);
 
     void send(const BufferViewList &payload);
 };
 
 
 class TCPSocket : public Socket {
   private:
     explicit TCPSocket(FileDescriptor &&fd) : Socket(std::move(fd), AF_INET, SOCK_STREAM) {}
 
   public:
     TCPSocket() : Socket(AF_INET, SOCK_STREAM) {}
 
     void listen(const int backlog = 16);
 
     TCPSocket accept();
 };
 
 
 class LocalStreamSocket : public Socket {
   public:
     explicit LocalStreamSocket(FileDescriptor &&fd) : Socket(std::move(fd), AF_UNIX, SOCK_STREAM) {}
 };
 
 
 #endif  // SPONGE_LIBSPONGE_SOCKET_HH
address.hh
 #ifndef SPONGE_LIBSPONGE_ADDRESS_HH
 #define SPONGE_LIBSPONGE_ADDRESS_HH
 
 #include <cstddef>
 #include <cstdint>
 #include <netdb.h>
 #include <netinet/in.h>
 #include <string>
 #include <sys/socket.h>
 #include <utility>
 
 class Address {
   public:
     class Raw {
       public:
         sockaddr_storage storage{};  
         operator sockaddr *();
         operator const sockaddr *() const;
     };
 
   private:
     socklen_t _size;  
     Raw _address{};   
 
     Address(const std::string &node, const std::string &service, const addrinfo &hints);
 
   public:
     Address(const std::string &hostname, const std::string &service);
 
     Address(const std::string &ip, const std::uint16_t port);
 
     Address(const sockaddr *addr, const std::size_t size);
 
     bool operator==(const Address &other) const;
     bool operator!=(const Address &other) const { return not operator==(other); }
 
 
     std::pair<std::string, uint16_t> ip_port() const;
     std::string ip() const { return ip_port().first; }
     uint16_t port() const { return ip_port().second; }
     uint32_t ipv4_numeric() const;
     std::string to_string() const;
 
 
     socklen_t size() const { return _size; }
     operator const sockaddr *() const { return _address; }
 };
 
 
 #endif  // SPONGE_LIBSPONGE_ADDRESS_HH

Writing webget \verb|webget| webget

填空的代码如下:

   TCPSocket sock1{};
    sock1.connect(Address(host, "http"));
    sock1.write("GET "+path+" HTTP/1.1\r\nHost: "+host+"\r\n\r\n");
    sock1.shutdown(SHUT_WR);
    while(!sock1.eof()){
        auto recvd = sock1.read();
        cout << recvd ;
    }

在这里插入图片描述
在这里插入图片描述

An in-memory reliable byte stream

这部分的实现我出现的问题是 EOF 的判定,相关初始化以及改变以及整体代码如下:

// byte_stream.hh
#ifndef SPONGE_LIBSPONGE_BYTE_STREAM_HH
#define SPONGE_LIBSPONGE_BYTE_STREAM_HH

#include <cstddef>
#include <cstdint>
#include <deque>
#include <list>
#include <string>
#include <utility>

//! \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.
    size_t _capacity;
    size_t _used;
    std::string _data;
    bool _input_ended;
    size_t _byte_written;
    size_t _byte_read;
    bool _eof;
    bool _error{};  //!< 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 vector of bytes read
    std::string read(const size_t len) {
        const auto ret = peek_output(len);
        pop_output(len);
        return ret;
    }

    //! \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

// byte_stream.cc
#include "byte_stream.hh"

#include <algorithm>
#include <iterator>
#include <stdexcept>

// 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):
    _capacity(capacity),_used(0),_data(""),
    _input_ended(false),_byte_written(0),
    _byte_read(0),_eof(false){}// DUMMY_CODE(capacity); }

size_t ByteStream::write(const string &data) {
    // DUMMY_CODE(data);
    size_t r_capacity = remaining_capacity();
    if(r_capacity == 0) 
	return 0;
    size_t write_size = min(data.length(), r_capacity);
    _data.append(data, 0, write_size);
    _byte_written += write_size;
    _used += write_size;
    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 true_len = min(len, _used);
    return _data.substr(0, true_len);
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) { 
    size_t true_len = min(len, _used);
    _data  = _data.substr(true_len, _used - true_len);
    _used -= true_len;
    _byte_read += true_len;
    if(buffer_empty() && input_ended())
	_eof = true;
}

void ByteStream::end_input() {
    _input_ended = true;
    if(buffer_empty())
	_eof = true;
}

bool ByteStream::input_ended() const { return _input_ended; }

size_t ByteStream::buffer_size() const { return _used; }

bool ByteStream::buffer_empty() const { return _used == 0; }

bool ByteStream::eof() const { return _eof; }

size_t ByteStream::bytes_written() const { return _byte_written; }

size_t ByteStream::bytes_read() const { return _byte_read; }

size_t ByteStream::remaining_capacity() const { return _capacity - _used; }

但是有一个 test 是超时的… 我怀疑可能是测试抓取了一个墙外的网站吧。。

在这里插入图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值