如果本lab中测试点99及之后有问题,考虑是环境问题!!
先测试一下 :传输的段可以使得 ack 增加。
回顾sender中写入_segment_out的情况:
TCPConnection有一个tick方法会被OS周期性调用。当tick被调用时:
当TCPConnection 决定TCP连接结束之后,会释放一个端口号,停止发送确认帧,active()返回false。
有两种方法使得连接结束。
unclean shutdown:接收或者发送RST。这种情况下 ByteStream的状态是error,active()返回false。
clean shutdown:是一种没有error但active() = false的情况。这表示实现了完整可靠地发送以及接收。由于”Two Generals Problem“,我们不可能实现一个 clean shutdown,但是TCP有一个精彩的close方法。从某一方TCPConnection的视角来看,实现clean shutdown 有四个必备条件:
方式一:流传送结束后等待
因为不会传送可靠的对确认帧的确认帧,所以local peer不能明确知道remote peer是否完成了全部流的接收。
但是local peer可以通过等待一段时间,来观察remote peer是否会重新发送任何信息来判断对方是否完全接收了。lab中采用的等待时间是10个initial retransmission。(一般应用中应该是2个MSL,报文在网络中传输的最大时间)
方式二:被动关闭
对方是先结束stream的情况。
在实践的角度中,用一个linger_after_streams_finish 变量来处理。起始值为true。如果 inbound stream 在 outbound stream EOF之前end,这个变量设置为false。
(方式二)在任何时间点,满足1,3条件,且 linger_after_streams_finish = false,这个连接就结束(active() = false)。否则就需要等待(方式一)。
不能用get去拿stl成员
通过seg.length_in_sequence_space() 去判断是否是。
可以回复确认帧。
改动比较多,就直接贴代码了。
// tcp_connection.hh
class TCPConnection {
private:
TCPConfig _cfg;
TCPReceiver _receiver{_cfg.recv_capacity};
TCPSender _sender{_cfg.send_capacity, _cfg.rt_timeout, _cfg.fixed_isn};
//! outbound queue of segments that the TCPConnection wants sent
std::queue<TCPSegment> _segments_out{};
//! Should the TCPConnection stay active (and keep ACKing)
//! for 10 * _cfg.rt_timeout milliseconds after both streams have ended,
//! in case the remote TCPConnection doesn't know we've received its whole stream?
bool _linger_after_streams_finish{true};
bool _isActive{true};
size_t _time_since_last_segment_received{0};
public:
//! \name "Input" interface for the writer
//!@{
//! \brief Initiate a connection by sending a SYN segment
void connect();
//! \brief Write data to the outbound byte stream, and send it over TCP if possible
//! \returns the number of bytes from `data` that were actually written.
size_t write(const std::string &data);
//! \returns the number of `bytes` that can be written right now.
size_t remaining_outbound_capacity() const;
//! \brief Shut down the outbound byte stream (still allows reading incoming data)
void end_input_stream();
//!@}
//! \name "Output" interface for the reader
//!@{
//! \brief The inbound byte stream received from the peer
ByteStream &inbound_stream() { return _receiver.stream_out(); }
ByteStream &outbound_stream() { return _sender.stream_in(); }
const ByteStream &outbound_stream() const { return _sender.stream_in(); }
//!@}
//! \name Accessors used for testing
//!@{
//! \brief number of bytes sent and not yet acknowledged, counting SYN/FIN each as one byte
size_t bytes_in_flight() const;
//! \brief number of bytes not yet reassembled
size_t unassembled_bytes() const;
//! \brief Number of milliseconds since the last segment was received
size_t time_since_last_segment_received() const;
//!< \brief summarize the state of the sender, receiver, and the connection
TCPState state() const { return {_sender, _receiver, active(), _linger_after_streams_finish}; };
//!@}
//! \name Methods for the owner or operating system to call
//!@{
//! Called when a new segment has been received from the network
void segment_received(const TCPSegment &seg);
//! Called periodically when time elapses
void tick(const size_t ms_since_last_tick);
//! \brief TCPSegments that the TCPConnection has enqueued for transmission.
//! \note The owner or operating system will dequeue these and
//! put each one into the payload of a lower-layer datagram (usually Internet datagrams (IP),
//! but could also be user datagrams (UDP) or any other kind).
std::queue<TCPSegment> &segments_out() { return _segments_out; }
//! \brief Is the connection still alive in any way?
//! \returns `true` if either stream is still running or if the TCPConnection is lingering
//! after both streams have finished (e.g. to ACK retransmissions from the peer)
bool active() const;
//!@}
//! when send or receive a RST,set the state.
void comeRST(){
outbound_stream().set_error();
inbound_stream().set_error();
_isActive = false;
};
//! Send the segments in _sender._segments_out
void sendAll();
//! Construct a new connection from a configuration
explicit TCPConnection(const TCPConfig &cfg) : _cfg{cfg} {}
//! \name construction and destruction
//! moving is allowed; copying is disallowed; default construction not possible
//!@{
~TCPConnection(); //!< destructor sends a RST if the connection is still open
TCPConnection() = delete;
TCPConnection(TCPConnection &&other) = default;
TCPConnection &operator=(TCPConnection &&other) = default;
TCPConnection(const TCPConnection &other) = delete;
TCPConnection &operator=(const TCPConnection &other) = delete;
//!@}
};
// tcp_connection.cc
size_t TCPConnection::remaining_outbound_capacity() const { return outbound_stream().remaining_capacity(); }
size_t TCPConnection::bytes_in_flight() const { return _sender.bytes_in_flight(); }
size_t TCPConnection::unassembled_bytes() const { return _receiver.unassembled_bytes(); }
size_t TCPConnection::time_since_last_segment_received() const { return _time_since_last_segment_received; }
void TCPConnection::segment_received(const TCPSegment &seg) {
_time_since_last_segment_received = 0;
// RST set
if(seg.header().rst){
comeRST();
return;
}
// Ignore ACK,when in LISTEN.
if(!seg.header().syn && seg.header().ack && !_receiver._isInit){
return;
}
//! Test the case: the segment will increment the receiver's expt_seq
_receiver.segment_received(seg);
// Set the _linger_after_streams_finish.
if(inbound_stream().get_end_input_flag() && !outbound_stream().eof())
_linger_after_streams_finish = false;
if(seg.header().ack){
_sender.ack_received(seg.header().ackno,seg.header().win);
}
_sender.fill_window();
// If no segment to be sent,send an empty one.
if(seg.length_in_sequence_space() && _sender._segments_out.empty()){
_sender.send_empty_segment();
}
// Add the ackno and window_size on the segment to be sent.
sendAll();
// Passive close
if(!_linger_after_streams_finish && inbound_stream().get_end_input_flag() && outbound_stream().eof() && _sender._next_seqno == _sender._ack_seqno){
_isActive = false;
}
}
bool TCPConnection::active() const { return _isActive; }
size_t TCPConnection::write(const string &data) {
size_t ret = outbound_stream().write(data);
_sender.fill_window();
sendAll();
if(!_linger_after_streams_finish && inbound_stream().get_end_input_flag() && outbound_stream().eof() && _sender._next_seqno == _sender._ack_seqno){
_isActive = false;
}
return ret;
}
//! \param[in] ms_since_last_tick number of milliseconds since the last call to this method
void TCPConnection::tick(const size_t ms_since_last_tick) {
_sender.tick(ms_since_last_tick);
// Exceed the limit,abort the connection.
if(_sender._num_consecutive_retransmissions > TCPConfig::MAX_RETX_ATTEMPTS){
while(!_sender._segments_out.empty())
_sender._segments_out.pop();
_sender.send_empty_segment(true);
sendAll();
// Set the local peer state.
comeRST();
}
// Send the TCPSegments.
sendAll();
_time_since_last_segment_received += ms_since_last_tick;
// Clean shutdown.
if(_time_since_last_segment_received >= 10 * _cfg.rt_timeout && inbound_stream().get_end_input_flag() && outbound_stream().eof() && _sender._next_seqno == _sender._ack_seqno){
_isActive = false;
}
}
void TCPConnection::end_input_stream() {
outbound_stream().end_input();
_sender.fill_window();
sendAll();
if(!_linger_after_streams_finish && inbound_stream().get_end_input_flag() && outbound_stream().eof() && _sender._next_seqno == _sender._ack_seqno){
_isActive = false;
}
}
void TCPConnection::connect() {
_sender.fill_window();
if(_sender._segments_out.size() != 1){
return;
}
//! There is no need to send window_size and ack?
_segments_out.push(_sender._segments_out.front());
_sender._segments_out.pop();
}
void TCPConnection::sendAll(){
while(!_sender._segments_out.empty()){
_sender._segments_out.front().header().ackno = wrap(_receiver.getExpt_seq(),_receiver.get_isn());
_sender._segments_out.front().header().win = _receiver.window_size();
// Set ACK flag
if(_receiver.getExpt_seq() != 0)
_sender._segments_out.front().header().ack = true;
_segments_out.push(_sender._segments_out.front());
_sender._segments_out.pop();
}
}
TCPConnection::~TCPConnection() {
try {
if (active()) {
cerr << "Warning: Unclean shutdown of TCPConnection\n";
// Your code here: need to send a RST segment to the peer
_sender.send_empty_segment(true);
// Set the local peer state.
comeRST();
// Send the TCPSegments.
sendAll();
}
} catch (const exception &e) {
std::cerr << "Exception destructing TCP FSM: " << e.what() << std::endl;
}
}
吐了,测试点99死活过不了,服务器收不到seg,一步一步测试到TCP底层代码,还是收不到。最后认为问题不可能出现在要写的部分,然后clone了一个网上的题解,测试还是过不了,然后确定是环境问题。下载了官方提供的ova文件,进行了测试,测试成功!