First step of sending side of transport layer
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp terminalfunctions.cpp swrite.cpp terminalframebuffer.cpp terminaldispatcher.cpp terminaluserinput.cpp terminaldisplay.cpp network.cpp ntester.cpp ocb.cpp base64.cpp encrypt.cpp decrypt.cpp crypto.cpp
|
source = parse.cpp parserstate.cpp parser.cpp templates.cpp terminal.cpp termemu.cpp parseraction.cpp terminalfunctions.cpp swrite.cpp terminalframebuffer.cpp terminaldispatcher.cpp terminaluserinput.cpp terminaldisplay.cpp network.cpp ntester.cpp ocb.cpp base64.cpp encrypt.cpp decrypt.cpp crypto.cpp networktransport.cpp
|
||||||
objects = parserstate.o parser.o templates.o terminal.o parseraction.o terminalfunctions.o swrite.o terminalframebuffer.o terminaldispatcher.o terminaluserinput.o terminaldisplay.o network.o ocb.o base64.o crypto.o
|
objects = parserstate.o parser.o templates.o terminal.o parseraction.o terminalfunctions.o swrite.o terminalframebuffer.o terminaldispatcher.o terminaluserinput.o terminaldisplay.o network.o ocb.o base64.o crypto.o networktransport.o
|
||||||
repos = templates.rpo
|
repos = templates.rpo
|
||||||
executables = parse termemu ntester encrypt decrypt
|
executables = parse termemu ntester encrypt decrypt
|
||||||
|
|
||||||
CXX = g++
|
CXX = g++
|
||||||
CXXFLAGS = -g --std=c++0x -pedantic -Werror -Wall -Wextra -Weffc++ -fno-implicit-templates -fno-default-inline -pipe -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=500 -D_GNU_SOURCE
|
CXXFLAGS = -g --std=c++0x -pedantic -Werror -Wall -Wextra -Weffc++ -fno-implicit-templates -fno-default-inline -pipe -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=500 -D_GNU_SOURCE
|
||||||
LIBS = -lutil -lssl
|
LIBS = -lutil -lssl -lrt
|
||||||
|
|
||||||
all: $(executables)
|
all: $(executables)
|
||||||
|
|
||||||
|
|||||||
+6
-18
@@ -4,27 +4,15 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
class KeyStroke
|
class KeyStroke
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
char letter;
|
void subtract( KeyStroke * const ) {}
|
||||||
|
string diff_from( KeyStroke const &, int ) { return ""; }
|
||||||
string tostring( void )
|
void apply_string( string ) {}
|
||||||
{
|
bool operator==( KeyStroke const & ) const { return true; }
|
||||||
return string( &letter, 1 );
|
|
||||||
};
|
|
||||||
|
|
||||||
KeyStroke( const string x )
|
|
||||||
: letter()
|
|
||||||
{
|
|
||||||
assert( x.size() == 1 );
|
|
||||||
|
|
||||||
letter = x[ 0 ];
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyStroke()
|
|
||||||
: letter( 0 )
|
|
||||||
{}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "networktransport.hpp"
|
||||||
|
|
||||||
|
using namespace Network;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
template <class MyState, class RemoteState>
|
||||||
|
uint64_t Transport<MyState, RemoteState>::timestamp( void )
|
||||||
|
{
|
||||||
|
struct timespec tp;
|
||||||
|
|
||||||
|
if ( clock_gettime( CLOCK_MONOTONIC, &tp ) < 0 ) {
|
||||||
|
throw NetworkException( "clock_gettime", errno );
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t millis = tp.tv_nsec / 1000000;
|
||||||
|
millis += uint64_t( tp.tv_sec ) * 1000000;
|
||||||
|
|
||||||
|
return millis;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class MyState, class RemoteState>
|
||||||
|
Transport<MyState, RemoteState>::Transport( MyState &initial_state )
|
||||||
|
: connection(),
|
||||||
|
server( true ),
|
||||||
|
current_state( initial_state ),
|
||||||
|
sent_states( 1, TimestampedState<MyState>( timestamp(), 0, initial_state ) ),
|
||||||
|
assumed_receiver_state( sent_states.begin() ),
|
||||||
|
timeout( INITIAL_TIMEOUT ),
|
||||||
|
highest_state_received( 0 )
|
||||||
|
{
|
||||||
|
/* server */
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class MyState, class RemoteState>
|
||||||
|
Transport<MyState, RemoteState>::Transport( MyState &initial_state,
|
||||||
|
const char *key_str, const char *ip, int port )
|
||||||
|
: connection( key_str, ip, port ),
|
||||||
|
server( false ),
|
||||||
|
current_state( initial_state ),
|
||||||
|
sent_states( 1, TimestampedState<MyState>( timestamp(), 0, initial_state ) ),
|
||||||
|
assumed_receiver_state( sent_states.begin() ),
|
||||||
|
timeout( INITIAL_TIMEOUT ),
|
||||||
|
highest_state_received( 0 )
|
||||||
|
{
|
||||||
|
/* client */
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class MyState, class RemoteState>
|
||||||
|
void Transport<MyState, RemoteState>::new_state( MyState &s )
|
||||||
|
{
|
||||||
|
current_state = s;
|
||||||
|
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class MyState, class RemoteState>
|
||||||
|
void Transport<MyState, RemoteState>::tick( void )
|
||||||
|
{
|
||||||
|
/* Update assumed receiver state */
|
||||||
|
update_assumed_receiver_state();
|
||||||
|
|
||||||
|
/* Cut out common prefix of all states */
|
||||||
|
rationalize_states();
|
||||||
|
|
||||||
|
/* Determine if a new diff or empty ack needs to be sent */
|
||||||
|
if ( timestamp() - sent_states.back().timestamp >= int64_t( SEND_INTERVAL ) ) {
|
||||||
|
/* Send diffs or ack */
|
||||||
|
send_to_receiver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class MyState, class RemoteState>
|
||||||
|
void Transport<MyState, RemoteState>::send_to_receiver( void )
|
||||||
|
{
|
||||||
|
if ( assumed_receiver_state->state == sent_states.back().state ) {
|
||||||
|
/* send empty ack */
|
||||||
|
Instruction inst( assumed_receiver_state->num,
|
||||||
|
assumed_receiver_state->num,
|
||||||
|
"",
|
||||||
|
highest_state_received );
|
||||||
|
string s = inst.tostring();
|
||||||
|
connection.send( s );
|
||||||
|
sent_states.back().timestamp = timestamp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, send sequence of diffs between assumed receiver state and current state */
|
||||||
|
|
||||||
|
/* We don't want to assume that this sequence of diffs will
|
||||||
|
necessarily bring the receiver to the _actual_ current
|
||||||
|
state. That requires perfect round-trip stability of the diff
|
||||||
|
mechanism -- stronger than we need (and probably too fragile).
|
||||||
|
Instead, we produce the full diff, unlimited by MTU, between
|
||||||
|
the assumed receiver state and current state, and apply that
|
||||||
|
diff to produce a target receiver state. Then we produce a
|
||||||
|
sequence of diffs (this time limited by MTU) that bring us to
|
||||||
|
that state. */
|
||||||
|
|
||||||
|
MyState target_receiver_state( assumed_receiver_state->state );
|
||||||
|
target_receiver_state.apply_string( current_state.diff_from( target_receiver_state, -1 ) );
|
||||||
|
|
||||||
|
while ( !(assumed_receiver_state->state == target_receiver_state) ) {
|
||||||
|
Instruction inst( assumed_receiver_state->num, -1,
|
||||||
|
current_state.diff_from( assumed_receiver_state->state,
|
||||||
|
connection.get_MTU() - HEADER_LEN ),
|
||||||
|
highest_state_received );
|
||||||
|
MyState new_state = assumed_receiver_state->state;
|
||||||
|
new_state.apply_string( inst.diff );
|
||||||
|
|
||||||
|
/* Find the right "new_num" for this instruction. */
|
||||||
|
/* Has this state previously been sent? */
|
||||||
|
/* should replace with hash table if this becomes demanding */
|
||||||
|
typename list< TimestampedState<MyState> >::iterator previously_sent = sent_states.begin();
|
||||||
|
while ( ( previously_sent != sent_states.end() )
|
||||||
|
&& ( !(previously_sent->state == new_state) ) ) {
|
||||||
|
previously_sent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( previously_sent == sent_states.end() ) { /* not previously sent */
|
||||||
|
inst.new_num = sent_states.back().num + 1;
|
||||||
|
sent_states.push_back( TimestampedState<MyState>( timestamp(), inst.new_num, new_state ) );
|
||||||
|
previously_sent = sent_states.end();
|
||||||
|
previously_sent--;
|
||||||
|
} else {
|
||||||
|
inst.new_num = previously_sent->num;
|
||||||
|
previously_sent->timestamp = timestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* send instruction */
|
||||||
|
/* XXX what about MTU problem? */
|
||||||
|
string s = inst.tostring();
|
||||||
|
|
||||||
|
try {
|
||||||
|
connection.send( s );
|
||||||
|
} catch ( MTUException m ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* successfully sent, probably */
|
||||||
|
/* ("probably" because the FIRST size-exceeded datagram doesn't get an error) */
|
||||||
|
assumed_receiver_state = previously_sent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class MyState, class RemoteState>
|
||||||
|
void Transport<MyState, RemoteState>::update_assumed_receiver_state( void )
|
||||||
|
{
|
||||||
|
uint64_t now = timestamp();
|
||||||
|
|
||||||
|
/* start from what is known and give benefit of the doubt to unacknowledged states
|
||||||
|
transmitted recently enough ago */
|
||||||
|
assumed_receiver_state = sent_states.begin();
|
||||||
|
|
||||||
|
for ( typename list< TimestampedState<MyState> >::iterator i = sent_states.begin();
|
||||||
|
i != sent_states.end();
|
||||||
|
i++ ) {
|
||||||
|
assert( now >= i->timestamp );
|
||||||
|
|
||||||
|
if ( now - i->timestamp < int64_t(timeout) ) {
|
||||||
|
assumed_receiver_state = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class MyState, class RemoteState>
|
||||||
|
void Transport<MyState, RemoteState>::rationalize_states( void )
|
||||||
|
{
|
||||||
|
MyState * const known_receiver_state = &sent_states.front().state;
|
||||||
|
|
||||||
|
for ( typename list< TimestampedState<MyState> >::iterator i = sent_states.begin();
|
||||||
|
i != sent_states.end();
|
||||||
|
i++ ) {
|
||||||
|
i->state.subtract( known_receiver_state );
|
||||||
|
}
|
||||||
|
}
|
||||||
+64
-12
@@ -1,32 +1,84 @@
|
|||||||
#ifndef NETWORK_TRANSPORT_HPP
|
#ifndef NETWORK_TRANSPORT_HPP
|
||||||
#define NETWORK_TRANSPORT_HPP
|
#define NETWORK_TRANSPORT_HPP
|
||||||
|
|
||||||
#include <google/dense_hash_map>
|
#include <string>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
using google::dense_hash_map;
|
#include "network.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
class Instruction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint64_t old_num, new_num;
|
||||||
|
string diff;
|
||||||
|
|
||||||
|
uint64_t ack_num;
|
||||||
|
|
||||||
|
Instruction( uint64_t s_old_num, uint64_t s_new_num, string s_diff, uint64_t s_ack_num )
|
||||||
|
: old_num( s_old_num ), new_num( s_new_num ), diff( s_diff ), ack_num( s_ack_num )
|
||||||
|
{}
|
||||||
|
|
||||||
|
string tostring( void ) { return ""; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class State>
|
||||||
|
class TimestampedState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint64_t num;
|
||||||
|
State state;
|
||||||
|
|
||||||
|
TimestampedState( uint64_t s_timestamp, uint64_t s_num, State &s_state )
|
||||||
|
: timestamp( s_timestamp ), num( s_num ), state( s_state )
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
template <class MyState, class RemoteState>
|
template <class MyState, class RemoteState>
|
||||||
class Transport
|
class Transport
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
static const int INITIAL_TIMEOUT = 1000; /* ms, same as TCP */
|
||||||
|
static const int SEND_INTERVAL = 20; /* ms between frames */
|
||||||
|
static const int HEADER_LEN = 40;
|
||||||
|
|
||||||
|
/* helper methods for tick() */
|
||||||
|
void update_assumed_receiver_state( void );
|
||||||
|
void rationalize_states( void );
|
||||||
|
void send_to_receiver( void );
|
||||||
|
|
||||||
Connection connection;
|
Connection connection;
|
||||||
|
bool server;
|
||||||
|
|
||||||
typedef dense_hash_map< uint64_t, MyState > StateMapper;
|
uint64_t timestamp( void );
|
||||||
|
|
||||||
dense_hash_map< uint64_t, MyState > sent;
|
/* sender */
|
||||||
dense_hash_map< uint64_t, RemoteState > received;
|
MyState current_state;
|
||||||
|
|
||||||
uint64_t known_receiver_state;
|
list< TimestampedState<MyState> > sent_states;
|
||||||
uint64_t assumed_receiver_state;
|
/* first element: known, acknowledged receiver state */
|
||||||
uint64_t last_sent_state;
|
/* last element: last sent state */
|
||||||
|
/* somewhere in the middle: the assumed state of the receiver */
|
||||||
|
|
||||||
|
typename list< TimestampedState<MyState> >::iterator assumed_receiver_state;
|
||||||
|
|
||||||
|
int timeout;
|
||||||
|
|
||||||
|
/* simple receiver */
|
||||||
|
uint64_t highest_state_received;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Transport();
|
Transport( MyState &initial_state );
|
||||||
Transport( const char *key_str, const char *ip, int port );
|
Transport( MyState &initial_state, const char *key_str, const char *ip, int port );
|
||||||
|
|
||||||
|
void new_state( MyState &s );
|
||||||
|
void tick( void );
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include "terminal.hpp"
|
#include "terminal.hpp"
|
||||||
|
|
||||||
|
#include "keystroke.hpp"
|
||||||
|
#include "networktransport.cpp"
|
||||||
|
|
||||||
namespace Parser {
|
namespace Parser {
|
||||||
class Action;
|
class Action;
|
||||||
}
|
}
|
||||||
@@ -22,3 +25,4 @@ template class vector<int>;
|
|||||||
template class map<string, Function>;
|
template class map<string, Function>;
|
||||||
template class vector<bool>;
|
template class vector<bool>;
|
||||||
|
|
||||||
|
template class Network::Transport<KeyStroke, KeyStroke>;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace Terminal {
|
|||||||
friend void Parser::OSC_Start::act_on_terminal( Emulator * );
|
friend void Parser::OSC_Start::act_on_terminal( Emulator * );
|
||||||
friend void Parser::OSC_Put::act_on_terminal( Emulator * );
|
friend void Parser::OSC_Put::act_on_terminal( Emulator * );
|
||||||
friend void Parser::OSC_End::act_on_terminal( Emulator * );
|
friend void Parser::OSC_End::act_on_terminal( Emulator * );
|
||||||
|
|
||||||
friend void Parser::UserByte::act_on_terminal( Emulator * );
|
friend void Parser::UserByte::act_on_terminal( Emulator * );
|
||||||
friend void Parser::Resize::act_on_terminal( Emulator * );
|
friend void Parser::Resize::act_on_terminal( Emulator * );
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user