编写webget,刚开始时报getaddrinfo(cs144.keithw.org, http): Servname not supported for ai_socktype,查阅发现getaddrinfo是从/etc/services查询服务对应的端口,而我使用的docker环境中没有这一文件,从主机上拷贝过来即可.
// Helper functions (provided) to access the ByteStream's Reader and Writer interfaces Reader& reader(); const Reader& reader()const; Writer& writer(); const Writer& writer()const;
voidset_error(){ error_ = true; }; // Signal that the stream suffered an error. boolhas_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_; std::deque<char> buffer_ {}; bool error_ {}; bool closed_ {}; uint64_t bytes_pushed_ {}; uint64_t bytes_popped_ {}; std::string tmp_buf_; };
classWriter : public ByteStream { public: voidpush( std::string data ); // Push data to stream, but only as much as available capacity allows. voidclose(); // Signal that the stream has reached its ending. Nothing more will be written.
boolis_closed()const; // Has the stream been closed? uint64_tavailable_capacity()const; // How many bytes can be pushed to the stream right now? uint64_tbytes_pushed()const; // Total number of bytes cumulatively pushed to the stream };
classReader : public ByteStream { public: std::string_view peek()const; // Peek at the next bytes in the buffer voidpop( uint64_t len ); // Remove `len` bytes from the buffer
boolis_finished()const; // Is the stream finished (closed and fully popped)? uint64_tbytes_buffered()const; // Number of bytes currently buffered (pushed and not popped) uint64_tbytes_popped()const; // Total number of bytes cumulatively popped from stream };
/* * read: A (provided) helper function thats peeks and pops up to `len` bytes * from a ByteStream Reader into a string; */ voidread( Reader& reader, uint64_t len, std::string& out );
classReassembler { public: // Construct Reassembler to write into given ByteStream. explicitReassembler( ByteStream&& output );
/* * Insert a new substring to be reassembled into a ByteStream. * `first_index`: the index of the first byte of the substring * `data`: the substring itself * `is_last_substring`: this substring represents the end of the stream * `output`: a mutable reference to the Writer * * The Reassembler's job is to reassemble the indexed substrings (possibly out-of-order * and possibly overlapping) back into the original ByteStream. As soon as the Reassembler * learns the next byte in the stream, it should write it to the output. * * If the Reassembler learns about bytes that fit within the stream's available capacity * but can't yet be written (because earlier bytes remain unknown), it should store them * internally until the gaps are filled in. * * The Reassembler should discard any bytes that lie beyond the stream's available capacity * (i.e., bytes that couldn't be written even if earlier gaps get filled in). * * The Reassembler should close the stream after writing the last byte. */ voidinsert( uint64_t first_index, std::string data, bool is_last_substring );
// How many bytes are stored in the Reassembler itself? uint64_tbytes_pending()const;
classTCPSender { public: /* Construct TCP sender with given default Retransmission Timeout and possible ISN */ TCPSender( ByteStream&& input, Wrap32 isn, uint64_t initial_RTO_ms ) : input_( std::move( input ) ), isn_( isn ), initial_RTO_ms_( initial_RTO_ms ),RTO_ms(initial_RTO_ms) {}
/* Generate an empty TCPSenderMessage */ TCPSenderMessage make_empty_message()const;
/* Receive and process a TCPReceiverMessage from the peer's receiver */ voidreceive( const TCPReceiverMessage& msg );
/* Type of the `transmit` function that the push and tick methods can use to send messages */ using TransmitFunction = std::function<void( const TCPSenderMessage& )>;
/* Push bytes from the outbound stream */ voidpush( const TransmitFunction& transmit );
/* Time has passed by the given # of milliseconds since the last time the tick() method was called */ voidtick( uint64_t ms_since_last_tick, const TransmitFunction& transmit );
// Accessors uint64_tsequence_numbers_in_flight()const; // How many sequence numbers are outstanding? uint64_tconsecutive_retransmissions()const; // How many consecutive *re*transmissions have happened? Writer& writer(){ return input_.writer(); } const Writer& writer()const{ return input_.writer(); }
// Access input stream reader, but const-only (can't read from outside) const Reader& reader()const{ return input_.reader(); }
// A "network interface" that connects IP (the internet layer, or network layer) // with Ethernet (the network access layer, or link layer).
// This module is the lowest layer of a TCP/IP stack // (connecting IP with the lower-layer network protocol, // e.g. Ethernet). But the same module is also used repeatedly // as part of a router: a router generally has many network // interfaces, and the router's job is to route Internet datagrams // between the different interfaces.
// The network interface translates datagrams (coming from the // "customer," e.g. a TCP/IP stack or router) into Ethernet // frames. To fill in the Ethernet destination address, it looks up // the Ethernet address of the next IP hop of each datagram, making // requests with the [Address Resolution Protocol](\ref rfc::rfc826). // In the opposite direction, the network interface accepts Ethernet // frames, checks if they are intended for it, and if so, processes // the the payload depending on its type. If it's an IPv4 datagram, // the network interface passes it up the stack. If it's an ARP // request or reply, the network interface processes the frame // and learns or replies as necessary. classNetworkInterface { public: // An abstraction for the physical output port where the NetworkInterface sends Ethernet frames classOutputPort { public: virtualvoidtransmit( const NetworkInterface& sender, const EthernetFrame& frame )= 0; virtual ~OutputPort() = default; };
// Construct a network interface with given Ethernet (network-access-layer) and IP (internet-layer) // addresses NetworkInterface( std::string_view name, std::shared_ptr<OutputPort> port, const EthernetAddress& ethernet_address, const Address& ip_address );
// Sends an Internet datagram, encapsulated in an Ethernet frame (if it knows the Ethernet destination // address). Will need to use [ARP](\ref rfc::rfc826) to look up the Ethernet destination address for the next // hop. Sending is accomplished by calling `transmit()` (a member variable) on the frame. voidsend_datagram( const InternetDatagram& dgram, const Address& next_hop );
// Receives an Ethernet frame and responds appropriately. // If type is IPv4, pushes the datagram to the datagrams_in queue. // If type is ARP request, learn a mapping from the "sender" fields, and send an ARP reply. // If type is ARP reply, learn a mapping from the "sender" fields. voidrecv_frame( const EthernetFrame& frame );
// Called periodically when time elapses voidtick( size_t ms_since_last_tick );
private: // Human-readable name of the interface std::string name_;
// The physical output port (+ a helper function `transmit` that uses it to send an Ethernet frame) std::shared_ptr<OutputPort> port_; voidtransmit( const EthernetFrame& frame )const{ port_->transmit( *this, frame ); }
// Ethernet (known as hardware, network-access-layer, or link-layer) address of the interface EthernetAddress ethernet_address_;
// IP (known as internet-layer or network-layer) address of the interface Address ip_address_;
// Datagrams that have been received std::queue<InternetDatagram> datagrams_received_ {};
// \brief A router that has multiple network interfaces and // performs longest-prefix-match routing between them. classRouter { public: // Add an interface to the router // \param[in] interface an already-constructed network interface // \returns The index of the interface after it has been added to the router size_tadd_interface( std::shared_ptr<NetworkInterface> interface ) { _interfaces.push_back( notnull( "add_interface", std::move( interface ) ) ); return _interfaces.size() - 1; }
// Access an interface by index std::shared_ptr<NetworkInterface> interface( constsize_t N ){ return _interfaces.at( N ); }
// Add a route (a forwarding rule) voidadd_route( uint32_t route_prefix, uint8_t prefix_length, std::optional<Address> next_hop, size_t interface_num );
// Route packets between the interfaces voidroute();
private: // The router's collection of network interfaces std::vector<std::shared_ptr<NetworkInterface>> _interfaces {};
// route_prefix: The "up-to-32-bit" IPv4 address prefix to match the datagram's destination address against // prefix_length: For this route to be applicable, how many high-order (most-significant) bits of // the route_prefix will need to match the corresponding bits of the datagram's destination address? // next_hop: The IP address of the next hop. Will be empty if the network is directly attached to the router (in // which case, the next hop address should be the datagram's final destination). // interface_num: The index of the interface to send the datagram out on. voidRouter::add_route( constuint32_t route_prefix, constuint8_t prefix_length, const optional<Address> next_hop, constsize_t interface_num ) { cerr << "DEBUG: adding route " << Address::from_ipv4_numeric( route_prefix ).ip() << "/" << static_cast<int>( prefix_length ) << " => " << ( next_hop.has_value() ? next_hop->ip() : "(direct)" ) << " on interface " << interface_num << "\n";
// Your code here. RouterTable.emplace(prefix_info(prefix_length,route_prefix), make_pair(interface_num,next_hop)); }
// Go through all the interfaces, and route every incoming datagram to its proper outgoing interface. voidRouter::route() { for(auto pNetInterface : _interfaces) { auto& incoming_dgrams = pNetInterface->datagrams_received(); while(!incoming_dgrams.empty()) { auto& dgram = incoming_dgrams.front(); if(dgram.header.ttl <= 1) { incoming_dgrams.pop(); continue; } for(auto& RTE : RouterTable) { if((dgram.header.dst & RTE.first.mask) == RTE.first.prefix) { --dgram.header.ttl; dgram.header.compute_checksum();