diff --git a/TODO b/TODO index 753ad13..bf06b5c 100644 --- a/TODO +++ b/TODO @@ -4,7 +4,7 @@ [DONE] Figure out packet double-sending issue [if delay > 1/50 sec?] -Graceful exit / server shutdown +[DONE] Graceful exit / server shutdown Make roaming much smoother (with error message placement) diff --git a/networktransport.cpp b/networktransport.cpp index 72d7407..5027f73 100644 --- a/networktransport.cpp +++ b/networktransport.cpp @@ -12,7 +12,8 @@ Transport::Transport( MyState &initial_state, RemoteState sender( &connection, initial_state ), received_states( 1, TimestampedState( timestamp(), 0, initial_remote ) ), last_receiver_state( initial_remote ), - fragments() + fragments(), + verbose( false ) { /* server */ } @@ -24,7 +25,8 @@ Transport::Transport( MyState &initial_state, RemoteState sender( &connection, initial_state ), received_states( 1, TimestampedState( timestamp(), 0, initial_remote ) ), last_receiver_state( initial_remote ), - fragments() + fragments(), + verbose( false ) { /* client */ } @@ -82,11 +84,9 @@ void Transport::recv( void ) return; } } - /* if ( verbose ) fprintf( stderr, "[%d] Received state %d [ack %d]\n", - (int)timestamp() % 100000, (int)new_state.num, (int)inst.ack_num ); - */ + (int)timestamp() % 100000, (int)new_state.num, (int)inst.ack_num() ); received_states.push_back( new_state ); sender.set_ack_num( received_states.back().num ); } @@ -128,4 +128,3 @@ string Transport::get_remote_diff( void ) return ret; } - diff --git a/networktransport.hpp b/networktransport.hpp index be3699f..d8a76f9 100644 --- a/networktransport.hpp +++ b/networktransport.hpp @@ -30,8 +30,8 @@ namespace Network { /* simple receiver */ list< TimestampedState > received_states; RemoteState last_receiver_state; /* the state we were in when user last queried state */ - FragmentAssembly fragments; + bool verbose; public: Transport( MyState &initial_state, RemoteState &initial_remote ); @@ -47,14 +47,24 @@ namespace Network { /* Blocks waiting for a packet. */ void recv( void ); + /* Find diff between last receiver state and current remote state, then rationalize states. */ + string get_remote_diff( void ); + + /* Shut down other side of connection. */ + /* Illegal to change current_state after this. */ + void start_shutdown( void ) { sender.start_shutdown(); } + bool shutdown_in_progress( void ) { return sender.get_shutdown_in_progress(); } + bool shutdown_acknowledged( void ) { return sender.get_shutdown_acknowledged(); } + + /* Other side has requested shutdown and we have sent one ACK */ + bool counterparty_shutdown_ack_sent( void ) { return sender.get_counterparty_shutdown_acknowledged(); } + int port( void ) { return connection.port(); } string get_key( void ) { return connection.get_key(); } MyState &get_current_state( void ) { return sender.get_current_state(); } void set_current_state( const MyState &x ) { sender.set_current_state( x ); } - string get_remote_diff( void ); - typename list< TimestampedState >::iterator begin( void ) { return received_states.begin(); } typename list< TimestampedState >::iterator end( void ) { return received_states.end(); } @@ -62,7 +72,7 @@ namespace Network { int fd( void ) { return connection.fd(); } - void set_verbose( void ) { sender.set_verbose(); } + void set_verbose( void ) { sender.set_verbose(); verbose = true; } }; } diff --git a/stm-server.cpp b/stm-server.cpp index 696a068..a7c0bd3 100644 --- a/stm-server.cpp +++ b/stm-server.cpp @@ -168,7 +168,9 @@ void serve( int host_fd ) } /* update client with new state of terminal */ - network.set_current_state( terminal ); + if ( !network.shutdown_in_progress() ) { + network.set_current_state( terminal ); + } /* write any writeback octets back to the host */ if ( swrite( host_fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { @@ -194,16 +196,35 @@ void serve( int host_fd ) string terminal_to_host = terminal.act( string( buf, bytes_read ) ); /* update client with new state of terminal */ - network.set_current_state( terminal ); - + if ( !network.shutdown_in_progress() ) { + network.set_current_state( terminal ); + } + /* write any writeback octets back to the host */ if ( swrite( host_fd, terminal_to_host.c_str(), terminal_to_host.length() ) < 0 ) { break; } } - if ( (pollfds[ 0 ].revents | pollfds[ 1 ].revents) + if ( (pollfds[ 0 ].revents) & (POLLERR | POLLHUP | POLLNVAL) ) { + /* network problem */ + break; + } + + if ( (pollfds[ 1 ].revents) + & (POLLERR | POLLHUP | POLLNVAL) ) { + /* host problem */ + network.start_shutdown(); + } + + /* quit if our shutdown has been acknowledged */ + if ( network.shutdown_in_progress() && network.shutdown_acknowledged() ) { + break; + } + + /* quit if we received and acknowledged a shutdown request */ + if ( network.counterparty_shutdown_ack_sent() ) { break; } diff --git a/stm.cpp b/stm.cpp index 52a11db..ce3296b 100644 --- a/stm.cpp +++ b/stm.cpp @@ -174,8 +174,10 @@ void client( const char *ip, int port, const char *key ) return; } - for ( int i = 0; i < bytes_read; i++ ) { - network.get_current_state().push_back( Parser::UserByte( buf[ i ] ) ); + if ( !network.shutdown_in_progress() ) { + for ( int i = 0; i < bytes_read; i++ ) { + network.get_current_state().push_back( Parser::UserByte( buf[ i ] ) ); + } } } @@ -193,8 +195,10 @@ void client( const char *ip, int port, const char *key ) /* tell remote emulator */ Parser::Resize res( window_size.ws_col, window_size.ws_row ); - - network.get_current_state().push_back( res ); + + if ( !network.shutdown_in_progress() ) { + network.get_current_state().push_back( res ); + } /* tell local emulator -- there is probably a safer way to do this */ for ( list< Network::TimestampedState >::iterator i = network.begin(); @@ -204,8 +208,25 @@ void client( const char *ip, int port, const char *key ) } } - if ( (pollfds[ 0 ].revents | pollfds[ 1 ].revents) + if ( (pollfds[ 0 ].revents) & (POLLERR | POLLHUP | POLLNVAL) ) { + /* network problem */ + break; + } + + if ( (pollfds[ 1 ].revents) + & (POLLERR | POLLHUP | POLLNVAL) ) { + /* user problem */ + network.start_shutdown(); + } + + /* quit if our shutdown has been acknowledged */ + if ( network.shutdown_in_progress() && network.shutdown_acknowledged() ) { + break; + } + + /* quit if we received and acknowledged a shutdown request */ + if ( network.counterparty_shutdown_ack_sent() ) { break; } diff --git a/transportsender.cpp b/transportsender.cpp index 22b999f..432673b 100644 --- a/transportsender.cpp +++ b/transportsender.cpp @@ -14,6 +14,7 @@ TransportSender::TransportSender( Connection *s_connection, MyState &in next_ack_time( timestamp() ), next_send_time( timestamp() ), verbose( false ), + shutdown_in_progress( false ), ack_num( 0 ) { } @@ -50,10 +51,15 @@ int TransportSender::wait_time( void ) if ( next_send_time < sent_states.back().timestamp + send_interval() ) { next_send_time = sent_states.back().timestamp + send_interval(); } + } - if ( next_send_time < next_wakeup ) { - next_wakeup = next_send_time; - } + /* speed up shutdown sequence */ + if ( shutdown_in_progress || (ack_num == uint64_t(-1)) ) { + next_ack_time = sent_states.back().timestamp + send_interval(); + } + + if ( next_send_time < next_wakeup ) { + next_wakeup = next_send_time; } if ( !connection->get_attached() ) { @@ -110,6 +116,11 @@ void TransportSender::send_empty_ack( void ) uint64_t new_num = sent_states.back().num + 1; + /* special case for shutdown sequence */ + if ( shutdown_in_progress ) { + new_num = uint64_t( -1 ); + } + send_in_fragments( "", new_num, false ); sent_states.push_back( TimestampedState( sent_states.back().timestamp, new_num, current_state ) ); next_ack_time = timestamp() + ACK_INTERVAL; @@ -125,6 +136,11 @@ void TransportSender::send_to_receiver( string diff ) new_num = sent_states.back().num + 1; } + /* special case for shutdown sequence */ + if ( shutdown_in_progress ) { + new_num = uint64_t( -1 ); + } + bool done = false; int MTU_tries = 0; while ( !done ) { @@ -157,7 +173,7 @@ void TransportSender::send_to_receiver( string diff ) assumed_receiver_state = sent_states.end(); assumed_receiver_state--; next_ack_time = timestamp() + ACK_INTERVAL; - next_send_time = -1; + next_send_time = uint64_t(-1); } template diff --git a/transportsender.hpp b/transportsender.hpp index a36eddc..67605f9 100644 --- a/transportsender.hpp +++ b/transportsender.hpp @@ -53,6 +53,7 @@ namespace Network { uint64_t next_send_time; bool verbose; + bool shutdown_in_progress; /* information about receiver state */ uint64_t ack_num; @@ -67,15 +68,24 @@ namespace Network { /* Returns the number of ms to wait until next possible event. */ int wait_time( void ); - /* executed upon receipt of ack */ + /* Executed upon receipt of ack */ void process_acknowledgment_through( uint64_t ack_num ); - /* getters and setters */ - MyState &get_current_state( void ) { return current_state; } - void set_current_state( const MyState &x ) { current_state = x; } - void set_verbose( void ) { verbose = true; } + /* Executed upon entry to new receiver state */ void set_ack_num( uint64_t s_ack_num ) { ack_num = s_ack_num; } + /* Starts shutdown sequence */ + void start_shutdown( void ) { shutdown_in_progress = true; } + bool get_shutdown_in_progress( void ) { return shutdown_in_progress; } + bool get_shutdown_acknowledged( void ) { return sent_states.front().num == uint64_t(-1); } + bool get_counterparty_shutdown_acknowledged( void ) { return last_instruction_sent.ack_num() == uint64_t(-1); } + + /* Misc. getters and setters */ + /* Cannot modify current_state while shutdown in progress */ + MyState &get_current_state( void ) { assert( !shutdown_in_progress ); return current_state; } + void set_current_state( const MyState &x ) { assert( !shutdown_in_progress ); current_state = x; } + void set_verbose( void ) { verbose = true; } + /* nonexistent methods to satisfy -Weffc++ */ TransportSender( const TransportSender &x ); TransportSender & operator=( const TransportSender &x );