v0.2
parent
565afa5529
commit
3456db14f2
41
README.md
41
README.md
|
|
@ -1,43 +1,10 @@
|
|||
|
||||
NORM PRELIMINARY CODE RELEASE
|
||||
|
||||
These directories contain source code for a very preliminary
|
||||
implementation of the Nack Oriented Reliable Multicast (NORM)
|
||||
protocol. Currently, a very dumb "norm" command-line application
|
||||
(which currently takes no command-line arguments) is
|
||||
created. This application creates a NormSession and acts
|
||||
both as a sender and receiver to the multicast group. It
|
||||
creates a "NORM_STREAM" object and writes repeated,
|
||||
continuous strings of "aaaaaaa ..." to the stream. As the
|
||||
stream is received, the client (receiver) portion of the
|
||||
"norm" app writes a notices of a successful Read()
|
||||
operation from the stream.
|
||||
|
||||
This currently uses a fixed transmission rate of 64 kbps
|
||||
and a Reed-Solomon FEC encoding block with 20 user data
|
||||
segments and calculates 8 parity segments per coding
|
||||
block. Four parity segments are sent at the end of each
|
||||
coding block as "auto parity". The current NORM code is
|
||||
currently automatically discarding segment 9 of the
|
||||
received data stream to test the FEC encoding/decoding and
|
||||
stream buffer routines.
|
||||
|
||||
The code does not currently generate any NACK messages,
|
||||
but the routines to perform checks for losses is in place
|
||||
and the routines for building NACK messages are in place,
|
||||
so the addition of the timer installation routines to
|
||||
schedule NACK back-off and subsequent transmission will be
|
||||
added very soon. Then, routines will be added for the
|
||||
server (sender) side to process received NACKs and
|
||||
subsequently provide repair messages. Then, client-side
|
||||
routines for NACK suppression will be added. After that,
|
||||
the final details of GRTT collection, unicast feedback
|
||||
suppression will be added and finally one or more
|
||||
congestion control schemes will be included.
|
||||
|
||||
The purpose of this release is to illustrate routines to
|
||||
build and parse the messages currently defined in the NORM
|
||||
Internet Draft.
|
||||
The NORM code here is still preliminary but a functional "norm"
|
||||
command-line application can be built which can send and receive
|
||||
a set of files _or_ a stream piped to/from stdin/stdout. The command-
|
||||
line options are not yet fully documented.
|
||||
|
||||
The current code only has Makefiles for Unix platforms, but the
|
||||
code could be assembled as Win32 project under VC++.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
#include "galois.h"
|
||||
|
||||
/* Galois field inversion table */
|
||||
const unsigned char GINV[256] =
|
||||
const unsigned char Norm::GINV[256] =
|
||||
{
|
||||
0x01, 0x01, 0x8e, 0xf4, 0x47, 0xa7, 0x7a, 0xba, 0xad, 0x9d, 0xdd, 0x98, 0x3d, 0xaa, 0x5d, 0x96,
|
||||
0xd8, 0x72, 0xc0, 0x58, 0xe0, 0x3e, 0x4c, 0x66, 0x90, 0xde, 0x55, 0x80, 0xa0, 0x83, 0x4b, 0x2a,
|
||||
|
|
@ -55,7 +55,7 @@ const unsigned char GINV[256] =
|
|||
}; // end GINV[]
|
||||
|
||||
// Galois field exponent table
|
||||
const unsigned char GEXP[512] =
|
||||
const unsigned char Norm::GEXP[512] =
|
||||
{
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
|
||||
0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
|
||||
|
|
@ -92,7 +92,7 @@ const unsigned char GEXP[512] =
|
|||
}; // end GEXP[]
|
||||
|
||||
// Galois field multiplication table
|
||||
const unsigned char GMULT[256][256] =
|
||||
const unsigned char Norm::GMULT[256][256] =
|
||||
{
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
|
@ -4447,3 +4447,4 @@ const unsigned char GMULT[256][256] =
|
|||
0x8e, 0x71, 0x6d, 0x92, 0x55, 0xaa, 0xb6, 0x49, 0x25, 0xda, 0xc6, 0x39, 0xfe, 0x01, 0x1d, 0xe2}
|
||||
}; // end GMULT[]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -30,15 +30,16 @@
|
|||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
********************************************************************/
|
||||
|
||||
#ifndef _GALOIS
|
||||
#define _GALOIS
|
||||
#ifndef _NORM_GALOIS
|
||||
#define _NORM_GALOIS
|
||||
|
||||
namespace Norm
|
||||
{
|
||||
extern const unsigned char GINV[256];
|
||||
extern const unsigned char GEXP[512];
|
||||
extern const unsigned char GMULT[256][256];
|
||||
|
||||
inline unsigned char gexp(unsigned int x) {return GEXP[x];}
|
||||
inline unsigned char gmult(unsigned int x, unsigned int y) {return GMULT[x][y];}
|
||||
inline unsigned char ginv(unsigned int x) {return GINV[x];}
|
||||
|
||||
#endif // _GALOIS
|
||||
}
|
||||
inline unsigned char gexp(unsigned int x) {return Norm::GEXP[x];}
|
||||
inline unsigned char gmult(unsigned int x, unsigned int y) {return Norm::GMULT[x][y];}
|
||||
inline unsigned char ginv(unsigned int x) {return Norm::GINV[x];}
|
||||
#endif // _NORM_GALOIS
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@
|
|||
|
||||
#include <stdio.h> // for stdout/stderr printouts
|
||||
#include <signal.h> // for SIGTERM/SIGINT handling
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
// Command-line application using Protolib EventDispatcher
|
||||
class NormApp : public NormController
|
||||
{
|
||||
public:
|
||||
|
|
@ -17,7 +20,12 @@ class NormApp : public NormController
|
|||
void Stop(int exitCode) {dispatcher.Stop(exitCode);}
|
||||
void OnShutdown();
|
||||
|
||||
bool ProcessCommand(const char* cmd, const char* val);
|
||||
bool ParseCommandLine(int argc, char* argv[]);
|
||||
|
||||
private:
|
||||
enum CmdType {CMD_INVALID, CMD_NOARG, CMD_ARG};
|
||||
CmdType CommandType(const char* cmd);
|
||||
|
||||
virtual void Notify(NormController::Event event,
|
||||
class NormSessionMgr* sessionMgr,
|
||||
|
|
@ -25,6 +33,12 @@ class NormApp : public NormController
|
|||
class NormServerNode* server,
|
||||
class NormObject* object);
|
||||
|
||||
void InstallTimer(ProtocolTimer* timer)
|
||||
{dispatcher.InstallTimer(timer);}
|
||||
bool OnIntervalTimeout();
|
||||
|
||||
static const char* const cmd_list[];
|
||||
|
||||
static void SignalHandler(int sigNum);
|
||||
|
||||
EventDispatcher dispatcher;
|
||||
|
|
@ -32,8 +46,395 @@ class NormApp : public NormController
|
|||
NormSession* session;
|
||||
NormStreamObject* stream;
|
||||
|
||||
// application parameters
|
||||
FILE* input; // input stream
|
||||
FILE* output; // output stream
|
||||
char input_buffer[512];
|
||||
unsigned int input_index;
|
||||
unsigned int input_length;
|
||||
|
||||
// NormSession parameters
|
||||
char* address; // session address
|
||||
UINT16 port; // session port number
|
||||
UINT8 ttl;
|
||||
double tx_rate; // bits/sec
|
||||
UINT16 segment_size;
|
||||
UINT8 ndata;
|
||||
UINT8 nparity;
|
||||
UINT8 auto_parity;
|
||||
unsigned long tx_buffer_size; // bytes
|
||||
unsigned long rx_buffer_size; // bytes
|
||||
|
||||
NormFileList tx_file_list;
|
||||
double tx_object_interval;
|
||||
int tx_repeat_count;
|
||||
double tx_repeat_interval;
|
||||
NormFileList rx_file_cache;
|
||||
char* rx_cache_path;
|
||||
|
||||
ProtocolTimer interval_timer;
|
||||
|
||||
// protocol debug parameters
|
||||
bool tracing;
|
||||
double tx_loss;
|
||||
double rx_loss;
|
||||
|
||||
}; // end class NormApp
|
||||
|
||||
NormApp::NormApp()
|
||||
: session(NULL), stream(NULL), input(NULL), output(NULL),
|
||||
input_index(0), input_length(0),
|
||||
address(NULL), port(0), ttl(3), tx_rate(64000.0),
|
||||
segment_size(1024), ndata(32), nparity(16), auto_parity(0),
|
||||
tx_buffer_size(1024*1024), rx_buffer_size(1024*1024),
|
||||
tx_object_interval(0.0), tx_repeat_count(0), tx_repeat_interval(0.0),
|
||||
rx_cache_path(NULL),
|
||||
tracing(false), tx_loss(0.0), rx_loss(0.0)
|
||||
{
|
||||
// Init tx_timer for 1.0 second interval, infinite repeats
|
||||
session_mgr.Init(EventDispatcher::TimerInstaller, &dispatcher,
|
||||
EventDispatcher::SocketInstaller, &dispatcher,
|
||||
this);
|
||||
interval_timer.Init(0.0, 0, (ProtocolTimerOwner*)this,
|
||||
(ProtocolTimeoutFunc)&NormApp::OnIntervalTimeout);
|
||||
}
|
||||
|
||||
NormApp::~NormApp()
|
||||
{
|
||||
if (address) delete address;
|
||||
if (rx_cache_path) delete rx_cache_path;
|
||||
}
|
||||
|
||||
|
||||
const char* const NormApp::cmd_list[] =
|
||||
{
|
||||
"+debug",
|
||||
"+log",
|
||||
"-trace",
|
||||
"+txloss",
|
||||
"+rxloss",
|
||||
"+address",
|
||||
"+ttl",
|
||||
"+rate",
|
||||
"+input",
|
||||
"+output",
|
||||
"+sendfile",
|
||||
"+interval",
|
||||
"+repeatcount",
|
||||
"+rinterval", // repeat interval
|
||||
"+rxcachedir",
|
||||
"+segment",
|
||||
"+block",
|
||||
"+parity",
|
||||
"+auto",
|
||||
"+txbuffer",
|
||||
"+rxbuffer",
|
||||
NULL
|
||||
};
|
||||
|
||||
bool NormApp::ProcessCommand(const char* cmd, const char* val)
|
||||
{
|
||||
CmdType type = CommandType(cmd);
|
||||
ASSERT(CMD_INVALID != type);
|
||||
unsigned int len = strlen(cmd);
|
||||
if ((CMD_ARG == type) && !val)
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(%s) missing argument\n", cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strncmp("debug", cmd, len))
|
||||
{
|
||||
|
||||
int debugLevel = atoi(val);
|
||||
if ((debugLevel < 0) || (debugLevel > 12))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(segment) invalid debug level!\n");
|
||||
return false;
|
||||
}
|
||||
SetDebugLevel(debugLevel);
|
||||
}
|
||||
else if (!strncmp("log", cmd, len))
|
||||
{
|
||||
OpenDebugLog(val);
|
||||
}
|
||||
else if (!strncmp("trace", cmd, len))
|
||||
{
|
||||
tracing = true;
|
||||
if (session) session->SetTrace(true);
|
||||
}
|
||||
else if (!strncmp("txloss", cmd, len))
|
||||
{
|
||||
double txLoss = atof(val);
|
||||
if (txLoss < 0)
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(txloss) invalid txRate!\n");
|
||||
return false;
|
||||
}
|
||||
tx_loss = txLoss;
|
||||
if (session) session->SetTxLoss(txLoss);
|
||||
}
|
||||
else if (!strncmp("rxloss", cmd, len))
|
||||
{
|
||||
double rxLoss = atof(val);
|
||||
if (rxLoss < 0)
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(rxloss) invalid txRate!\n");
|
||||
return false;
|
||||
}
|
||||
rx_loss = rxLoss;
|
||||
if (session) session->SetRxLoss(rxLoss);
|
||||
}
|
||||
else if (!strncmp("address", cmd, len))
|
||||
{
|
||||
unsigned int len = strlen(val);
|
||||
if (address) delete address;
|
||||
if (!(address = new char[len+1]))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(address) allocation error:%s\n",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
strcpy(address, val);
|
||||
char* ptr = strchr(address, '/');
|
||||
if (!ptr)
|
||||
{
|
||||
delete address;
|
||||
address = NULL;
|
||||
DMSG(0, "NormApp::ProcessCommand(address) missing port number!\n");
|
||||
return false;
|
||||
}
|
||||
*ptr++ = '\0';
|
||||
int portNum = atoi(ptr);
|
||||
if ((portNum < 1) || (portNum > 65535))
|
||||
{
|
||||
delete address;
|
||||
address = NULL;
|
||||
DMSG(0, "NormApp::ProcessCommand(address) invalid port number!\n");
|
||||
return false;
|
||||
}
|
||||
port = portNum;
|
||||
}
|
||||
else if (!strncmp("ttl", cmd, len))
|
||||
{
|
||||
int ttlTemp = atoi(val);
|
||||
if ((ttlTemp < 1) || (ttlTemp > 255))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(ttl) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
ttl = ttlTemp;
|
||||
}
|
||||
else if (!strncmp("rate", cmd, len))
|
||||
{
|
||||
double txRate = atof(val);
|
||||
if (txRate < 0)
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(rate) invalid txRate!\n");
|
||||
return false;
|
||||
}
|
||||
tx_rate = txRate;
|
||||
if (session) session->SetTxRate(txRate);
|
||||
}
|
||||
else if (!strncmp("input", cmd, len))
|
||||
{
|
||||
if (!(input = fopen(val, "rb")))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(input) fopen() error: %s\n",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("output", cmd, len))
|
||||
{
|
||||
if (!(output = fopen(val, "wb")))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(output) fopen() error: %s\n",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("sendfile", cmd, len))
|
||||
{
|
||||
if (!tx_file_list.Append(val))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(sendfile) Error appending \"%s\" "
|
||||
"to tx file list.\n", val);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("interval", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lf", &tx_object_interval))
|
||||
tx_object_interval = -1.0;
|
||||
if (tx_object_interval < 0.0)
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(interval) Invalid tx object interval: %s\n",
|
||||
val);
|
||||
tx_object_interval = 0.0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("repeat", cmd, len))
|
||||
{
|
||||
tx_repeat_count = atoi(val);
|
||||
}
|
||||
else if (!strncmp("rinterval", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lf", &tx_repeat_interval))
|
||||
tx_repeat_interval = -1.0;
|
||||
if (tx_repeat_interval < 0.0)
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(rinterval) Invalid tx repeat interval: %s\n",
|
||||
val);
|
||||
tx_repeat_interval = 0.0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("rxcachedir", cmd, len))
|
||||
{
|
||||
unsigned int length = strlen(val);
|
||||
// Make sure there is a trailing DIR_DELIMITER
|
||||
if (DIR_DELIMITER != val[length-1])
|
||||
length += 2;
|
||||
else
|
||||
length += 1;
|
||||
if (!(rx_cache_path = new char[length]))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(rxcachedir) alloc error: %s\n",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
strcpy(rx_cache_path, val);
|
||||
rx_cache_path[length-2] = DIR_DELIMITER;
|
||||
rx_cache_path[length-1] = '\0';
|
||||
}
|
||||
else if (!strncmp("segment", cmd, len))
|
||||
{
|
||||
int segmentSize = atoi(val);
|
||||
if ((segmentSize < 0) || (segmentSize > 8000))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(segment) invalid segment size!\n");
|
||||
return false;
|
||||
}
|
||||
segment_size = segmentSize;
|
||||
}
|
||||
else if (!strncmp("block", cmd, len))
|
||||
{
|
||||
int blockSize = atoi(val);
|
||||
if ((blockSize < 1) || (blockSize > 255))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(block) invalid block size!\n");
|
||||
return false;
|
||||
}
|
||||
ndata = blockSize;
|
||||
}
|
||||
else if (!strncmp("parity", cmd, len))
|
||||
{
|
||||
int numParity = atoi(val);
|
||||
if ((numParity < 0) || (numParity > 254))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(parity) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
nparity = numParity;
|
||||
}
|
||||
else if (!strncmp("auto", cmd, len))
|
||||
{
|
||||
int autoParity = atoi(val);
|
||||
if ((autoParity < 0) || (autoParity > 254))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(auto) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
auto_parity = autoParity;
|
||||
if (session) session->ServerSetAutoParity(autoParity);
|
||||
}
|
||||
else if (!strncmp("txbuffer", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lu", &tx_buffer_size))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(txbuffer) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("rxbuffer", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lu", &rx_buffer_size))
|
||||
{
|
||||
DMSG(0, "NormApp::ProcessCommand(rxbuffer) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} // end NormApp::ProcessCommand()
|
||||
|
||||
|
||||
NormApp::CmdType NormApp::CommandType(const char* cmd)
|
||||
{
|
||||
if (!cmd) return CMD_INVALID;
|
||||
unsigned int len = strlen(cmd);
|
||||
bool matched = false;
|
||||
CmdType type = CMD_INVALID;
|
||||
const char* const* nextCmd = cmd_list;
|
||||
while (*nextCmd)
|
||||
{
|
||||
if (!strncmp(cmd, *nextCmd+1, len))
|
||||
{
|
||||
if (matched)
|
||||
{
|
||||
// ambiguous command (command should match only once)
|
||||
return CMD_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
matched = true;
|
||||
if ('+' == *nextCmd[0])
|
||||
type = CMD_ARG;
|
||||
else
|
||||
type = CMD_NOARG;
|
||||
}
|
||||
}
|
||||
nextCmd++;
|
||||
}
|
||||
return type;
|
||||
} // end NormApp::CommandType()
|
||||
|
||||
|
||||
bool NormApp::ParseCommandLine(int argc, char* argv[])
|
||||
{
|
||||
int i = 1;
|
||||
while ( i < argc)
|
||||
{
|
||||
CmdType cmdType = CommandType(argv[i]);
|
||||
switch (cmdType)
|
||||
{
|
||||
case CMD_INVALID:
|
||||
DMSG(0, "NormApp::ParseCommandLine() Invalid command:%s\n",
|
||||
argv[i]);
|
||||
return false;
|
||||
case CMD_NOARG:
|
||||
if (!ProcessCommand(argv[i], NULL))
|
||||
{
|
||||
DMSG(0, "NormApp::ParseCommandLine() ProcessCommand(%s) error\n",
|
||||
argv[i]);
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
case CMD_ARG:
|
||||
if (!ProcessCommand(argv[i], argv[i+1]))
|
||||
{
|
||||
DMSG(0, "NormApp::ParseCommandLine() ProcessCommand(%s, %s) error\n",
|
||||
argv[i], argv[i+1]);
|
||||
return false;
|
||||
}
|
||||
i += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} // end NormApp::ParseCommandLine()
|
||||
|
||||
void NormApp::Notify(NormController::Event event,
|
||||
class NormSessionMgr* sessionMgr,
|
||||
|
|
@ -44,85 +445,286 @@ void NormApp::Notify(NormController::Event event,
|
|||
switch (event)
|
||||
{
|
||||
case TX_QUEUE_EMPTY:
|
||||
//TRACE("NormApp::Notify(TX_QUEUE_EMPTY) ...\n");
|
||||
if (object == stream)
|
||||
{
|
||||
char text[256];
|
||||
memset(text, 'a', 256);
|
||||
stream->Write(text, 256);
|
||||
}
|
||||
break;
|
||||
|
||||
case RX_OBJECT_NEW:
|
||||
//TRACE("NormApp::Notify(RX_OBJECT_NEW) ...\n");
|
||||
// Write to stream as needed
|
||||
//DMSG(0, "NormApp::Notify(TX_QUEUE_EMPTY) ...\n");
|
||||
if (object && (object == stream))
|
||||
{
|
||||
switch (object->GetType())
|
||||
bool flush = false;
|
||||
if (input)
|
||||
{
|
||||
case NormObject::STREAM:
|
||||
if (input_index >= input_length)
|
||||
{
|
||||
const NormObjectSize& size = object->Size();
|
||||
if (!((NormStreamObject*)object)->Accept(size.LSB()))
|
||||
size_t result = fread(input_buffer, sizeof(char), 512, input);
|
||||
if (result)
|
||||
{
|
||||
TRACE("stream object accept error!\n");
|
||||
input_index = 0;
|
||||
input_length = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
input_length = input_index = 0;
|
||||
if (feof(input))
|
||||
{
|
||||
DMSG(0, "norm: input end-of-file.\n");
|
||||
fclose(input);
|
||||
input = NULL;
|
||||
flush = true;
|
||||
}
|
||||
else if (ferror(input))
|
||||
{
|
||||
DMSG(0, "norm: input error:%s\n", strerror(errno));
|
||||
clearerr(input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NormObject::FILE:
|
||||
case NormObject::DATA:
|
||||
TRACE("NormApp::Notify() FILE/DATA objects not supported...\n");
|
||||
break;
|
||||
input_index += stream->Write(input_buffer+input_index,
|
||||
input_length-input_index,
|
||||
flush);
|
||||
} // end if (input)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Can queue a new object for transmission
|
||||
if (interval_timer.Interval() > 0.0)
|
||||
{
|
||||
InstallTimer(&interval_timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnIntervalTimeout();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RX_OBJECT_NEW:
|
||||
{
|
||||
//TRACE("NormApp::Notify(RX_OBJECT_NEW) ...\n");
|
||||
// It's up to the app to "accept" the object
|
||||
switch (object->GetType())
|
||||
{
|
||||
case NormObject::STREAM:
|
||||
{
|
||||
const NormObjectSize& size = object->Size();
|
||||
if (!((NormStreamObject*)object)->Accept(size.LSB()))
|
||||
{
|
||||
DMSG(0, "NormApp::Notify(RX_OBJECT_NEW) stream object accept error!\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NormObject::FILE:
|
||||
{
|
||||
if (rx_cache_path)
|
||||
{
|
||||
// (TBD) re-arrange so if we've already recv'd INFO,
|
||||
// we can use that for file name right away ???
|
||||
// (TBD) Manage recv file name collisions, etc ...
|
||||
char fileName[PATH_MAX];
|
||||
strcpy(fileName, rx_cache_path);
|
||||
strcat(fileName, "normTempXXXXXX");
|
||||
#ifdef WIN32
|
||||
if (!_mktemp(fileName))
|
||||
#else
|
||||
int fd = mkstemp(fileName);
|
||||
if (fd >= 0)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
else
|
||||
#endif // if/else WIN32
|
||||
{
|
||||
DMSG(0, "NormApp::Notify(RX_OBJECT_NEW) Warning: mkstemp() error: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
if (!((NormFileObject*)object)->Accept(fileName))
|
||||
{
|
||||
DMSG(0, "NormApp::Notify(RX_OBJECT_NEW) file object accept error!\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormApp::Notify(RX_OBJECT_NEW) no rx cache for file\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NormObject::DATA:
|
||||
DMSG(0, "NormApp::Notify() FILE/DATA objects not _yet_ supported...\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RX_OBJECT_INFO:
|
||||
//TRACE("NormApp::Notify(RX_OBJECT_INFO) ...\n");
|
||||
switch(object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
{
|
||||
// Rename rx file using newly received info
|
||||
char fileName[PATH_MAX];
|
||||
strncpy(fileName, rx_cache_path, PATH_MAX);
|
||||
UINT16 pathLen = strlen(rx_cache_path);
|
||||
pathLen = MIN(pathLen, PATH_MAX);
|
||||
UINT16 len = object->InfoLength();
|
||||
len = MIN(len, (PATH_MAX - pathLen));
|
||||
strncat(fileName, object->GetInfo(), len);
|
||||
// Convert '/' in file info to directory delimiters
|
||||
for (UINT16 i = pathLen; i < (pathLen+len); i++)
|
||||
{
|
||||
if ('/' == fileName[i])
|
||||
fileName[i] = DIR_DELIMITER;
|
||||
}
|
||||
pathLen += len;
|
||||
if (pathLen < PATH_MAX) fileName[pathLen] = '\0';
|
||||
|
||||
// Deal with concurrent rx name collisions
|
||||
// (TBD) and implement overwrite policy
|
||||
// and cache files in cache mode
|
||||
|
||||
if (!((NormFileObject*)object)->Rename(fileName))
|
||||
{
|
||||
DMSG(0, "NormApp::Notify() Error renaming rx file: %s\n",
|
||||
fileName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NormObject::DATA:
|
||||
case NormObject::STREAM:
|
||||
break;
|
||||
} // end switch(object->GetType())
|
||||
break;
|
||||
|
||||
case RX_OBJECT_UPDATE:
|
||||
//TRACE("NormApp::Notify(RX_OBJECT_UPDATE) ...\n");
|
||||
switch (object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
// (TBD) update progress
|
||||
break;
|
||||
|
||||
case NormObject::STREAM:
|
||||
{
|
||||
// Read the stream
|
||||
char buffer[2048];
|
||||
// Read the stream when it's updated
|
||||
ASSERT(output);
|
||||
char buffer[256];
|
||||
unsigned int nBytes;
|
||||
while ((nBytes = ((NormStreamObject*)object)->Read(buffer, 2048)))
|
||||
while ((nBytes = ((NormStreamObject*)object)->Read(buffer, 256)))
|
||||
{
|
||||
for (unsigned int i =0; i < nBytes; i++)
|
||||
unsigned int put = 0;
|
||||
while (put < nBytes)
|
||||
{
|
||||
if ('a' != buffer[i])
|
||||
size_t result = fwrite(buffer, sizeof(char), nBytes, output);
|
||||
fflush(output);
|
||||
if (result)
|
||||
{
|
||||
TRACE("NormApp::Notify() bad data received!\n");
|
||||
break;
|
||||
put += result;
|
||||
}
|
||||
}
|
||||
buffer[32] = '\0';
|
||||
DMSG(0, "NormApp::Notify() stream read %u bytes: \"%s\"\n", nBytes, buffer);
|
||||
else
|
||||
{
|
||||
if (ferror(output))
|
||||
{
|
||||
if (EINTR == errno)
|
||||
{
|
||||
clearerr(output);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "norm: output error:%s\n", strerror(errno));
|
||||
clearerr(output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end while(put < nBytes)
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case NormObject::FILE:
|
||||
case NormObject::DATA:
|
||||
TRACE("NormApp::Notify() FILE/DATA objects not supported...\n");
|
||||
DMSG(0, "NormApp::Notify() FILE/DATA objects not _yet_ supported...\n");
|
||||
break;
|
||||
} // end switch (object->GetType())
|
||||
break;
|
||||
case RX_OBJECT_COMPLETE:
|
||||
{
|
||||
switch(object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
DMSG(0, "norm: Completed rx file: %s\n", ((NormFileObject*)object)->Path());
|
||||
break;
|
||||
|
||||
case NormObject::STREAM:
|
||||
ASSERT(0);
|
||||
break;
|
||||
case NormObject::DATA:
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} // end switch(event)
|
||||
} // end NormApp::Notify()
|
||||
|
||||
|
||||
bool NormApp::OnIntervalTimeout()
|
||||
{
|
||||
char fileName[PATH_MAX];
|
||||
if (tx_file_list.GetNextFile(fileName))
|
||||
{
|
||||
char pathName[PATH_MAX];
|
||||
tx_file_list.GetCurrentBasePath(pathName);
|
||||
unsigned int len = strlen(pathName);
|
||||
len = MIN(len, PATH_MAX);
|
||||
unsigned int maxLen = PATH_MAX - len;
|
||||
char* ptr = fileName + len;
|
||||
len = strlen(ptr);
|
||||
len = MIN(len, maxLen);
|
||||
// (TBD) Make sure len <= segment_size)
|
||||
char fileNameInfo[PATH_MAX];
|
||||
strncpy(fileNameInfo, ptr, len);
|
||||
// Normalize directory delimiters in file name info
|
||||
for (unsigned int i = 0; i < len; i++)
|
||||
{
|
||||
if (DIR_DELIMITER == fileNameInfo[i])
|
||||
fileNameInfo[i] = '/';
|
||||
}
|
||||
char temp[PATH_MAX];
|
||||
strncpy(temp, fileNameInfo, len);
|
||||
temp[len] = '\0';
|
||||
if (!session->QueueTxFile(fileName, fileNameInfo, len))
|
||||
{
|
||||
DMSG(0, "NormApp::OnIntervalTimeout() Error opening tx file: %s\n",
|
||||
fileName);
|
||||
// Try the next file in the list
|
||||
return OnIntervalTimeout();
|
||||
}
|
||||
DMSG(0, "norm: File \"%s\" queued for transmission.\n", fileName);
|
||||
interval_timer.SetInterval(tx_object_interval);
|
||||
}
|
||||
}
|
||||
|
||||
NormApp::NormApp()
|
||||
: session(NULL), stream(NULL)
|
||||
{
|
||||
// Init tx_timer for 1.0 second interval, infinite repeats
|
||||
session_mgr.Init(EventDispatcher::TimerInstaller, &dispatcher,
|
||||
EventDispatcher::SocketInstaller, &dispatcher,
|
||||
this);
|
||||
}
|
||||
|
||||
NormApp::~NormApp()
|
||||
{
|
||||
|
||||
}
|
||||
else if (tx_repeat_count)
|
||||
{
|
||||
// (TBD) When repeating, remove previous instance from tx queue???
|
||||
if (tx_repeat_count > 0) tx_repeat_count--;
|
||||
tx_file_list.ResetIterator();
|
||||
if (tx_repeat_interval > tx_object_interval)
|
||||
{
|
||||
if (interval_timer.IsActive()) interval_timer.Deactivate();
|
||||
interval_timer.SetInterval(tx_repeat_interval = tx_object_interval);
|
||||
InstallTimer(&interval_timer);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return OnIntervalTimeout();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "norm: End of tx file list reached.\n");
|
||||
}
|
||||
return true;
|
||||
} // end NormApp::OnIntervalTimeout()
|
||||
|
||||
bool NormApp::OnStartup()
|
||||
{
|
||||
|
|
@ -137,34 +739,69 @@ bool NormApp::OnStartup()
|
|||
signal(SIGTERM, SignalHandler);
|
||||
signal(SIGINT, SignalHandler);
|
||||
|
||||
// Validate our application settings
|
||||
if (!address)
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() Error! no session address given.");
|
||||
return false;
|
||||
}
|
||||
if (!input && !output && tx_file_list.IsEmpty() && !rx_cache_path)
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() Error! no \"input\", \"output\", "
|
||||
"\"sendfile\", or \"rxcache\" given.");
|
||||
return false;
|
||||
}
|
||||
|
||||
session = session_mgr.NewSession("224.225.1.5", 5005);
|
||||
// Create a new session on multicast group/port
|
||||
session = session_mgr.NewSession(address, port);
|
||||
if (session)
|
||||
{
|
||||
if (!session->StartServer(1024*1024, 256, 20, 8))
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() start server error!\n");
|
||||
session_mgr.Destroy();
|
||||
return false;
|
||||
}
|
||||
session->ServerSetAutoParity(4);
|
||||
//session->SetLoopback(true);
|
||||
// Common session parameters
|
||||
session->SetTxRate(tx_rate);
|
||||
session->SetTrace(tracing);
|
||||
session->SetTxLoss(tx_loss);
|
||||
session->SetRxLoss(rx_loss);
|
||||
|
||||
// Open a stream object to write to
|
||||
stream = session->QueueTxStream(10*1024);
|
||||
if (!stream)
|
||||
if (input || !tx_file_list.IsEmpty())
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() queue tx stream error!\n");
|
||||
session_mgr.Destroy();
|
||||
return false;
|
||||
// StartServer(bufferMax, segmentSize, fec_ndata, fec_nparity)
|
||||
if (!session->StartServer(tx_buffer_size, segment_size, ndata, nparity))
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() start server error!\n");
|
||||
session_mgr.Destroy();
|
||||
return false;
|
||||
}
|
||||
session->ServerSetAutoParity(auto_parity);
|
||||
|
||||
if (input)
|
||||
{
|
||||
// Open a stream object to write to (QueueTxStream(stream bufferSize))
|
||||
stream = session->QueueTxStream(tx_buffer_size);
|
||||
if (!stream)
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() queue tx stream error!\n");
|
||||
session_mgr.Destroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (output || rx_cache_path)
|
||||
{
|
||||
TRACE("starting client ...\n");
|
||||
// StartClient(bufferMax (per-sender))
|
||||
if (!session->StartClient(rx_buffer_size))
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() start client error!\n");
|
||||
session_mgr.Destroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
char text[256];
|
||||
memset(text, 'a', 256);
|
||||
stream->Write(text, 256);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormApp::OnStartup() new session error!\n");
|
||||
return false;
|
||||
}
|
||||
} // end NormApp::OnStartup()
|
||||
|
|
@ -172,6 +809,10 @@ bool NormApp::OnStartup()
|
|||
void NormApp::OnShutdown()
|
||||
{
|
||||
session_mgr.Destroy();
|
||||
if (input) fclose(input);
|
||||
input = NULL;
|
||||
if (output) fclose(output);
|
||||
output = NULL;
|
||||
} // end NormApp::OnShutdown()
|
||||
|
||||
|
||||
|
|
@ -180,14 +821,18 @@ NormApp theApp;
|
|||
|
||||
// Use "main()" for UNIX and WIN32 console apps,
|
||||
// "WinMain()" for non-console WIN32
|
||||
// (VC++ uses the "_CONSOLE_ macro to indicate build type)
|
||||
// (VC++ uses the "_CONSOLE_" macro to indicate build type)
|
||||
|
||||
#if defined(WIN32) && !defined(_CONSOLE)
|
||||
#if defined(WIN32) && !defined(_CONSOLE_)
|
||||
int PASCAL WinMain(HINSTANCE instance, HINSTANCE prevInst, LPSTR cmdline, int cmdshow)
|
||||
{
|
||||
// (TBD) transform WinMain "cmdLine" to (argc, argv[]) values
|
||||
int argc = 0;
|
||||
char* argv[] = NULL;
|
||||
#else
|
||||
int main(int argc, char* argv[])
|
||||
#endif
|
||||
{
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
// Hack to determine if Win32 console application was launched
|
||||
// independently or from a pre-existing console window
|
||||
|
|
@ -207,16 +852,29 @@ int main(int argc, char* argv[])
|
|||
OpenDebugWindow();
|
||||
pauseForUser = true;
|
||||
}
|
||||
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
int exitCode = 0;
|
||||
if (theApp.OnStartup())
|
||||
if (theApp.ParseCommandLine(argc, argv))
|
||||
{
|
||||
exitCode = theApp.MainLoop();
|
||||
theApp.OnShutdown();
|
||||
fprintf(stderr, "norm: Done.\n");
|
||||
if (theApp.OnStartup())
|
||||
{
|
||||
exitCode = theApp.MainLoop();
|
||||
theApp.OnShutdown();
|
||||
fprintf(stderr, "norm: Done.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "norm: Error initializing application!\n");
|
||||
exitCode = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "norm: Error parsing command line!\n");
|
||||
exitCode = -1;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
// If Win32 console is going to disappear, pause for user before exiting
|
||||
if (pauseForUser)
|
||||
|
|
@ -225,12 +883,6 @@ int main(int argc, char* argv[])
|
|||
getchar();
|
||||
}
|
||||
#endif // WIN32
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "norm: Error initializing application!\n");
|
||||
return -1;
|
||||
}
|
||||
return exitCode; // exitCode contains "signum" causing exit
|
||||
} // end main();
|
||||
|
||||
|
|
@ -240,7 +892,7 @@ void NormApp::SignalHandler(int sigNum)
|
|||
{
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
theApp.Stop(sigNum); // causes theApp's main loop to exit
|
||||
theApp.Stop(sigNum); // causes theApp's main loop to exit
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
#include "normBitmask.h"
|
||||
#include "debug.h"
|
||||
#include "sysdefs.h"
|
||||
|
||||
// Hamming weights for given one-byte bit masks
|
||||
static unsigned char WEIGHT[256] =
|
||||
|
|
@ -377,7 +379,9 @@ bool NormBitmask::XCopy(const NormBitmask& b)
|
|||
{
|
||||
if (b.num_bits > num_bits) return false;
|
||||
unsigned int len = b.mask_len;
|
||||
for (unsigned int i = 0; i < len; i++)
|
||||
unsigned int begin = b.first_set >> 3;
|
||||
if (begin) memset(mask, 0, begin);
|
||||
for (unsigned int i = begin; i < len; i++)
|
||||
mask[i] = b.mask[i] & ~mask[i];
|
||||
if (len < mask_len) memset(&mask[len], 0, mask_len - len);
|
||||
if (b.first_set < first_set)
|
||||
|
|
@ -601,6 +605,7 @@ bool NormSlidingMask::Unset(unsigned long index)
|
|||
mask[(pos >> 3)] &= ~(0x80 >> (pos & 0x07));
|
||||
if (start == end)
|
||||
{
|
||||
ASSERT(pos == start);
|
||||
start = end = num_bits;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1133,7 +1138,7 @@ bool NormSlidingMask::Add(const NormSlidingMask& b)
|
|||
} // end NormSlidingMask::Add()
|
||||
|
||||
// A sort of logical subtraction "this = (this & ~b)"
|
||||
// This leaves us with bits uniquely set in this instance
|
||||
// This leaves us with bits uniquely set in "this"
|
||||
bool NormSlidingMask::Subtract(const NormSlidingMask& b)
|
||||
{
|
||||
if (b.IsSet())
|
||||
|
|
@ -1150,11 +1155,15 @@ bool NormSlidingMask::Subtract(const NormSlidingMask& b)
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Copy(b);
|
||||
}
|
||||
return true;
|
||||
} // end NormSlidingMask::Subtract()
|
||||
|
||||
// A sort of logical subtraction "this = (~this & b)"
|
||||
// This leaves us with bits uniquely set in this instance
|
||||
// This leaves us with bits uniquely set in "b"
|
||||
bool NormSlidingMask::XCopy(const NormSlidingMask& b)
|
||||
{
|
||||
if (b.IsSet())
|
||||
|
|
@ -1175,6 +1184,10 @@ bool NormSlidingMask::XCopy(const NormSlidingMask& b)
|
|||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Copy(b);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} // end NormSlidingMask::XCopy()
|
||||
|
|
@ -1232,3 +1245,18 @@ void NormSlidingMask::Display(FILE* stream)
|
|||
if (0x3f == (i & 0x3f)) fprintf(stream, "\n");
|
||||
}
|
||||
} // end NormSlidingMask::Display()
|
||||
|
||||
void NormSlidingMask::Debug(long theCount)
|
||||
{
|
||||
unsigned long index = offset;
|
||||
theCount = MIN(theCount, num_bits);
|
||||
DMSG(0, "NormSlidingMask::Debug() offset:%lu\n ", index);
|
||||
long i;
|
||||
for (i = 0; i < theCount; i++)
|
||||
{
|
||||
if (Test(index++)) DMSG(0, "1"); else DMSG(0, "0");
|
||||
if (0x07 == (i & 0x07)) DMSG(0, " ");
|
||||
if (0x3f == (i & 0x3f)) DMSG(0, "\n ");
|
||||
}
|
||||
if (0x3f != (i & 0x3f)) DMSG(0, "\n");
|
||||
} // end NormSlidingMask::Display()
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ class NormSlidingMask
|
|||
}
|
||||
void Reset(unsigned long index = 0)
|
||||
{
|
||||
ASSERT(num_bits);
|
||||
memset(mask, 0xff, mask_len);
|
||||
mask[mask_len-1] = 0x00ff << ((8 - (num_bits & 0x07)) & 0x07);
|
||||
start = 0;
|
||||
|
|
@ -171,6 +172,7 @@ class NormSlidingMask
|
|||
bool Xor(const NormSlidingMask & b); // this = this ^ b
|
||||
|
||||
void Display(FILE* stream);
|
||||
void Debug(long theCount);
|
||||
|
||||
private:
|
||||
unsigned char* mask;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,986 @@
|
|||
|
||||
#include "normFile.h"
|
||||
|
||||
#include <string.h> // for strerror()
|
||||
#include <errno.h> // for errno
|
||||
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#include <share.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
// Most don't have the dirfd() function
|
||||
#ifndef HAVE_DIRFD
|
||||
static inline int dirfd(DIR *dir) {return (dir->dd_fd);}
|
||||
#endif // HAVE_DIRFD
|
||||
#endif // if/else WIN32
|
||||
|
||||
NormFile::NormFile()
|
||||
: fd(-1)
|
||||
{
|
||||
}
|
||||
|
||||
NormFile::~NormFile()
|
||||
{
|
||||
if (IsOpen()) Close();
|
||||
} // end NormFile::~NormFile()
|
||||
|
||||
// This should be called with a full path only!
|
||||
bool NormFile::Open(const char* thePath, int theFlags)
|
||||
{
|
||||
ASSERT(!IsOpen());
|
||||
if (theFlags & O_CREAT)
|
||||
{
|
||||
// Create sub-directories as needed.
|
||||
char tempPath[PATH_MAX];
|
||||
strncpy(tempPath, thePath, PATH_MAX);
|
||||
char* ptr = strrchr(tempPath, DIR_DELIMITER);
|
||||
if (ptr) *ptr = '\0';
|
||||
ptr = NULL;
|
||||
while (!NormFile::Exists(tempPath))
|
||||
{
|
||||
char* ptr2 = ptr;
|
||||
ptr = strrchr(tempPath, DIR_DELIMITER);
|
||||
if (ptr2) *ptr2 = DIR_DELIMITER;
|
||||
if (ptr)
|
||||
{
|
||||
*ptr = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = tempPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ptr && ('\0' == *ptr)) *ptr++ = DIR_DELIMITER;
|
||||
while (ptr)
|
||||
{
|
||||
ptr = strchr(ptr, DIR_DELIMITER);
|
||||
if (ptr) *ptr = '\0';
|
||||
if (mkdir(tempPath, 0755))
|
||||
{
|
||||
DMSG(0, "NormFile::Rename() mkdir(%s) error: %s\n",
|
||||
tempPath, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (ptr) *ptr++ = DIR_DELIMITER;
|
||||
}
|
||||
}
|
||||
#ifdef WIN32
|
||||
// Make sure we're in binary mode (important for WIN32)
|
||||
theFlags |= _O_BINARY;
|
||||
// Allow sharing of read-only files but not of files being written
|
||||
if (theFlags & _O_RDONLY)
|
||||
fd = _sopen(thePath, theFlags, _SH_DENYNO);
|
||||
else
|
||||
fd = _open(thePath, theFlags, 0640);
|
||||
if(fd >= 0)
|
||||
{
|
||||
offset = 0;
|
||||
flags = theFlags;
|
||||
return true; // no error
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "Error opening file \"%s\": %s\n", path,
|
||||
strerror(errno));
|
||||
flags = 0;
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if((fd = open(thePath, theFlags, 0640)) >= 0)
|
||||
{
|
||||
offset = 0;
|
||||
return true; // no error
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "norm: Error opening file \"%s\": %s\n",
|
||||
thePath, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#endif // if/else WIN32
|
||||
} // end NormFile::Open()
|
||||
|
||||
void NormFile::Close()
|
||||
{
|
||||
if (IsOpen())
|
||||
{
|
||||
#ifdef WIN32
|
||||
_close(fd);
|
||||
#else
|
||||
close(fd);
|
||||
#endif // if/else WIN32
|
||||
fd = -1;
|
||||
}
|
||||
} // end NormFile::Close()
|
||||
|
||||
|
||||
// Routines to try to get an exclusive lock on a file
|
||||
bool NormFile::Lock()
|
||||
{
|
||||
#ifndef WIN32 // WIN32 files are automatically locked
|
||||
fchmod(fd, 0640 | S_ISGID);
|
||||
#ifdef HAVE_FLOCK
|
||||
if (flock(fd, LOCK_EX | LOCK_NB))
|
||||
#else
|
||||
//#ifdef HAVE_LOCKF
|
||||
if (lockf(fd, F_LOCK, 0)) // assume lockf if not flock
|
||||
//#endif // HAVE_LOCKF
|
||||
#endif // if/else HAVE_FLOCK
|
||||
return false;
|
||||
else
|
||||
#endif // !WIN32
|
||||
return true;
|
||||
} // end NormFile::Lock()
|
||||
|
||||
void NormFile::Unlock()
|
||||
{
|
||||
#ifndef WIN32
|
||||
#ifdef HAVE_FLOCK
|
||||
flock(fd, LOCK_UN);
|
||||
#else
|
||||
#ifdef HAVE_LOCKF
|
||||
lockf(fd, F_ULOCK, 0);
|
||||
#endif // HAVE_LOCKF
|
||||
#endif // HAVE_FLOCK
|
||||
fchmod(fd, 0640);
|
||||
#endif // !WIN32
|
||||
} // end NormFile::UnLock()
|
||||
|
||||
bool NormFile::Rename(const char* oldName, const char* newName)
|
||||
{
|
||||
if (!strcmp(oldName, newName)) return true; // no change required
|
||||
// Make sure the new file name isn't an existing "busy" file
|
||||
// (This also builds sub-directories as needed)
|
||||
if (NormFile::IsLocked(newName)) return false;
|
||||
#ifdef WIN32
|
||||
// In Win32, the new file can't already exist
|
||||
if (NormFile::Exists(newName) _unlink(newName);
|
||||
// In Win32, the old file can't be open
|
||||
int oldFlags = 0;
|
||||
if (IsOpen())
|
||||
{
|
||||
oldFlags = flags;
|
||||
oldFlags &= ~(O_CREAT | O_TRUNC); // unset these
|
||||
Close();
|
||||
}
|
||||
#else
|
||||
// Create sub-directories as needed.
|
||||
char tempPath[PATH_MAX];
|
||||
strncpy(tempPath, newName, PATH_MAX);
|
||||
char* ptr = strrchr(tempPath, DIR_DELIMITER);
|
||||
if (ptr) *ptr = '\0';
|
||||
ptr = NULL;
|
||||
while (!NormFile::Exists(tempPath))
|
||||
{
|
||||
char* ptr2 = ptr;
|
||||
ptr = strrchr(tempPath, DIR_DELIMITER);
|
||||
if (ptr2) *ptr2 = DIR_DELIMITER;
|
||||
if (ptr)
|
||||
{
|
||||
*ptr = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = tempPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ptr && ('\0' == *ptr)) *ptr++ = DIR_DELIMITER;
|
||||
while (ptr)
|
||||
{
|
||||
ptr = strchr(ptr, DIR_DELIMITER);
|
||||
if (ptr) *ptr = '\0';
|
||||
if (mkdir(tempPath, 0755))
|
||||
{
|
||||
DMSG(0, "NormFile::Rename() mkdir(%s) error: %s\n",
|
||||
tempPath, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (ptr) *ptr++ = DIR_DELIMITER;
|
||||
}
|
||||
#endif // if/else WIN32
|
||||
if (rename(oldName, newName))
|
||||
{
|
||||
DMSG(0, "NormFile::Rename() rename() error: %s\n", strerror(errno));
|
||||
#ifdef WIN32
|
||||
//if (oldFlags) Open(oldName, oldFlags);
|
||||
#endif // WIN32
|
||||
return false;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef WIN32
|
||||
// (TBD) Is the file offset OK doing this???
|
||||
//if (oldFlags) Open(newName, oldFlags);
|
||||
#endif // WIN32
|
||||
return true;
|
||||
}
|
||||
} // end NormFile::Rename()
|
||||
|
||||
int NormFile::Read(char* buffer, int len)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
#ifdef WIN32
|
||||
int result = _read(fd, buffer, len);
|
||||
#else
|
||||
int result = read(fd, buffer, len);
|
||||
#endif // if/else WIN32
|
||||
if (result > 0) offset += result;
|
||||
return result;
|
||||
} // end NormFile::Read()
|
||||
|
||||
int NormFile::Write(const char* buffer, int len)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
#ifdef WIN32
|
||||
int result = _write(fd, buffer, len);
|
||||
#else
|
||||
int result = write(fd, buffer, len);
|
||||
#endif // if/else WIN32
|
||||
if (result > 0) offset += result;
|
||||
return result;
|
||||
} // end NormFile::Write()
|
||||
|
||||
bool NormFile::Seek(off_t theOffset)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
#ifdef WIN32
|
||||
off_t result = _lseek(fd, theOffset, SEEK_SET);
|
||||
#else
|
||||
off_t result = lseek(fd, theOffset, SEEK_SET);
|
||||
#endif // if/else WIN32
|
||||
if (result == (off_t)-1)
|
||||
{
|
||||
DMSG(0, "NormFile::Seek() lseek() error: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = result;
|
||||
return true; // no error
|
||||
}
|
||||
} // end NormFile::Seek()
|
||||
|
||||
off_t NormFile::GetSize() const
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
#ifdef WIN32
|
||||
struct _stat info;
|
||||
int result = _fstat(fd, &info);
|
||||
#else
|
||||
struct stat info;
|
||||
int result = fstat(fd, &info);
|
||||
#endif // if/else WIN32
|
||||
if (result)
|
||||
{
|
||||
DMSG(0, "Error getting file size: %s\n", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return info.st_size;
|
||||
}
|
||||
} // end NormFile::GetSize()
|
||||
|
||||
|
||||
/***********************************************
|
||||
* The NormDirectoryIterator classes is used to
|
||||
* walk directory trees for file transmission
|
||||
*/
|
||||
|
||||
NormDirectoryIterator::NormDirectoryIterator()
|
||||
: current(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NormDirectoryIterator::~NormDirectoryIterator()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool NormDirectoryIterator::Open(const char *thePath)
|
||||
{
|
||||
if (current) Close();
|
||||
#ifdef WIN32
|
||||
if (thePath && _access(thePath, 0))
|
||||
#else
|
||||
if (thePath && access(thePath, X_OK))
|
||||
#endif // if/else WIN32
|
||||
{
|
||||
DMSG(0, "NormDirectoryIterator: can't access directory: %s\n", thePath);
|
||||
return false;
|
||||
}
|
||||
current = new NormDirectory(thePath);
|
||||
if (current && current->Open())
|
||||
{
|
||||
path_len = strlen(current->Path());
|
||||
path_len = MIN(PATH_MAX, path_len);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormDirectoryIterator: can't open directory: %s\n", thePath);
|
||||
if (current) delete current;
|
||||
current = NULL;
|
||||
return false;
|
||||
}
|
||||
} // end NormDirectoryIterator::Init()
|
||||
|
||||
void NormDirectoryIterator::Close()
|
||||
{
|
||||
NormDirectory* d;
|
||||
while ((d = current))
|
||||
{
|
||||
current = d->parent;
|
||||
d->Close();
|
||||
delete d;
|
||||
}
|
||||
} // end NormDirectoryIterator::Close()
|
||||
|
||||
bool NormDirectoryIterator::GetPath(char* pathBuffer)
|
||||
{
|
||||
if (current)
|
||||
{
|
||||
NormDirectory* d = current;
|
||||
while (d->parent) d = d->parent;
|
||||
strncpy(pathBuffer, d->Path(), PATH_MAX);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathBuffer[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
bool NormDirectoryIterator::GetNextFile(char* fileName)
|
||||
{
|
||||
if (!current) return false;
|
||||
bool success = true;
|
||||
while(success)
|
||||
{
|
||||
WIN32_findData findData;
|
||||
if (current->hSearch == (HANDLE)-1)
|
||||
{
|
||||
// Construct search string
|
||||
current->GetFullName(fileName);
|
||||
strcat(fileName, "\\*");
|
||||
if ((HANDLE)-1 ==
|
||||
(current->hSearch = FindFirstFile(fileName, &findData)) )
|
||||
success = false;
|
||||
else
|
||||
success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = (0 != FindNextFile(current->hSearch, &findData));
|
||||
}
|
||||
|
||||
// Do we have a candidate file?
|
||||
if (success)
|
||||
{
|
||||
char* ptr = strrchr(findData.cFileName, DIR_DELIMITER);
|
||||
if (ptr)
|
||||
ptr += 1;
|
||||
else
|
||||
ptr = findData.cFileName;
|
||||
|
||||
// Skip "." and ".." directories
|
||||
if (ptr[0] == '.')
|
||||
{
|
||||
if ((1 == strlen(ptr)) ||
|
||||
((ptr[1] == '.') && (2 == strlen(ptr))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
current->GetFullName(fileName);
|
||||
strcat(fileName, ptr);
|
||||
NormFile::Type type = NormFile::GetType(fileName);
|
||||
if (NormFile::NORMAL == type)
|
||||
{
|
||||
int nameLen = strlen(fileName);
|
||||
nameLen = MIN(PATH_MAX, nameLen);
|
||||
nameLen -= path_len;
|
||||
memmove(fileName, fileName+path_len, nameLen);
|
||||
if (nameLen < PATH_MAX) fileName[nameLen] = '\0';
|
||||
return true;
|
||||
}
|
||||
else if (NormFile::DIRECTORY == type)
|
||||
{
|
||||
|
||||
NormDirectory *dir = new NormDirectory(ptr, current);
|
||||
if (dir && dir->Open())
|
||||
{
|
||||
// Push sub-directory onto stack and search it
|
||||
current = dir;
|
||||
return GetNextFile(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Couldn't open try next one
|
||||
if (dir) delete dir;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NormFile::INVALID file, try next one
|
||||
}
|
||||
} // end if(success)
|
||||
} // end while(success)
|
||||
|
||||
// if parent, popup a level and continue search
|
||||
if (current->parent)
|
||||
{
|
||||
current->Close();
|
||||
NormDirectory *dir = current;
|
||||
current = current->parent;
|
||||
delete dir;
|
||||
return GetNextFile(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
current->Close();
|
||||
delete current;
|
||||
current = NULL;
|
||||
return false;
|
||||
}
|
||||
} // end NormDirectoryIterator::GetNextFile() (WIN32)
|
||||
#else
|
||||
bool NormDirectoryIterator::GetNextFile(char* fileName)
|
||||
{
|
||||
if (!current) return false;
|
||||
struct dirent *dp;
|
||||
while((dp = readdir(current->dptr)))
|
||||
{
|
||||
// Make sure it's not "." or ".."
|
||||
if (dp->d_name[0] == '.')
|
||||
{
|
||||
if ((1 == strlen(dp->d_name)) ||
|
||||
((dp->d_name[1] == '.' ) && (2 == strlen(dp->d_name))))
|
||||
{
|
||||
continue; // skip "." and ".." directory names
|
||||
}
|
||||
}
|
||||
current->GetFullName(fileName);
|
||||
strcat(fileName, dp->d_name);
|
||||
NormFile::Type type = NormFile::GetType(fileName);
|
||||
if (NormFile::NORMAL == type)
|
||||
{
|
||||
int nameLen = strlen(fileName);
|
||||
nameLen = MIN(PATH_MAX, nameLen);
|
||||
nameLen -= path_len;
|
||||
memmove(fileName, fileName+path_len, nameLen);
|
||||
if (nameLen < PATH_MAX) fileName[nameLen] = '\0';
|
||||
return true;
|
||||
}
|
||||
else if (NormFile::DIRECTORY == type)
|
||||
{
|
||||
NormDirectory *dir = new NormDirectory(dp->d_name, current);
|
||||
if (dir && dir->Open())
|
||||
{
|
||||
// Push sub-directory onto stack and search it
|
||||
current = dir;
|
||||
return GetNextFile(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Couldn't open this one, try next one
|
||||
if (dir) delete dir;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// NormFile::INVALID, try next one
|
||||
}
|
||||
} // end while(readdir())
|
||||
|
||||
// Pop up a level and recursively continue or finish if done
|
||||
if (current->parent)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
current->parent->GetFullName(path);
|
||||
current->Close();
|
||||
NormDirectory *dir = current;
|
||||
current = current->parent;
|
||||
delete dir;
|
||||
return GetNextFile(fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
current->Close();
|
||||
delete current;
|
||||
current = NULL;
|
||||
return false; // no more files remain
|
||||
}
|
||||
} // end NormDirectoryIterator::GetNextFile() (UNIX)
|
||||
#endif // if/else WIN32
|
||||
|
||||
NormDirectoryIterator::NormDirectory::NormDirectory(const char* thePath,
|
||||
NormDirectory* theParent)
|
||||
: parent(theParent),
|
||||
#ifdef WIN32
|
||||
hSearch((HANDLE)-1)
|
||||
#else
|
||||
dptr(NULL)
|
||||
#endif // if/else WIN32
|
||||
{
|
||||
strncpy(path, thePath, PATH_MAX);
|
||||
int len = MIN(PATH_MAX, strlen(path));
|
||||
if ((len < PATH_MAX) && (DIR_DELIMITER != path[len-1]))
|
||||
{
|
||||
path[len++] = DIR_DELIMITER;
|
||||
if (len < PATH_MAX) path[len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
NormDirectoryIterator::NormDirectory::~NormDirectory()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool NormDirectoryIterator::NormDirectory::Open()
|
||||
{
|
||||
Close(); // in case it's already open
|
||||
char fullName[PATH_MAX];
|
||||
GetFullName(fullName);
|
||||
// Get rid of trailing DIR_DELIMITER
|
||||
int len = MIN(PATH_MAX, strlen(fullName));
|
||||
if (DIR_DELIMITER == fullName[len-1]) fullName[len-1] = '\0';
|
||||
#ifdef WIN32
|
||||
DWORD attr = GetFileAttributes(fullName);
|
||||
if (0xFFFFFFFF == attr)
|
||||
return false;
|
||||
else if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
#else
|
||||
if((dptr = opendir(fullName)))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
#endif // if/else WIN32
|
||||
|
||||
} // end NormDirectoryIterator::NormDirectory::Open()
|
||||
|
||||
void NormDirectoryIterator::NormDirectory::Close()
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (hSearch != (HANDLE)-1)
|
||||
{
|
||||
FindClose(hSearch);
|
||||
hSearch = (HANDLE)-1;
|
||||
}
|
||||
#else
|
||||
closedir(dptr);
|
||||
dptr = NULL;
|
||||
#endif // if/else WIN32
|
||||
} // end NormDirectoryIterator::NormDirectory::Close()
|
||||
|
||||
|
||||
void NormDirectoryIterator::NormDirectory::GetFullName(char* ptr)
|
||||
{
|
||||
ptr[0] = '\0';
|
||||
RecursiveCatName(ptr);
|
||||
} // end NormDirectoryIterator::NormDirectory::GetFullName()
|
||||
|
||||
void NormDirectoryIterator::NormDirectory::RecursiveCatName(char* ptr)
|
||||
{
|
||||
if (parent) parent->RecursiveCatName(ptr);
|
||||
int len = MIN(PATH_MAX, strlen(ptr));
|
||||
strncat(ptr, path, PATH_MAX-len);
|
||||
} // end NormDirectoryIterator::NormDirectory::RecursiveCatName()
|
||||
|
||||
// Below are some static routines for getting file/directory information
|
||||
|
||||
// Is the named item a valid directory or file (or neither)??
|
||||
NormFile::Type NormFile::GetType(const char* path)
|
||||
{
|
||||
#ifdef WIN32
|
||||
DWORD attr = GetFileAttributes(path);
|
||||
if (0xFFFFFFFF == attr)
|
||||
return INVALID; // error
|
||||
else if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return DIRECTORY;
|
||||
else
|
||||
return NORMAL;
|
||||
#else
|
||||
struct stat file_info;
|
||||
if (stat(path, &file_info))
|
||||
return INVALID; // stat() error
|
||||
else if ((S_ISDIR(file_info.st_mode)))
|
||||
return DIRECTORY;
|
||||
else
|
||||
return NORMAL;
|
||||
#endif // if/else WIN32
|
||||
} // end NormFile::GetType()
|
||||
|
||||
off_t NormFile::GetSize(const char* path)
|
||||
{
|
||||
#ifdef WIN32
|
||||
struct _stat info;
|
||||
int result = _stat(path, &info);
|
||||
#else
|
||||
struct stat info;
|
||||
int result = stat(path, &info);
|
||||
#endif // if/else WIN32
|
||||
if (result)
|
||||
{
|
||||
//DMSG(0, "Error getting file size: %s\n", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return info.st_size;
|
||||
}
|
||||
} // end NormFile::GetSize()
|
||||
|
||||
time_t NormFile::GetUpdateTime(const char* path)
|
||||
{
|
||||
#ifdef WIN32
|
||||
struct _stat info;
|
||||
int result = _stat(path, &info);
|
||||
#else
|
||||
struct stat info;
|
||||
int result = stat(path, &info);
|
||||
#endif // if/else WIN32
|
||||
if (result)
|
||||
{
|
||||
return (time_t)0; // stat() error
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef WIN32
|
||||
// Hack because Win2K and Win98 seem to work differently
|
||||
time_t updateTime = MAX(info.st_ctime, info.st_atime);
|
||||
updateTime = MAX(updateTime, info.st_mtime);
|
||||
return updateTime;
|
||||
#else
|
||||
return info.st_ctime;
|
||||
#endif // if/else WIN32
|
||||
}
|
||||
} // end NormFile::GetUpdateTime()
|
||||
|
||||
bool NormFile::IsLocked(const char* path)
|
||||
{
|
||||
// If file doesn't exist, it's not locked
|
||||
if (!Exists(path)) return false;
|
||||
NormFile testFile;
|
||||
#ifdef WIN32
|
||||
if(!testFile.Open(path, _O_WRONLY | _O_CREAT))
|
||||
#else
|
||||
if(!testFile.Open(path, O_WRONLY | O_CREAT))
|
||||
#endif // if/else WIN32
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (testFile.Lock())
|
||||
{
|
||||
// We were able to lock the file successfully
|
||||
testFile.Unlock();
|
||||
testFile.Close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
testFile.Close();
|
||||
return true;
|
||||
}
|
||||
} // end NormFile::IsLocked()
|
||||
|
||||
bool NormFile::Unlink(const char* path)
|
||||
{
|
||||
// Don't unlink a file that is open (locked)
|
||||
if (NormFile::IsLocked(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#ifdef WIN32
|
||||
else if (_unlink(path))
|
||||
#else
|
||||
else if (unlink(path))
|
||||
#endif // if/else WIN32
|
||||
{
|
||||
//DMSG(0, "NormFile::Unlink() unlink error: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} // end NormFile::Unlink()
|
||||
|
||||
NormFileList::NormFileList()
|
||||
: this_time(0), big_time(0), last_time(0),
|
||||
updates_only(false), head(NULL), tail(NULL), next(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
NormFileList::~NormFileList()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void NormFileList::Destroy()
|
||||
{
|
||||
while ((next = head))
|
||||
{
|
||||
head = next->next;
|
||||
delete next;
|
||||
}
|
||||
tail = NULL;
|
||||
} // end NormFileList::Destroy()
|
||||
|
||||
bool NormFileList::Append(const char* path)
|
||||
{
|
||||
FileItem* theItem = NULL;
|
||||
switch(NormFile::GetType(path))
|
||||
{
|
||||
case NormFile::NORMAL:
|
||||
theItem = new FileItem(path);
|
||||
break;
|
||||
case NormFile::DIRECTORY:
|
||||
theItem = new DirectoryItem(path);
|
||||
break;
|
||||
default:
|
||||
// Allow non-existent files for update_only mode
|
||||
// (TBD) allow non-existent directories?
|
||||
if (updates_only)
|
||||
{
|
||||
theItem = new FileItem(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormFileList::Append() Bad file/directory name: %s\n",
|
||||
path);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (theItem)
|
||||
{
|
||||
theItem->next = NULL;
|
||||
if ((theItem->prev = tail))
|
||||
tail->next = theItem;
|
||||
else
|
||||
head = theItem;
|
||||
tail = theItem;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormFileList::Append() Error creating file/directory item: %s\n",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
} // end NormFileList::Append()
|
||||
|
||||
bool NormFileList::Remove(const char* path)
|
||||
{
|
||||
FileItem* nextItem = head;
|
||||
unsigned int pathLen = strlen(path);
|
||||
pathLen = MIN(pathLen, PATH_MAX);
|
||||
while (nextItem)
|
||||
{
|
||||
unsigned nameLen = strlen(nextItem->Path());
|
||||
nameLen = MIN(nameLen, PATH_MAX);
|
||||
nameLen = MAX(nameLen, pathLen);
|
||||
if (!strncmp(path, nextItem->Path(), nameLen))
|
||||
{
|
||||
if (nextItem == next) next = nextItem->next;
|
||||
if (nextItem->prev)
|
||||
nextItem->prev = next = nextItem->next;
|
||||
else
|
||||
head = nextItem->next;
|
||||
if (nextItem->next)
|
||||
nextItem->next->prev = nextItem->prev;
|
||||
else
|
||||
tail = nextItem->prev;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} // end NormFileList::Remove()
|
||||
|
||||
bool NormFileList::GetNextFile(char* pathBuffer)
|
||||
{
|
||||
if (!next)
|
||||
{
|
||||
next = head;
|
||||
reset = true;
|
||||
}
|
||||
if (next)
|
||||
{
|
||||
if (next->GetNextFile(pathBuffer, reset, updates_only,
|
||||
last_time, this_time, big_time))
|
||||
{
|
||||
reset = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (next->next)
|
||||
{
|
||||
next = next->next;
|
||||
reset = true;
|
||||
return GetNextFile(pathBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
reset = false;
|
||||
return false; // end of list
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false; // empty list
|
||||
}
|
||||
} // end NormFileList::GetNextFile()
|
||||
|
||||
void NormFileList::GetCurrentBasePath(char* pathBuffer)
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
if (NormFile::DIRECTORY == next->GetType())
|
||||
{
|
||||
strncpy(pathBuffer, next->Path(), PATH_MAX);
|
||||
unsigned int len = strlen(pathBuffer);
|
||||
len = MIN(len, PATH_MAX);
|
||||
if (DIR_DELIMITER != pathBuffer[len-1])
|
||||
{
|
||||
if (len < PATH_MAX) pathBuffer[len++] = DIR_DELIMITER;
|
||||
if (len < PATH_MAX) pathBuffer[len] = '\0';
|
||||
}
|
||||
}
|
||||
else // NormFile::NORMAL
|
||||
{
|
||||
const char* ptr = strrchr(next->Path(), DIR_DELIMITER);
|
||||
if (ptr++)
|
||||
{
|
||||
unsigned int len = ptr - next->Path();
|
||||
strncpy(pathBuffer, next->Path(), len);
|
||||
pathBuffer[len] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
pathBuffer[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pathBuffer[0] = '\0';
|
||||
}
|
||||
} // end NormFileList::GetBasePath()
|
||||
|
||||
|
||||
NormFileList::FileItem::FileItem(const char* thePath)
|
||||
: prev(NULL), next(NULL)
|
||||
{
|
||||
unsigned int len = strlen(thePath);
|
||||
len = MIN(len, PATH_MAX);
|
||||
strncpy(path, thePath, PATH_MAX);
|
||||
size = NormFile::GetSize(thePath);
|
||||
}
|
||||
|
||||
NormFileList::FileItem::~FileItem()
|
||||
{
|
||||
}
|
||||
|
||||
bool NormFileList::FileItem::GetNextFile(char* thePath,
|
||||
bool reset,
|
||||
bool updatesOnly,
|
||||
time_t lastTime,
|
||||
time_t thisTime,
|
||||
time_t& bigTime)
|
||||
{
|
||||
if (reset)
|
||||
{
|
||||
if (updatesOnly)
|
||||
{
|
||||
time_t updateTime = NormFile::GetUpdateTime(thePath);
|
||||
if (updateTime > bigTime) bigTime = updateTime;
|
||||
if ((updateTime <= lastTime) || (updateTime > thisTime))
|
||||
return false;
|
||||
}
|
||||
strncpy(thePath, path, PATH_MAX);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} // end NormFileList::FileItem::GetNextFile()
|
||||
|
||||
NormFileList::DirectoryItem::DirectoryItem(const char* thePath)
|
||||
: NormFileList::FileItem(thePath)
|
||||
{
|
||||
}
|
||||
|
||||
NormFileList::DirectoryItem::~DirectoryItem()
|
||||
{
|
||||
diterator.Close();
|
||||
}
|
||||
|
||||
bool NormFileList::DirectoryItem::GetNextFile(char* thePath,
|
||||
bool reset,
|
||||
bool updatesOnly,
|
||||
time_t lastTime,
|
||||
time_t thisTime,
|
||||
time_t& bigTime)
|
||||
{
|
||||
if (reset)
|
||||
{
|
||||
/* For now we are going to poll all files in a directory individually
|
||||
since directory update times aren't always changed when files are
|
||||
are replaced within the directory tree ... uncomment this code
|
||||
if you only want to check directory nodes that have had their
|
||||
change time updated
|
||||
if (updates_only)
|
||||
{
|
||||
// Check to see if directory has been touched
|
||||
time_t update_time = MdpFileGetUpdateTime(path);
|
||||
if (updateTime > bigTime) *bigTime = updateTime;
|
||||
if ((updateTime <= lastTime) || (updateTime > thisTime))
|
||||
return false;
|
||||
} */
|
||||
if (!diterator.Open(path))
|
||||
{
|
||||
DMSG(0, "NormFileList::DirectoryItem::GetNextFile() Directory iterator init error\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
strncpy(thePath, path, PATH_MAX);
|
||||
unsigned int len = strlen(thePath);
|
||||
len = MIN(len, PATH_MAX);
|
||||
if ((DIR_DELIMITER != thePath[len-1]) && (len < PATH_MAX))
|
||||
{
|
||||
thePath[len++] = DIR_DELIMITER;
|
||||
if (len < PATH_MAX) thePath[len] = '\0';
|
||||
}
|
||||
char tempPath[PATH_MAX];
|
||||
while (diterator.GetNextFile(tempPath))
|
||||
{
|
||||
unsigned int maxLen = PATH_MAX - len;
|
||||
strncat(thePath, tempPath, maxLen);
|
||||
if (updatesOnly)
|
||||
{
|
||||
time_t updateTime = NormFile::GetUpdateTime(thePath);
|
||||
if (updateTime > bigTime) bigTime = updateTime;
|
||||
if ((updateTime <= lastTime) || (updateTime > thisTime))
|
||||
{
|
||||
thePath[len] = '\0';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} // end NormFileList::DirectoryItem::GetNextFile()
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
#ifndef _NORM_FILE
|
||||
#define _NORM_FILE
|
||||
|
||||
// This module defines some simple classes for manipulating files.
|
||||
// Unix and Win32 platforms are supported. Routines for iterating
|
||||
// over directories are also provided. And a file/directory list
|
||||
// class is provided to manage a list of files.
|
||||
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#endif // if/else WIN32
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
// From PROTOLIB
|
||||
#include "sysdefs.h" // for bool definition, DIR_DELIMITER, PATH_MAX, etc
|
||||
#include "debug.h" // for DEBUG stuff
|
||||
|
||||
class NormFile
|
||||
{
|
||||
// Methods
|
||||
public:
|
||||
enum Type {INVALID, NORMAL, DIRECTORY};
|
||||
NormFile();
|
||||
~NormFile();
|
||||
bool Open(const char* path, int theFlags);
|
||||
bool Lock();
|
||||
void Unlock();
|
||||
bool Rename(const char* oldName, const char* newName);
|
||||
bool Unlink(const char *path);
|
||||
void Close();
|
||||
bool IsOpen() const {return (fd >= 0);}
|
||||
int Read(char* buffer, int len);
|
||||
int Write(const char* buffer, int len);
|
||||
bool Seek(off_t theOffset);
|
||||
off_t GetOffset() const {return (offset);}
|
||||
off_t GetSize() const;
|
||||
|
||||
// static helper methods
|
||||
static NormFile::Type GetType(const char *path);
|
||||
static off_t GetSize(const char* path);
|
||||
static time_t GetUpdateTime(const char* path);
|
||||
static bool IsLocked(const char *path);
|
||||
|
||||
static bool Exists(const char* path)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return (0xFFFFFFFF != GetFileAttributes(path));
|
||||
#else
|
||||
return (0 == access(path, F_OK));
|
||||
#endif // if/else WIN32
|
||||
}
|
||||
|
||||
static bool IsWritable(const char* path)
|
||||
{
|
||||
#ifdef WIN32
|
||||
DWORD attr = GetFileAttributes(path);
|
||||
return ((0xFFFFFFFF == attr) ?
|
||||
false : (0 == (attr & FILE_ATTRIBUTE_READONLY)));
|
||||
#else
|
||||
return (0 == access(path, W_OK));
|
||||
}
|
||||
#endif // if/else WIN32
|
||||
|
||||
|
||||
// Members
|
||||
private:
|
||||
int fd;
|
||||
int flags;
|
||||
off_t offset;
|
||||
};
|
||||
|
||||
/******************************************
|
||||
* The NormDirectory and NormDirectoryIterator classes
|
||||
* are used to walk directory trees for file transmission
|
||||
*/
|
||||
|
||||
class NormDirectoryIterator
|
||||
{
|
||||
public:
|
||||
NormDirectoryIterator();
|
||||
~NormDirectoryIterator();
|
||||
bool Open(const char*thePath);
|
||||
void Close();
|
||||
bool GetPath(char* pathBuffer);
|
||||
// "buffer" should be PATH_MAX long!
|
||||
bool GetNextFile(char* buffer);
|
||||
|
||||
private:
|
||||
class NormDirectory
|
||||
{
|
||||
friend class NormDirectoryIterator;
|
||||
private:
|
||||
char path[PATH_MAX];
|
||||
NormDirectory* parent;
|
||||
#ifdef WIN32
|
||||
HANDLE hSearch;
|
||||
#else
|
||||
DIR* dptr;
|
||||
#endif // if/else WIN32
|
||||
NormDirectory(const char *thePath, NormDirectory* theParent = NULL);
|
||||
~NormDirectory();
|
||||
void GetFullName(char* namePtr);
|
||||
bool Open();
|
||||
void Close();
|
||||
|
||||
const char* Path() const {return path;}
|
||||
void RecursiveCatName(char* ptr);
|
||||
}; // end class NormDirectoryIterator::NormDirectory
|
||||
|
||||
NormDirectory* current;
|
||||
int path_len;
|
||||
}; // end class NormDirectoryIterator
|
||||
|
||||
|
||||
class NormFileList
|
||||
{
|
||||
public:
|
||||
NormFileList();
|
||||
~NormFileList();
|
||||
void Destroy();
|
||||
bool IsEmpty() {return (NULL == head);}
|
||||
void ResetIterator()
|
||||
{
|
||||
last_time = this_time;
|
||||
this_time = big_time;
|
||||
next = NULL;
|
||||
reset = true;
|
||||
}
|
||||
void InitUpdateTime(bool updatesOnly, time_t initTime = 0)
|
||||
{
|
||||
updates_only = updatesOnly;
|
||||
last_time = this_time = big_time = initTime;
|
||||
}
|
||||
|
||||
bool Append(const char* path);
|
||||
bool Remove(const char* path);
|
||||
bool GetNextFile(char* pathBuffer);
|
||||
void GetCurrentBasePath(char* pathBuffer);
|
||||
|
||||
private:
|
||||
class FileItem
|
||||
{
|
||||
friend class NormFileList;
|
||||
public:
|
||||
FileItem(const char* thePath);
|
||||
virtual ~FileItem();
|
||||
NormFile::Type GetType() {return NormFile::GetType(path);}
|
||||
off_t Size() const {return size;}
|
||||
virtual bool GetNextFile(char* thePath,
|
||||
bool reset,
|
||||
bool updatesOnly,
|
||||
time_t lastTime,
|
||||
time_t thisTime,
|
||||
time_t& bigTime);
|
||||
|
||||
protected:
|
||||
const char* Path() {return path;}
|
||||
|
||||
char path[PATH_MAX];
|
||||
off_t size;
|
||||
FileItem* prev;
|
||||
FileItem* next;
|
||||
};
|
||||
class DirectoryItem : public FileItem
|
||||
{
|
||||
friend class NormFileList;
|
||||
public:
|
||||
DirectoryItem(const char* thePath);
|
||||
~DirectoryItem();
|
||||
virtual bool GetNextFile(char* thePath,
|
||||
bool reset,
|
||||
bool updatesOnly,
|
||||
time_t lastTime,
|
||||
time_t thisTime,
|
||||
time_t& bigTime);
|
||||
private:
|
||||
NormDirectoryIterator diterator;
|
||||
};
|
||||
|
||||
time_t this_time;
|
||||
time_t big_time;
|
||||
time_t last_time;
|
||||
bool updates_only;
|
||||
FileItem* head;
|
||||
FileItem* tail;
|
||||
FileItem* next;
|
||||
bool reset;
|
||||
}; // end class NormFileList
|
||||
|
||||
#endif // _NORM_FILE
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
#include "normMessage.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
NormRepairRequest::NormRepairRequest()
|
||||
: form(INVALID), flags(0), length(0), buffer(NULL), buffer_len(0)
|
||||
{
|
||||
|
|
@ -11,7 +9,13 @@ bool NormRepairRequest::AppendRepairItem(const NormObjectId& objectId,
|
|||
const NormBlockId& blockId,
|
||||
UINT16 symbolId)
|
||||
{
|
||||
if (buffer_len >= (CONTENT_OFFSET+length+RepairItemLength()))
|
||||
if (RANGES == form)
|
||||
DMSG(4, "NormRepairRequest::AppendRepairRange(obj>%hu blk>%lu seg>%hu) ...\n",
|
||||
(UINT16)objectId, (UINT32)blockId, (UINT32)symbolId);
|
||||
else
|
||||
DMSG(4, "NormRepairRequest::AppendRepairItem(obj>%hu blk>%lu seg>%hu) ...\n",
|
||||
(UINT16)objectId, (UINT32)blockId, (UINT32)symbolId);
|
||||
if (buffer_len >= (length+CONTENT_OFFSET+RepairItemLength()))
|
||||
{
|
||||
UINT16 temp16 = htons((UINT16)objectId);
|
||||
memcpy(buffer+CONTENT_OFFSET+length, &temp16, 2);
|
||||
|
|
@ -35,7 +39,10 @@ bool NormRepairRequest::AppendRepairRange(const NormObjectId& startObjectId,
|
|||
const NormBlockId& endBlockId,
|
||||
UINT16 endSymbolId)
|
||||
{
|
||||
if (buffer_len >= (CONTENT_OFFSET+length+RepairRangeLength()))
|
||||
DMSG(4, "NormRepairRequest::AppendRepairRange(%hu:%lu:%hu->%hu:%lu:%hu) ...\n",
|
||||
(UINT16)startObjectId, (UINT32)startBlockId, (UINT32)startSymbolId,
|
||||
(UINT16)endObjectId, (UINT32)endBlockId, (UINT32)endSymbolId);
|
||||
if (buffer_len >= (length+CONTENT_OFFSET+RepairRangeLength()))
|
||||
{
|
||||
// range start
|
||||
UINT16 temp16;
|
||||
|
|
@ -257,6 +264,28 @@ NormMessage* NormMessageQueue::RemoveTail()
|
|||
}
|
||||
} // end NormMessageQueue::RemoveTail()
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* RTT quantization routines:
|
||||
* These routines are valid for 1.0e-06 <= RTT <= 1000.0 seconds
|
||||
* They allow us to pack our RTT estimates into a 1 byte fields
|
||||
*/
|
||||
|
||||
// valid for rtt = 1.0e-06 to 1.0e+03
|
||||
unsigned char NormQuantizeRtt(double rtt)
|
||||
{
|
||||
if (rtt > NORM_RTT_MAX)
|
||||
rtt = NORM_RTT_MAX;
|
||||
else if (rtt < NORM_RTT_MIN)
|
||||
rtt = NORM_RTT_MIN;
|
||||
if (rtt < 3.3e-05)
|
||||
return ((unsigned char)ceil((rtt*NORM_RTT_MIN)) - 1);
|
||||
else
|
||||
return ((unsigned char)(ceil(255.0 - (13.0*log(NORM_RTT_MAX/rtt)))));
|
||||
} // end NormQuantizeRtt()
|
||||
|
||||
|
||||
|
||||
bool NormObjectSize::operator<(const NormObjectSize& size) const
|
||||
{
|
||||
UINT16 diff = msb - size.msb;
|
||||
|
|
@ -314,16 +343,15 @@ NormObjectSize NormObjectSize::operator*(const NormObjectSize& b) const
|
|||
return result;
|
||||
} // end NormObjectSize::operator*(NormObjectSize size)
|
||||
|
||||
// Note: This always rounds up if there is _any_ remainder
|
||||
NormObjectSize NormObjectSize::operator/(const NormObjectSize& b) const
|
||||
{
|
||||
// Zero dividend is special case
|
||||
if ((0 == lsb) && (0 == msb)) return NormObjectSize(0, 0);
|
||||
// Zero divisor is special case
|
||||
if ((0 == b.lsb) && (0 == b.msb)) return NormObjectSize(0xffff, 0xffffffff);
|
||||
// Dividend equals divisor is special case
|
||||
if (*this == b) return NormObjectSize(0,1);
|
||||
// Divisor > dividend is special case
|
||||
if ((b.lsb > lsb) && (b.msb > msb)) return NormObjectSize(0,1);
|
||||
// Divisor >= dividend is special case
|
||||
if ((b.lsb >= lsb) && (b.msb >= msb)) return NormObjectSize(0,1);
|
||||
// divisor
|
||||
UINT32 divisor[2];
|
||||
divisor[0] = (UINT32)b.msb;
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
#define _NORM_MESSAGE
|
||||
|
||||
// PROTOLIB includes
|
||||
#include <networkAddress.h> // for NetworkAddress class
|
||||
#include <sysdefs.h> // for UINT typedefs
|
||||
#include <debug.h>
|
||||
#include "networkAddress.h" // for NetworkAddress class
|
||||
#include "sysdefs.h" // for UINT typedefs
|
||||
#include "debug.h"
|
||||
|
||||
// standard includes
|
||||
#include <string.h> // for memcpy(), etc
|
||||
#include <math.h>
|
||||
|
||||
// (TBD) Alot of the "memcpy()" calls could be eliminated by
|
||||
// taking advantage of the alignment of NORM messsages
|
||||
|
|
@ -15,6 +16,32 @@
|
|||
const UINT16 NORM_MSG_SIZE_MAX = 8192;
|
||||
const int NORM_ROBUST_FACTOR = 20; // default robust factor
|
||||
|
||||
// These are the GRTT estimation bounds set for the current
|
||||
// NORM protocol. (Note that our Grtt quantization routines
|
||||
// are good for the range of 1.0e-06 <= 1000.0)
|
||||
const double NORM_GRTT_MIN = 0.001; // 1 msec
|
||||
const double NORM_GRTT_MAX = 15.0; // 15 sec
|
||||
const double NORM_RTT_MIN = 1.0e-06;
|
||||
const double NORM_RTT_MAX = 1000.0;
|
||||
inline double NormUnquantizeRtt(unsigned char qrtt)
|
||||
{
|
||||
return ((qrtt < 31) ?
|
||||
(((double)(qrtt+1))/(double)NORM_RTT_MIN) :
|
||||
(NORM_RTT_MAX/exp(((double)(255-qrtt))/(double)13.0)));
|
||||
}
|
||||
unsigned char NormQuantizeRtt(double rtt);
|
||||
|
||||
inline double NormUnquantizeGroupSize(unsigned char gsize)
|
||||
{
|
||||
return ((double)(gsize >> 4) * pow(10.0, (double)(gsize & 0x0f)));
|
||||
}
|
||||
inline unsigned char NormQuantizeGroupSize(double gsize)
|
||||
{
|
||||
unsigned char exponent = (unsigned char)log10(gsize);
|
||||
return ((((unsigned char)(gsize / pow(10.0, (double)exponent) + 0.5)) << 4)
|
||||
| exponent);
|
||||
}
|
||||
|
||||
// This class is used to describe object "size" and/or "offset"
|
||||
class NormObjectSize
|
||||
{
|
||||
|
|
@ -139,9 +166,8 @@ class NormMsg
|
|||
UINT32 temp32 = htonl(sender);
|
||||
memcpy(buffer+SENDER_OFFSET, &temp32, 4);
|
||||
}
|
||||
|
||||
void SetDestination(const NetworkAddress& dest)
|
||||
{addr = dest;}
|
||||
void SetDestination(const NetworkAddress& dst) {addr = dst;}
|
||||
void SetLength(UINT16 len) {length = len;}
|
||||
|
||||
// Message processing routines
|
||||
UINT8 GetVersion() const {return buffer[VERSION_OFFSET];}
|
||||
|
|
@ -158,13 +184,14 @@ class NormMsg
|
|||
memcpy(&temp32, buffer+SENDER_OFFSET, 4);
|
||||
return (ntohl(temp32));
|
||||
}
|
||||
const NetworkAddress& GetDestination() {return addr;}
|
||||
const NetworkAddress& GetSource() {return addr;}
|
||||
UINT16 GetLength() {return length;}
|
||||
|
||||
// For message buffer transmission/reception and misc.
|
||||
NetworkAddress* Src() {return &addr;}
|
||||
NetworkAddress* Dest() {return &addr;}
|
||||
char* GetBuffer() {return buffer;}
|
||||
UINT16 GetLength() const {return length;}
|
||||
void SetLength(UINT16 len) {length = len;}
|
||||
char* AccessBuffer() {return buffer;}
|
||||
UINT16 AccessLength() const {return length;}
|
||||
NetworkAddress& AccessAddress() {return addr;}
|
||||
|
||||
|
||||
protected:
|
||||
|
|
@ -230,9 +257,9 @@ class NormObjectMsg : public NormMsg
|
|||
}
|
||||
UINT16 GetFecBlockLen() const
|
||||
{
|
||||
UINT16 nparity;
|
||||
memcpy(&nparity, buffer+FEC_NDATA_OFFSET, 2);
|
||||
return (ntohs(nparity));
|
||||
UINT16 ndata;
|
||||
memcpy(&ndata, buffer+FEC_NDATA_OFFSET, 2);
|
||||
return (ntohs(ndata));
|
||||
}
|
||||
NormObjectId GetObjectId() const
|
||||
{
|
||||
|
|
@ -242,7 +269,7 @@ class NormObjectMsg : public NormMsg
|
|||
}
|
||||
|
||||
// Message building routines
|
||||
|
||||
void ResetFlags() {buffer[FLAGS_OFFSET] = 0;}
|
||||
void SetFlag(NormObjectMsg::Flag flag)
|
||||
{buffer[FLAGS_OFFSET] |= flag;}
|
||||
void SetGrtt(UINT8 grtt) {buffer[GRTT_OFFSET] = grtt;}
|
||||
|
|
@ -370,10 +397,10 @@ class NormDataMsg : public NormObjectMsg
|
|||
}
|
||||
bool IsData() const {return (GetFecSymbolId() < GetFecBlockLen());}
|
||||
const char* GetData() {return (buffer + DATA_OFFSET);}
|
||||
UINT16 GetDataLen() const {return (length - DATA_OFFSET);}
|
||||
UINT16 GetDataLength() const {return (length - DATA_OFFSET);}
|
||||
|
||||
const char* GetPayload() {return (buffer+LENGTH_OFFSET);}
|
||||
UINT16 GetPayloadLen() const {return (length - LENGTH_OFFSET);}
|
||||
UINT16 GetPayloadLength() const {return (length - LENGTH_OFFSET);}
|
||||
|
||||
bool IsParity() const {return (GetFecSymbolId() >= GetFecBlockLen());}
|
||||
// (Note: "payload_len" and "offset" field spaces are in the FEC payload)
|
||||
|
|
@ -447,9 +474,9 @@ class NormCmdMsg : public NormMsg
|
|||
{buffer[FLAVOR_OFFSET] = flavor;}
|
||||
|
||||
// Message processing
|
||||
UINT8 GetGrtt() {return buffer[GRTT_OFFSET];}
|
||||
UINT8 GetGroupSize() {return buffer[GSIZE_OFFSET];}
|
||||
NormCmdMsg::Flavor GetFlavor() {return (Flavor)buffer[FLAVOR_OFFSET];}
|
||||
UINT8 GetGrtt() const {return buffer[GRTT_OFFSET];}
|
||||
UINT8 GetGroupSize() const {return buffer[GSIZE_OFFSET];}
|
||||
NormCmdMsg::Flavor GetFlavor() const {return (Flavor)buffer[FLAVOR_OFFSET];}
|
||||
|
||||
protected:
|
||||
enum
|
||||
|
|
@ -466,6 +493,11 @@ class NormCmdFlushMsg : public NormCmdMsg
|
|||
enum Flag {NORM_FLUSH_FLAG_EOT = 0x01};
|
||||
|
||||
// Message building
|
||||
void Reset()
|
||||
{
|
||||
buffer[FLAGS_OFFSET] = 0;
|
||||
length = SYMBOL_ID_OFFSET + 2;
|
||||
}
|
||||
void SetFlag(NormCmdFlushMsg::Flag flag)
|
||||
{buffer[FLAGS_OFFSET] |= flag;}
|
||||
void UnsetFlag(NormCmdFlushMsg::Flag flag)
|
||||
|
|
@ -542,12 +574,13 @@ class NormCmdSquelchMsg : public NormCmdMsg
|
|||
}
|
||||
|
||||
void ResetInvalidObjectList() {length = OBJECT_LIST_OFFSET;}
|
||||
UINT16 AppendInvalidObject(NormObjectId objectId)
|
||||
bool AppendInvalidObject(NormObjectId objectId, UINT16 segmentSize)
|
||||
{
|
||||
if ((length-OBJECT_LIST_OFFSET+2) > segmentSize) return false;
|
||||
UINT16 temp16 = htons((UINT16)objectId);
|
||||
memcpy(buffer+length, &temp16, 2);
|
||||
length += 2;
|
||||
return length;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Message processing
|
||||
|
|
@ -716,8 +749,10 @@ class NormCmdApplicationMsg : public NormCmdMsg
|
|||
|
||||
class NormRepairRequest
|
||||
{
|
||||
friend class NormRepairRequest::Iterator;
|
||||
public:
|
||||
class Iterator;
|
||||
friend class NormRepairRequest::Iterator;
|
||||
|
||||
enum Form
|
||||
{
|
||||
INVALID,
|
||||
|
|
@ -748,8 +783,9 @@ class NormRepairRequest
|
|||
|
||||
// Repair request building
|
||||
void SetForm(NormRepairRequest::Form theForm) {form = theForm;}
|
||||
void ResetFlags() {flags = 0;}
|
||||
void SetFlag(NormRepairRequest::Flag theFlag) {flags |= theFlag;}
|
||||
void SetFlags(int theFlags) {flags |= theFlags;}
|
||||
//void SetFlags(int theFlags) {flags |= theFlags;}
|
||||
void UnsetFlag(NormRepairRequest::Flag theFlag) {flags &= ~theFlag;}
|
||||
|
||||
|
||||
|
|
@ -917,7 +953,7 @@ class NormReportMsg : public NormMsg
|
|||
// do some unions so we can easily use these
|
||||
// via casting or dereferencing the union members
|
||||
|
||||
class NormCommandMessage
|
||||
class NormCommandMsg
|
||||
{
|
||||
public:
|
||||
union
|
||||
|
|
@ -930,7 +966,7 @@ class NormCommandMessage
|
|||
NormCmdCCMsg cc;
|
||||
NormCmdApplicationMsg app;
|
||||
};
|
||||
}; // end class NormCommandMessage
|
||||
}; // end class NormCommandMsg
|
||||
|
||||
class NormMessage
|
||||
{
|
||||
|
|
@ -942,7 +978,7 @@ class NormMessage
|
|||
NormObjectMsg object;
|
||||
NormInfoMsg info;
|
||||
NormDataMsg data;
|
||||
NormCommandMessage cmd;
|
||||
NormCommandMsg cmd;
|
||||
NormNackMsg nack;
|
||||
NormAckMsg ack;
|
||||
NormReportMsg report;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -46,7 +46,9 @@ class NormServerNode : public NormNode
|
|||
NormServerNode(class NormSession* theSession, NormNodeId nodeId);
|
||||
~NormServerNode();
|
||||
|
||||
void HandleCommand(NormCommandMsg& cmd);
|
||||
void HandleObjectMessage(NormMessage& msg);
|
||||
void HandleNackMessage(NormNackMsg& nack);
|
||||
|
||||
bool Open(UINT16 segmentSize, UINT16 numData, UINT16 numParity);
|
||||
void Close();
|
||||
|
|
@ -54,8 +56,9 @@ class NormServerNode : public NormNode
|
|||
|
||||
bool SyncTest(const NormMessage& msg) const;
|
||||
void Sync(NormObjectId objectId);
|
||||
ObjectStatus GetObjectStatus(NormObjectId objectId) const;
|
||||
ObjectStatus UpdateSyncStatus(const NormObjectId& objectId);
|
||||
void SetPending(NormObjectId objectId);
|
||||
ObjectStatus GetObjectStatus(const NormObjectId& objectId) const;
|
||||
|
||||
void DeleteObject(NormObject* obj);
|
||||
|
||||
|
|
@ -77,7 +80,7 @@ class NormServerNode : public NormNode
|
|||
|
||||
void SetErasureLoc(UINT16 index, UINT16 value)
|
||||
{
|
||||
ASSERT(index < (nparity));
|
||||
ASSERT(index < nparity);
|
||||
erasure_loc[index] = value;
|
||||
}
|
||||
UINT16 GetErasureLoc(UINT16 index)
|
||||
|
|
@ -87,6 +90,27 @@ class NormServerNode : public NormNode
|
|||
return decoder.Decode(segmentList, numData, erasureCount, erasure_loc);
|
||||
}
|
||||
|
||||
void CalculateGrttResponse(struct timeval& grttResponse);
|
||||
|
||||
unsigned long CurrentBufferUsage()
|
||||
{return (segment_size * segment_pool.CurrentUsage());}
|
||||
unsigned long PeakBufferUsage()
|
||||
{return (segment_size * segment_pool.PeakUsage());}
|
||||
unsigned long BufferOverunCount()
|
||||
{return segment_pool.OverunCount() + block_pool.OverrunCount();}
|
||||
|
||||
unsigned long RecvTotal() {return recv_total;}
|
||||
unsigned long RecvGoodput() {return recv_goodput;}
|
||||
void IncrementRecvTotal(unsigned long count) {recv_total += count;}
|
||||
void IncrementRecvGoodput(unsigned long count) {recv_goodput += count;}
|
||||
void ResetRecvStats() {recv_total = recv_goodput = 0;}
|
||||
void IncrementResyncCount() {resync_count++;}
|
||||
unsigned long ResyncCount() {return resync_count;}
|
||||
unsigned long NackCount() {return nack_count;}
|
||||
unsigned long SuppressCount() {return suppress_count;}
|
||||
unsigned long CompletionCount() {return completion_count;}
|
||||
unsigned long PendingCount() {return rx_table.Count();}
|
||||
unsigned long FailureCount() {return failure_count;}
|
||||
|
||||
private:
|
||||
void RepairCheck(NormObject::CheckLevel checkLevel,
|
||||
|
|
@ -117,6 +141,22 @@ class NormServerNode : public NormNode
|
|||
ProtocolTimer repair_timer;
|
||||
NormObjectId current_object_id; // index for repair
|
||||
|
||||
double grtt_estimate;
|
||||
UINT8 grtt_quantized;
|
||||
struct timeval grtt_send_time;
|
||||
struct timeval grtt_recv_time;
|
||||
double gsize_estimate;
|
||||
UINT8 gsize_quantized;
|
||||
|
||||
// For statistics tracking
|
||||
unsigned long recv_total; // total recvd accumulator
|
||||
unsigned long recv_goodput; // goodput recvd accumulator
|
||||
unsigned long resync_count;
|
||||
unsigned long nack_count;
|
||||
unsigned long suppress_count;
|
||||
unsigned long completion_count;
|
||||
unsigned long failure_count; // due to re-syncs
|
||||
|
||||
}; // end class NodeServerNode
|
||||
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -2,6 +2,8 @@
|
|||
#define _NORM_OBJECT
|
||||
|
||||
#include "normSegment.h" // NORM segmentation classes
|
||||
#include "normEncoder.h"
|
||||
#include "normFile.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
|
@ -30,9 +32,12 @@ class NormObject
|
|||
virtual ~NormObject();
|
||||
NormObject::Type GetType() const {return type;}
|
||||
const NormObjectId& Id() const {return id;}
|
||||
const NormObjectSize& Size() {return object_size;}
|
||||
const NormObjectSize& Size() const {return object_size;}
|
||||
bool HaveInfo() const {return (info_len > 0);}
|
||||
const char* GetInfo() const {return info;}
|
||||
UINT16 InfoLength() const {return info_len;}
|
||||
bool IsStream() const {return (STREAM == type);}
|
||||
|
||||
bool IsStream() {return (STREAM == type);}
|
||||
NormNodeId LocalNodeId() const;
|
||||
|
||||
// Opens (inits) object for tx operation
|
||||
|
|
@ -47,30 +52,66 @@ class NormObject
|
|||
virtual bool WriteSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
const char* buffer) = 0;
|
||||
|
||||
virtual UINT16 ReadSegment(NormBlockId blockId, NormSegmentId segmentId,
|
||||
NormObjectSize* offset, char* buffer, UINT16 maxlen) = 0;
|
||||
virtual bool ReadSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
char* buffer) = 0;
|
||||
|
||||
// These are only valid after object is open
|
||||
NormBlockId LastBlockId();
|
||||
NormSegmentId LastSegmentId();
|
||||
NormSegmentId LastSegmentId(NormBlockId blockId);
|
||||
NormBlockId LastBlockId() const {return last_block_id;}
|
||||
NormSegmentId LastBlockSize() const {return last_block_size;}
|
||||
NormSegmentId BlockSize(NormBlockId blockId)
|
||||
{return ((blockId == last_block_id) ? last_block_size : ndata);}
|
||||
|
||||
// (TBD) re-write to use current_block_id/next_segment_id when
|
||||
// flush == false !!!
|
||||
bool IsPending(bool flush = true)
|
||||
{return (pending_info || pending_mask.IsSet());}
|
||||
bool IsPending(bool flush = true) const;
|
||||
bool IsRepairPending() const;
|
||||
bool IsPendingInfo() {return pending_info;}
|
||||
NormBlockId FirstPending() {return pending_mask.FirstSet();}
|
||||
|
||||
// Methods available to server for transmission
|
||||
bool NextServerMsg(NormMessage* msg);
|
||||
NormBlock* ServerRecoverBlock(NormBlockId blockId);
|
||||
bool CalculateBlockParity(NormBlock* block);
|
||||
|
||||
bool TxReset(NormBlockId firstBlock = NormBlockId(0));
|
||||
bool TxResetBlocks(NormBlockId nextId, NormBlockId lastId);
|
||||
bool TxUpdateBlock(NormBlock* theBlock,
|
||||
NormSegmentId firstSegmentId,
|
||||
NormSegmentId lastSegmentId,
|
||||
UINT16 numErasures)
|
||||
{
|
||||
NormBlockId blockId = theBlock->Id();
|
||||
bool result = theBlock->TxUpdate(firstSegmentId, lastSegmentId,
|
||||
BlockSize(blockId), nparity,
|
||||
segment_size, numErasures);
|
||||
if (result)
|
||||
{
|
||||
result = pending_mask.Set(blockId);
|
||||
ASSERT(result);
|
||||
}
|
||||
return result;
|
||||
} // end NormObject::TxUpdateBlock()
|
||||
bool HandleInfoRequest();
|
||||
bool HandleBlockRequest(NormBlockId nextId, NormBlockId lastId);
|
||||
bool SetPending(NormBlockId blockId)
|
||||
{return pending_mask.Set(blockId);}
|
||||
NormBlock* FindBlock(NormBlockId blockId)
|
||||
{
|
||||
return block_buffer.Find(blockId);
|
||||
}
|
||||
bool ActivateRepairs();
|
||||
bool IsRepairSet(NormBlockId blockId)
|
||||
{return repair_mask.Test(blockId);}
|
||||
|
||||
// Used by session server for resource management scheme
|
||||
NormBlock* StealOldestBlock(bool excludeBlock, NormBlockId excludeId = 0);
|
||||
NormBlock* StealNonPendingBlock(bool excludeBlock, NormBlockId excludeId = 0);
|
||||
|
||||
|
||||
// Methods available to client for reception
|
||||
bool Accepted() {return accepted;}
|
||||
void HandleObjectMessage(NormMessage& msg);
|
||||
void HandleObjectMessage(NormMessage& msg,
|
||||
NormMsgType msgType,
|
||||
NormBlockId blockId,
|
||||
NormSegmentId segmentId);
|
||||
bool StreamUpdateStatus(NormBlockId blockId);
|
||||
|
||||
// Used by remote server node for resource management scheme
|
||||
|
|
@ -82,7 +123,15 @@ class NormObject
|
|||
NormSegmentId segmentId,
|
||||
bool timerActive);
|
||||
bool IsRepairPending(bool flush);
|
||||
bool AppendRepairRequest(NormNackMsg& nack) {return true;}
|
||||
bool AppendRepairRequest(NormNackMsg& nack, bool flush);
|
||||
void SetRepairInfo() {repair_info = true;}
|
||||
bool SetRepairs(NormBlockId first, NormBlockId last)
|
||||
{
|
||||
if (first == last)
|
||||
return repair_mask.Set(first);
|
||||
else
|
||||
return repair_mask.SetBits(first, last-first+1);
|
||||
}
|
||||
|
||||
protected:
|
||||
NormObject(NormObject::Type theType,
|
||||
|
|
@ -109,7 +158,8 @@ class NormObject
|
|||
NormBlockId current_block_id;
|
||||
NormSegmentId next_segment_id;
|
||||
NormBlockId last_block_id;
|
||||
NormSegmentId last_segment_id;
|
||||
NormSegmentId last_block_size;
|
||||
UINT16 last_segment_size;
|
||||
|
||||
char* info;
|
||||
UINT16 info_len;
|
||||
|
|
@ -125,6 +175,47 @@ class NormObject
|
|||
}; // end class NormObject
|
||||
|
||||
|
||||
class NormFileObject : public NormObject
|
||||
{
|
||||
public:
|
||||
NormFileObject(class NormSession* theSession,
|
||||
class NormServerNode* theServer,
|
||||
const NormObjectId& objectId);
|
||||
~NormFileObject();
|
||||
|
||||
bool Open(const char* thePath,
|
||||
const char* infoPtr = NULL,
|
||||
UINT16 infoLen = 0);
|
||||
bool Accept(const char* thePath);
|
||||
void Close();
|
||||
const char* Path() {return path;}
|
||||
bool Rename(const char* newPath)
|
||||
{
|
||||
if (file.Rename(path, newPath))
|
||||
{
|
||||
strncpy(path, newPath, PATH_MAX);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool WriteSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
const char* buffer);
|
||||
|
||||
virtual bool ReadSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
char* buffer);
|
||||
|
||||
private:
|
||||
char path[PATH_MAX];
|
||||
NormFile file;
|
||||
NormObjectSize block_size;
|
||||
}; // end class NormFileObject
|
||||
|
||||
|
||||
class NormStreamObject : public NormObject
|
||||
{
|
||||
|
|
@ -144,15 +235,30 @@ class NormStreamObject : public NormObject
|
|||
virtual bool WriteSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
const char* buffer);
|
||||
virtual bool ReadSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
char* buffer);
|
||||
|
||||
virtual UINT16 ReadSegment(NormBlockId blockId, NormSegmentId segmentId,
|
||||
NormObjectSize* offset, char* buffer, UINT16 maxlen);
|
||||
unsigned long Read(char* buffer, unsigned long len);
|
||||
unsigned long Write(char* buffer, unsigned long len, bool flush = false);
|
||||
|
||||
// For receive stream, we can rewind to earliest buffered offset
|
||||
void Rewind();
|
||||
|
||||
bool LockBlocks(NormBlockId nextId, NormBlockId lastId);
|
||||
bool LockSegments(NormBlockId blockId, NormSegmentId firstId,
|
||||
NormSegmentId lastId);
|
||||
NormBlockId StreamBufferLo() {return stream_buffer.RangeLo();}
|
||||
void Prune(NormBlockId blockId);
|
||||
|
||||
bool IsFlushPending() {return flush_pending;}
|
||||
NormBlockId FlushBlockId()
|
||||
{return (write_index.segment ? write_index.block :
|
||||
(NormBlockId((UINT32)write_index.block-1)));}
|
||||
NormSegmentId FlushSegmentId()
|
||||
{return (write_index.segment ? (write_index.segment-1) :
|
||||
(ndata-1));}
|
||||
|
||||
private:
|
||||
class Index
|
||||
{
|
||||
|
|
@ -167,20 +273,52 @@ class NormStreamObject : public NormObject
|
|||
NormObjectSize write_offset;
|
||||
Index read_index;
|
||||
NormObjectSize read_offset;
|
||||
bool flush_pending;
|
||||
}; // end class NormStreamObject
|
||||
|
||||
#ifdef SIMULATE
|
||||
// This class is used to simulate file objects in the
|
||||
// network simulation build of NORM
|
||||
class NormSimObject : public NormObject
|
||||
{
|
||||
public:
|
||||
NormSimObject(class NormSession* theSession,
|
||||
class NormServerNode* theServer,
|
||||
const NormObjectId& objectId);
|
||||
~NormSimObject();
|
||||
|
||||
bool Open(unsigned long objectSize,
|
||||
const char* infoPtr = NULL,
|
||||
UINT16 infoLen = 0)
|
||||
{
|
||||
return server ? true : NormObject::Open(objectSize, infoPtr, infoLen);
|
||||
}
|
||||
bool Accept() {NormObject::Accept(); return true;}
|
||||
void Close() {NormObject::Close();}
|
||||
|
||||
virtual bool WriteSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
const char* buffer) {return true;}
|
||||
|
||||
virtual bool ReadSegment(NormBlockId blockId,
|
||||
NormSegmentId segmentId,
|
||||
char* buffer);
|
||||
}; // end class NormSimObject
|
||||
#endif // SIMULATE
|
||||
|
||||
class NormObjectTable
|
||||
{
|
||||
friend class NormObjectTable::Iterator;
|
||||
|
||||
public:
|
||||
class Iterator;
|
||||
friend class NormObjectTable::Iterator;
|
||||
|
||||
NormObjectTable();
|
||||
~NormObjectTable();
|
||||
bool Init(UINT16 rangeMax, UINT16 tableSize = 256);
|
||||
void Destroy();
|
||||
|
||||
bool IsInited() {return (NULL != table);}
|
||||
|
||||
bool CanInsert(NormObjectId objectId) const;
|
||||
bool Insert(NormObject* theObject);
|
||||
bool Remove(const NormObject* theObject);
|
||||
NormObject* Find(const NormObjectId& objectId) const;
|
||||
|
|
@ -188,6 +326,8 @@ class NormObjectTable
|
|||
NormObjectId RangeLo() {return range_lo;}
|
||||
NormObjectId RangeHi() {return range_hi;}
|
||||
bool IsEmpty() {return (0 == range);}
|
||||
unsigned long Count() {return count;}
|
||||
const NormObjectSize& Size() {return size;}
|
||||
|
||||
class Iterator
|
||||
{
|
||||
|
|
@ -213,6 +353,8 @@ class NormObjectTable
|
|||
unsigned long range; // zero if "object table" is empty
|
||||
NormObjectId range_lo;
|
||||
NormObjectId range_hi;
|
||||
unsigned long count;
|
||||
NormObjectSize size;
|
||||
}; // end class NormObjectTable
|
||||
|
||||
#endif // _NORM_OBJECT
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ char* NormSegmentPool::Get()
|
|||
{
|
||||
seg_list = *ptr;
|
||||
seg_count--;
|
||||
#ifdef NORM_DEBUG
|
||||
//#ifdef NORM_DEBUG
|
||||
overrun_flag = false;
|
||||
unsigned int usage = seg_total - seg_count;
|
||||
if (usage > peak_usage) peak_usage = usage;
|
||||
|
|
@ -79,7 +79,7 @@ char* NormSegmentPool::Get()
|
|||
overruns++;
|
||||
overrun_flag = true;
|
||||
}
|
||||
#endif // NORM_DEBUG
|
||||
//#endif // NORM_DEBUG
|
||||
}
|
||||
return ((char*)ptr);
|
||||
} // end NormSegmentPool::GetSegment()
|
||||
|
|
@ -122,6 +122,7 @@ bool NormBlock::Init(UINT16 blockSize)
|
|||
size = blockSize;
|
||||
erasure_count = 0;
|
||||
parity_count = 0;
|
||||
parity_offset = 0;;
|
||||
return true;
|
||||
} // end NormBlock::Init()
|
||||
|
||||
|
|
@ -133,7 +134,10 @@ void NormBlock::Destroy()
|
|||
if (segment_table)
|
||||
{
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
{
|
||||
ASSERT(!segment_table[i]);
|
||||
if (segment_table[i]) delete []segment_table[i];
|
||||
}
|
||||
delete []segment_table;
|
||||
segment_table = (char**)NULL;
|
||||
}
|
||||
|
|
@ -161,17 +165,319 @@ bool NormBlock::IsEmpty()
|
|||
return true;
|
||||
} // end NormBlock::EmptyToPool()
|
||||
|
||||
bool NormBlock::IsRepairPending(NormSegmentId end)
|
||||
// Used by client side to determine if NACK should be sent
|
||||
// Note: This clears the block's "repair_mask" state
|
||||
bool NormBlock::IsRepairPending(UINT16 ndata, UINT16 nparity)
|
||||
{
|
||||
ASSERT(end <= repair_mask.Size());
|
||||
repair_mask.SetBits(end, repair_mask.Size() - end);
|
||||
// Clients ask for a block of parity to fulfill their
|
||||
// repair needs (erasure_count), but if there isn't
|
||||
// enough parity, they ask for some data segments, too
|
||||
if (erasure_count > nparity)
|
||||
{
|
||||
if (nparity)
|
||||
{
|
||||
UINT16 i = nparity;
|
||||
NormSegmentId nextId = pending_mask.FirstSet();
|
||||
while (i--)
|
||||
{
|
||||
// (TBD) for more NACK suppression, we could skip ahead
|
||||
// if this bit is already set in repair_mask?
|
||||
repair_mask.Set(nextId); // set bit a parity can fill
|
||||
nextId++;
|
||||
nextId = pending_mask.NextSet(nextId);
|
||||
}
|
||||
}
|
||||
else if (size > ndata)
|
||||
{
|
||||
repair_mask.SetBits(ndata, size-ndata);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
repair_mask.SetBits(0, ndata);
|
||||
repair_mask.SetBits(ndata+erasure_count, nparity-erasure_count);
|
||||
}
|
||||
repair_mask.XCopy(pending_mask);
|
||||
return (repair_mask.IsSet() ? (repair_mask.FirstSet() < end) : false);
|
||||
return (repair_mask.IsSet());
|
||||
} // end NormBlock::IsRepairPending()
|
||||
|
||||
// Called by server
|
||||
bool NormBlock::TxReset(UINT16 ndata,
|
||||
UINT16 nparity,
|
||||
UINT16 autoParity,
|
||||
UINT16 segmentSize)
|
||||
{
|
||||
bool increasedRepair = false;
|
||||
repair_mask.SetBits(0, ndata+autoParity);
|
||||
repair_mask.UnsetBits(ndata+autoParity, nparity-autoParity);
|
||||
repair_mask.Xor(pending_mask);
|
||||
if (repair_mask.IsSet())
|
||||
{
|
||||
increasedRepair = true;
|
||||
repair_mask.Clear();
|
||||
pending_mask.SetBits(0, ndata+autoParity);
|
||||
pending_mask.UnsetBits(ndata+autoParity, nparity-autoParity);
|
||||
parity_offset = autoParity; // reset parity since we're resending this one
|
||||
parity_count = nparity; // no parity repair this repair cycle
|
||||
SetFlag(IN_REPAIR);
|
||||
if (!ParityReady(ndata)) // (TBD) only when incrementalParity == true
|
||||
{
|
||||
// Clear _any_ existing incremental parity state
|
||||
char** ptr = segment_table+ndata;
|
||||
while (nparity--)
|
||||
{
|
||||
if (*ptr) memset(*ptr, 0, segmentSize);
|
||||
ptr++;
|
||||
}
|
||||
erasure_count = 0;
|
||||
}
|
||||
}
|
||||
return increasedRepair;
|
||||
} // end NormBlock::TxReset()
|
||||
|
||||
bool NormBlock::ActivateRepairs(UINT16 nparity)
|
||||
{
|
||||
if (repair_mask.IsSet())
|
||||
{
|
||||
pending_mask.Add(repair_mask);
|
||||
repair_mask.Clear();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} // end NormBlock::ActivateRepairs()
|
||||
|
||||
// For NACKs arriving during server repair_timer "holdoff" time
|
||||
bool NormBlock::TxUpdate(NormSegmentId nextId, NormSegmentId lastId,
|
||||
UINT16 ndata, UINT16 nparity,
|
||||
UINT16 segmentSize, UINT16 erasureCount)
|
||||
{
|
||||
bool increasedRepair = false;
|
||||
|
||||
if (nextId < ndata)
|
||||
{
|
||||
// Explicit data repair request
|
||||
parity_offset = parity_count = nparity;
|
||||
while (nextId <= lastId)
|
||||
{
|
||||
if (!pending_mask.Test(nextId))
|
||||
{
|
||||
pending_mask.Set(nextId);
|
||||
increasedRepair = true;
|
||||
}
|
||||
nextId++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// parity repair request
|
||||
UINT16 parityAvailable = nparity - parity_offset;
|
||||
if (erasureCount <= parityAvailable)
|
||||
{
|
||||
// Use fresh parity for repair
|
||||
if (erasureCount > parity_count)
|
||||
{
|
||||
pending_mask.SetBits(ndata+parity_offset+parity_count,
|
||||
erasureCount - parity_count);
|
||||
parity_count = erasureCount;
|
||||
increasedRepair = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use any remaining fresh parity ...
|
||||
if (parity_count < parityAvailable)
|
||||
{
|
||||
UINT16 count = parityAvailable - parity_count;
|
||||
pending_mask.SetBits(ndata+parity_offset+parity_count, count);
|
||||
parity_count = parityAvailable;
|
||||
nextId += parityAvailable;
|
||||
increasedRepair = true;
|
||||
}
|
||||
// and explicit repair for the rest
|
||||
while (nextId <= lastId)
|
||||
{
|
||||
if (!pending_mask.Test(nextId))
|
||||
{
|
||||
pending_mask.Set(nextId);
|
||||
increasedRepair = true;
|
||||
}
|
||||
nextId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return increasedRepair;
|
||||
} // end NormBlock::TxUpdate()
|
||||
|
||||
bool NormBlock::HandleSegmentRequest(NormSegmentId nextId, NormSegmentId lastId,
|
||||
UINT16 ndata, UINT16 nparity, UINT16 erasureCount)
|
||||
{
|
||||
DMSG(4, "NormBlock::HandleSegmentRequest() blk>%lu seg>%hu:%hu erasures:%hu\n",
|
||||
(UINT32)id, (UINT16)nextId, (UINT16)lastId, erasureCount);
|
||||
bool increasedRepair = false;
|
||||
if (nextId < ndata)
|
||||
{
|
||||
// Explicit data repair request
|
||||
parity_count = parity_offset = nparity;
|
||||
while (nextId <= lastId)
|
||||
{
|
||||
if (!repair_mask.Test(nextId))
|
||||
{
|
||||
repair_mask.Set(nextId);
|
||||
increasedRepair = true;
|
||||
}
|
||||
nextId++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// parity repair request
|
||||
UINT16 parityAvailable = nparity - parity_offset;
|
||||
if (erasureCount <= parityAvailable)
|
||||
{
|
||||
// Use fresh parity for repair
|
||||
if (erasureCount > parity_count)
|
||||
{
|
||||
repair_mask.SetBits(ndata+parity_offset+parity_count,
|
||||
erasureCount - parity_count);
|
||||
parity_count = erasureCount;
|
||||
increasedRepair = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use any remaining fresh parity ...
|
||||
if (parity_count < parityAvailable)
|
||||
{
|
||||
UINT16 count = parityAvailable - parity_count;
|
||||
repair_mask.SetBits(ndata+parity_offset+parity_count, count);
|
||||
parity_count = parityAvailable;
|
||||
nextId += parityAvailable;
|
||||
increasedRepair = true;
|
||||
}
|
||||
// and explicit repair for the rest
|
||||
while (nextId <= lastId)
|
||||
{
|
||||
if (!repair_mask.Test(nextId))
|
||||
{
|
||||
repair_mask.Set(nextId);
|
||||
increasedRepair = true;
|
||||
}
|
||||
nextId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return increasedRepair;
|
||||
} // end NormBlock::HandleSegmentRequest()
|
||||
|
||||
|
||||
// Called by client
|
||||
bool NormBlock::AppendRepairRequest(NormNackMsg& nack,
|
||||
UINT16 ndata,
|
||||
UINT16 nparity,
|
||||
NormObjectId objectId,
|
||||
bool pendingInfo,
|
||||
UINT16 segmentSize)
|
||||
{
|
||||
ASSERT(pending_mask.FirstSet() < ndata);
|
||||
NormRepairRequest req;
|
||||
nack.AttachRepairRequest(req, segmentSize);
|
||||
req.SetFlag(NormRepairRequest::SEGMENT);
|
||||
if (pendingInfo) req.SetFlag(NormRepairRequest::INFO);
|
||||
NormSegmentId nextId, lastId;
|
||||
|
||||
if (erasure_count > nparity)
|
||||
{
|
||||
// Request explicit repair
|
||||
nextId = pending_mask.FirstSet();
|
||||
UINT16 i = nparity;
|
||||
// Skip nparity missing data segments
|
||||
while (i--)
|
||||
{
|
||||
nextId++;
|
||||
nextId = pending_mask.NextSet(nextId);
|
||||
}
|
||||
lastId = ndata + nparity;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextId = pending_mask.NextSet(ndata);
|
||||
lastId = ndata + erasure_count;
|
||||
}
|
||||
NormRepairRequest::Form prevForm = NormRepairRequest::INVALID;
|
||||
UINT16 reqCount = 0;
|
||||
NormSegmentId prevId = nextId;
|
||||
while ((nextId <= lastId) || (reqCount > 0))
|
||||
{
|
||||
// force break of possible ending consec. series
|
||||
if (nextId == lastId) nextId++;
|
||||
if (reqCount && (reqCount == (nextId - prevId)))
|
||||
{
|
||||
reqCount++; // consecutive series continues
|
||||
}
|
||||
else
|
||||
{
|
||||
NormRepairRequest::Form nextForm;
|
||||
switch(reqCount)
|
||||
{
|
||||
case 0:
|
||||
nextForm = NormRepairRequest::INVALID;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
nextForm = NormRepairRequest::ITEMS;
|
||||
break;
|
||||
default:
|
||||
nextForm = NormRepairRequest::RANGES;
|
||||
break;
|
||||
} // end switch(reqCount)
|
||||
if (prevForm != nextForm)
|
||||
{
|
||||
if (NormRepairRequest::INVALID != prevForm)
|
||||
nack.PackRepairRequest(req); // (TBD) error check
|
||||
if (NormRepairRequest::INVALID != nextForm)
|
||||
{
|
||||
nack.AttachRepairRequest(req, segmentSize);
|
||||
req.SetForm(nextForm);
|
||||
req.SetFlag(NormRepairRequest::SEGMENT);
|
||||
if (pendingInfo) req.SetFlag(NormRepairRequest::INFO);
|
||||
}
|
||||
prevForm = nextForm;
|
||||
}
|
||||
if (NormRepairRequest::INVALID != nextForm)
|
||||
DMSG(6, "NormBlock::AppendRepairRequest() SEGMENT request\n");
|
||||
switch (nextForm)
|
||||
{
|
||||
case NormRepairRequest::ITEMS:
|
||||
req.AppendRepairItem(objectId, id, prevId); // (TBD) error check
|
||||
if (2 == reqCount)
|
||||
req.AppendRepairItem(objectId, id, prevId+1); // (TBD) error check
|
||||
break;
|
||||
case NormRepairRequest::RANGES:
|
||||
req.AppendRepairItem(objectId, id, prevId); // (TBD) error check
|
||||
req.AppendRepairItem(objectId, id, prevId+reqCount-1); // (TBD) error check
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // end switch(nextForm)
|
||||
prevId = nextId;
|
||||
if (nextId < lastId)
|
||||
reqCount = 1;
|
||||
else
|
||||
reqCount = 0;
|
||||
} // end if/else (reqCount && (reqCount == (nextId - prevId)))
|
||||
nextId++;
|
||||
if (nextId <= lastId) nextId = pending_mask.NextSet(nextId);
|
||||
} // end while(nextId <= lastId)
|
||||
if (NormRepairRequest::INVALID != prevForm)
|
||||
nack.PackRepairRequest(req); // (TBD) error check
|
||||
return true;
|
||||
} // end NormBlock::AppendRepairRequest()
|
||||
|
||||
NormBlockPool::NormBlockPool()
|
||||
: head((NormBlock*)NULL)
|
||||
: head((NormBlock*)NULL), overruns(0), overrun_flag(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -273,10 +579,11 @@ NormBlock* NormBlockBuffer::Find(const NormBlockId& blockId) const
|
|||
{
|
||||
if (range)
|
||||
{
|
||||
if ((blockId < range_lo) || (blockId > range_hi)) return (NormBlock*)NULL;
|
||||
if ((blockId < range_lo) || (blockId > range_hi))
|
||||
return (NormBlock*)NULL;
|
||||
NormBlock* theBlock = table[((UINT32)blockId) & hash_mask];
|
||||
//printf("NormBlockBuffer::Find() table[%lu] = %p\n", ((UINT32)blockId) & hash_mask, theBlock);
|
||||
while (theBlock && (blockId != theBlock->Id())) theBlock = theBlock->next;
|
||||
while (theBlock && (blockId != theBlock->Id()))
|
||||
theBlock = theBlock->next;
|
||||
return theBlock;
|
||||
}
|
||||
else
|
||||
|
|
@ -318,56 +625,49 @@ bool NormBlockBuffer::CanInsert(NormBlockId blockId) const
|
|||
|
||||
bool NormBlockBuffer::Insert(NormBlock* theBlock)
|
||||
{
|
||||
if (range < range_max)
|
||||
const NormBlockId& blockId = theBlock->Id();
|
||||
if (!range)
|
||||
{
|
||||
const NormBlockId& blockId = theBlock->Id();
|
||||
if (!range)
|
||||
{
|
||||
range_lo = range_hi = blockId;
|
||||
range = 1;
|
||||
}
|
||||
if (blockId < range_lo)
|
||||
{
|
||||
UINT32 newRange = range_lo - blockId + range;
|
||||
if (newRange > range_max) return false;
|
||||
range_lo = blockId;
|
||||
range = newRange;
|
||||
}
|
||||
else if (blockId > range_hi)
|
||||
{
|
||||
UINT32 newRange = blockId - range_hi + range;
|
||||
if (newRange > range_max) return false;
|
||||
range_hi = blockId;
|
||||
range = newRange;
|
||||
}
|
||||
UINT32 index = ((UINT32)blockId) & hash_mask;
|
||||
NormBlock* prev = NULL;
|
||||
NormBlock* entry = table[index];
|
||||
while (entry && (entry->Id() < blockId))
|
||||
{
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
if (prev)
|
||||
prev->next = theBlock;
|
||||
else
|
||||
table[index] = theBlock;
|
||||
ASSERT((entry ? (blockId != entry->Id()) : true));
|
||||
theBlock->next = entry;
|
||||
return true;
|
||||
range_lo = range_hi = blockId;
|
||||
range = 1;
|
||||
}
|
||||
if (blockId < range_lo)
|
||||
{
|
||||
UINT32 newRange = range_lo - blockId + range;
|
||||
if (newRange > range_max) return false;
|
||||
range_lo = blockId;
|
||||
range = newRange;
|
||||
}
|
||||
else if (blockId > range_hi)
|
||||
{
|
||||
UINT32 newRange = blockId - range_hi + range;
|
||||
if (newRange > range_max) return false;
|
||||
range_hi = blockId;
|
||||
range = newRange;
|
||||
}
|
||||
UINT32 index = ((UINT32)blockId) & hash_mask;
|
||||
NormBlock* prev = NULL;
|
||||
NormBlock* entry = table[index];
|
||||
while (entry && (entry->Id() < blockId))
|
||||
{
|
||||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
if (prev)
|
||||
prev->next = theBlock;
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
table[index] = theBlock;
|
||||
ASSERT((entry ? (blockId != entry->Id()) : true));
|
||||
theBlock->next = entry;
|
||||
return true;
|
||||
} // end NormBlockBuffer::Insert()
|
||||
|
||||
bool NormBlockBuffer::Remove(const NormBlock* theBlock)
|
||||
{
|
||||
ASSERT(theBlock);
|
||||
const NormBlockId& blockId = theBlock->Id();
|
||||
if (range)
|
||||
{
|
||||
const NormBlockId& blockId = theBlock->Id();
|
||||
if ((blockId < range_lo) || (blockId > range_hi)) return false;
|
||||
UINT32 index = ((UINT32)blockId) & hash_mask;
|
||||
NormBlock* prev = NULL;
|
||||
|
|
@ -377,11 +677,12 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
|
|||
prev = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
if (entry != theBlock) return false;
|
||||
if (!entry) return false;
|
||||
if (prev)
|
||||
prev->next = entry->next;
|
||||
else
|
||||
table[index] = (NormBlock*)NULL;
|
||||
table[index] = entry->next;
|
||||
|
||||
if (range > 1)
|
||||
{
|
||||
if (blockId == range_lo)
|
||||
|
|
@ -390,7 +691,7 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
|
|||
UINT32 i = index;
|
||||
UINT32 endex;
|
||||
if (range <= hash_mask)
|
||||
endex = (index + range) & hash_mask;
|
||||
endex = (index + range - 1) & hash_mask;
|
||||
else
|
||||
endex = index;
|
||||
entry = NULL;
|
||||
|
|
@ -405,8 +706,8 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
|
|||
NormBlockId id = (UINT32)index + offset;
|
||||
while(entry && (entry->Id() != id))
|
||||
{
|
||||
if ((entry->Id() > blockId) && (entry->Id() < nextId))
|
||||
nextId = entry->Id();
|
||||
if ((entry->Id() > blockId) &&
|
||||
(entry->Id() < nextId)) nextId = entry->Id();
|
||||
entry = entry->next;
|
||||
|
||||
}
|
||||
|
|
@ -414,19 +715,10 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
|
|||
}
|
||||
} while (i != endex);
|
||||
if (entry)
|
||||
{
|
||||
range_lo = entry->Id();
|
||||
range = range_hi - range_lo + 1;
|
||||
}
|
||||
else if (nextId != range_hi)
|
||||
{
|
||||
range_lo = nextId;
|
||||
range = range_hi - range_lo + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
range = 0;
|
||||
}
|
||||
range_lo = nextId;
|
||||
range = range_hi - range_lo + 1;
|
||||
}
|
||||
else if (blockId == range_hi)
|
||||
{
|
||||
|
|
@ -434,7 +726,7 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
|
|||
UINT32 i = index;
|
||||
UINT32 endex;
|
||||
if (range <= hash_mask)
|
||||
endex = (index - range) & hash_mask;
|
||||
endex = (index - range + 1) & hash_mask;
|
||||
else
|
||||
endex = index;
|
||||
entry = NULL;
|
||||
|
|
@ -451,27 +743,18 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
|
|||
//printf("Looking for id:%lu at index:%lu\n", (UINT32)id, i);
|
||||
while(entry && (entry->Id() != id))
|
||||
{
|
||||
if ((entry->Id() < blockId) && (entry->Id() > prevId))
|
||||
prevId = entry->Id();
|
||||
if ((entry->Id() < blockId) &&
|
||||
(entry->Id() > prevId)) prevId = entry->Id();
|
||||
entry = entry->next;
|
||||
}
|
||||
if (entry) break;
|
||||
}
|
||||
} while (i != endex);
|
||||
if (entry)
|
||||
{
|
||||
range_hi = entry->Id();
|
||||
|
||||
}
|
||||
else if (prevId != range_lo)
|
||||
{
|
||||
range_hi = prevId;
|
||||
range = range_hi - range_lo + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
range = 0;
|
||||
}
|
||||
range_hi = prevId;
|
||||
range = range_hi - range_lo + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -510,13 +793,13 @@ NormBlock* NormBlockBuffer::Iterator::GetNextBlock()
|
|||
{
|
||||
if (buffer.range &&
|
||||
(index < buffer.range_hi) &&
|
||||
!(index < buffer.range_lo))
|
||||
(index >= buffer.range_lo))
|
||||
{
|
||||
// Find next entry _after_ current "index"
|
||||
UINT32 i = index;
|
||||
UINT32 endex = buffer.range_hi - index;
|
||||
if (endex <= buffer.hash_mask)
|
||||
endex = (index + endex) & buffer.hash_mask;
|
||||
UINT32 endex;
|
||||
if ((UINT32)(buffer.range_hi - index) <= buffer.hash_mask)
|
||||
endex = buffer.range_hi & buffer.hash_mask;
|
||||
else
|
||||
endex = index;
|
||||
UINT32 offset = 0;
|
||||
|
|
@ -526,8 +809,9 @@ NormBlock* NormBlockBuffer::Iterator::GetNextBlock()
|
|||
++i &= buffer.hash_mask;
|
||||
offset++;
|
||||
NormBlockId id = (UINT32)index + offset;
|
||||
ASSERT(i < 256);
|
||||
NormBlock* entry = buffer.table[i];
|
||||
while ((NULL != entry )& (entry->Id() != id))
|
||||
while ((NULL != entry ) && (entry->Id() != id))
|
||||
{
|
||||
if ((entry->Id() > index) && (entry->Id() < nextId))
|
||||
nextId = entry->Id();
|
||||
|
|
|
|||
|
|
@ -26,14 +26,18 @@ class NormSegmentPool
|
|||
}
|
||||
bool IsEmpty() {return (NULL == seg_list);}
|
||||
|
||||
unsigned long CurrentUsage() {return (seg_total - seg_count);}
|
||||
unsigned long PeakUsage() {return peak_usage;}
|
||||
unsigned long OverunCount() {return overruns;}
|
||||
|
||||
private:
|
||||
unsigned int seg_size;
|
||||
unsigned int seg_count;
|
||||
unsigned int seg_total;
|
||||
char* seg_list;
|
||||
|
||||
unsigned int peak_usage;
|
||||
unsigned int overruns;
|
||||
unsigned long peak_usage;
|
||||
unsigned long overruns;
|
||||
bool overrun_flag;
|
||||
}; // end class NormSegmentPool
|
||||
|
||||
|
|
@ -45,8 +49,7 @@ class NormBlock
|
|||
public:
|
||||
enum Flag
|
||||
{
|
||||
PARITY_READY = 0x01,
|
||||
IN_REPAIR = 0x02
|
||||
IN_REPAIR = 0x01
|
||||
};
|
||||
|
||||
NormBlock();
|
||||
|
|
@ -57,8 +60,11 @@ class NormBlock
|
|||
void Destroy();
|
||||
|
||||
void SetFlag(NormBlock::Flag flag) {flags |= flag;}
|
||||
bool ParityReady() {return (0 != (flags & PARITY_READY));}
|
||||
bool InRepair() {return (0 != (flags & IN_REPAIR));}
|
||||
bool ParityReady(UINT16 ndata) {return (erasure_count == ndata);}
|
||||
UINT16 ParityReadiness() {return erasure_count;}
|
||||
void IncreaseParityReadiness() {erasure_count++;}
|
||||
void SetParityReadiness(UINT16 ndata) {erasure_count = ndata;}
|
||||
|
||||
char** SegmentList(UINT16 index = 0) {return &segment_table[index];}
|
||||
char* Segment(NormSegmentId sid)
|
||||
|
|
@ -71,7 +77,6 @@ class NormBlock
|
|||
ASSERT(sid < size);
|
||||
ASSERT(!segment_table[sid]);
|
||||
segment_table[sid] = segment;
|
||||
erasure_count--;
|
||||
}
|
||||
char* DetachSegment(NormSegmentId sid)
|
||||
{
|
||||
|
|
@ -87,25 +92,62 @@ class NormBlock
|
|||
segment_table[sid] = segment;
|
||||
}
|
||||
|
||||
// Server routines
|
||||
void TxInit(NormBlockId& blockId, UINT16 ndata, UINT16 autoParity)
|
||||
{
|
||||
id = blockId;
|
||||
pending_mask.Clear();
|
||||
pending_mask.SetBits(0, ndata+autoParity);
|
||||
repair_mask.Clear();
|
||||
erasure_count = size - ndata;
|
||||
erasure_count = 0;
|
||||
parity_count = 0;
|
||||
parity_offset = autoParity;
|
||||
flags = 0;
|
||||
}
|
||||
void RxInit(NormBlockId& blockId, UINT16 ndata)
|
||||
void TxRecover(NormBlockId& blockId, UINT16 ndata, UINT16 nparity)
|
||||
{
|
||||
id = blockId;
|
||||
pending_mask.Reset();
|
||||
pending_mask.Clear();
|
||||
repair_mask.Clear();
|
||||
erasure_count = size;
|
||||
erasure_count = 0;
|
||||
parity_count = nparity; // force recovered blocks to
|
||||
parity_offset = nparity; // explicit repair mode ???
|
||||
flags = IN_REPAIR;
|
||||
}
|
||||
bool TxReset(UINT16 ndata, UINT16 nparity, UINT16 autoParity,
|
||||
UINT16 segmentSize);
|
||||
bool TxUpdate(NormSegmentId nextId, NormSegmentId lastId,
|
||||
UINT16 ndata, UINT16 nparity,
|
||||
UINT16 segmentSize, UINT16 erasureCount);
|
||||
|
||||
bool HandleSegmentRequest(NormSegmentId nextId, NormSegmentId lastId,
|
||||
UINT16 ndata, UINT16 nparity,
|
||||
UINT16 erasureCount);
|
||||
bool ActivateRepairs(UINT16 nparity);
|
||||
void ResetParityCount(UINT16 nparity)
|
||||
{
|
||||
parity_offset += parity_count;
|
||||
parity_offset = MIN(parity_offset, nparity);
|
||||
parity_count = 0;
|
||||
}
|
||||
|
||||
// Client routines
|
||||
void RxInit(NormBlockId& blockId, UINT16 ndata, UINT16 nparity)
|
||||
{
|
||||
id = blockId;
|
||||
pending_mask.Clear();
|
||||
pending_mask.SetBits(0, ndata+nparity);
|
||||
repair_mask.Clear();
|
||||
erasure_count = ndata;
|
||||
parity_count = 0;
|
||||
parity_offset = 0;
|
||||
flags = 0;
|
||||
}
|
||||
bool IsRepairPending(UINT16 ndata, UINT16 nparity);
|
||||
void DecrementErasureCount() {erasure_count--;}
|
||||
UINT16 ErasureCount() const {return erasure_count;}
|
||||
void IncrementParityCount() {parity_count++;}
|
||||
UINT16 ParityCount() {return parity_count;}
|
||||
|
||||
NormSymbolId FirstPending() const
|
||||
{return pending_mask.FirstSet();}
|
||||
|
|
@ -113,12 +155,21 @@ class NormBlock
|
|||
{return repair_mask.FirstSet();}
|
||||
bool SetPending(NormSymbolId s)
|
||||
{return pending_mask.Set(s);}
|
||||
bool SetPending(NormSymbolId firstId, UINT16 count)
|
||||
{return pending_mask.SetBits(firstId, count);}
|
||||
void UnsetPending(NormSymbolId s)
|
||||
{pending_mask.Unset(s);}
|
||||
void ClearPending()
|
||||
{pending_mask.Clear();}
|
||||
bool SetRepair(NormSymbolId s)
|
||||
{return repair_mask.Set(s);}
|
||||
bool SetRepairs(NormSymbolId first, NormSymbolId last)
|
||||
{
|
||||
if (first == last)
|
||||
return repair_mask.Set(first);
|
||||
else
|
||||
return (repair_mask.SetBits(first, last-first+1));
|
||||
}
|
||||
void UnsetRepair(NormSymbolId s)
|
||||
{repair_mask.Unset(s);}
|
||||
void ClearRepairs()
|
||||
|
|
@ -127,11 +178,22 @@ class NormBlock
|
|||
{return pending_mask.Test(s);}
|
||||
bool IsPending() const
|
||||
{return pending_mask.IsSet();}
|
||||
bool IsRepairPending(NormSegmentId end);
|
||||
bool IsRepairPending() const
|
||||
{return repair_mask.IsSet();}
|
||||
bool IsTransmitPending() const
|
||||
{return (pending_mask.IsSet() || repair_mask.IsSet());}
|
||||
|
||||
|
||||
NormSymbolId NextPending(NormSymbolId index) const
|
||||
{return pending_mask.NextSet(index);}
|
||||
UINT16 ErasureCount() const {return erasure_count;}
|
||||
|
||||
|
||||
bool AppendRepairRequest(NormNackMsg& nack,
|
||||
UINT16 ndata,
|
||||
UINT16 nparity,
|
||||
NormObjectId objectId,
|
||||
bool pendingInfo,
|
||||
UINT16 segmentSize);
|
||||
//void DisplayPendingMask(FILE* f) {pending_mask.Display(f);}
|
||||
|
||||
bool IsEmpty();
|
||||
|
|
@ -145,6 +207,7 @@ class NormBlock
|
|||
int flags;
|
||||
UINT16 erasure_count;
|
||||
UINT16 parity_count;
|
||||
UINT16 parity_offset;
|
||||
|
||||
NormBitmask pending_mask;
|
||||
NormBitmask repair_mask;
|
||||
|
|
@ -164,6 +227,15 @@ class NormBlockPool
|
|||
{
|
||||
NormBlock* b = head;
|
||||
head = b ? b->next : NULL;
|
||||
if (b)
|
||||
{
|
||||
overrun_flag = false;
|
||||
}
|
||||
else if (!overrun_flag)
|
||||
{
|
||||
overruns++;
|
||||
overrun_flag = true;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
void Put(NormBlock* b)
|
||||
|
|
@ -171,16 +243,20 @@ class NormBlockPool
|
|||
b->next = head;
|
||||
head = b;
|
||||
}
|
||||
unsigned long OverrunCount() {return overruns;}
|
||||
|
||||
private:
|
||||
NormBlock* head;
|
||||
unsigned long overruns;
|
||||
bool overrun_flag;
|
||||
}; // end class NormBlockPool
|
||||
|
||||
class NormBlockBuffer
|
||||
{
|
||||
friend class NormBlockBuffer::Iterator;
|
||||
|
||||
public:
|
||||
class Iterator;
|
||||
friend class NormBlockBuffer::Iterator;
|
||||
|
||||
NormBlockBuffer();
|
||||
~NormBlockBuffer();
|
||||
bool Init(unsigned long rangeMax, unsigned long tableSize = 256);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -17,7 +17,9 @@ class NormController
|
|||
{
|
||||
TX_QUEUE_EMPTY,
|
||||
RX_OBJECT_NEW,
|
||||
RX_OBJECT_UPDATE
|
||||
RX_OBJECT_INFO,
|
||||
RX_OBJECT_UPDATE,
|
||||
RX_OBJECT_COMPLETE,
|
||||
};
|
||||
|
||||
virtual void Notify(NormController::Event event,
|
||||
|
|
@ -51,6 +53,8 @@ class NormSessionMgr
|
|||
UINT16 sessionPort,
|
||||
NormNodeId localNodeId =
|
||||
NORM_NODE_ANY);
|
||||
void DeleteSession(class NormSession* theSession);
|
||||
|
||||
|
||||
UdpSocketInstallFunc* SocketInstaller() {return socket_installer;}
|
||||
const void* SocketInstallData() {return socket_install_data;}
|
||||
|
|
@ -64,16 +68,15 @@ class NormSessionMgr
|
|||
controller->Notify(event, this, session, server, object);
|
||||
}
|
||||
|
||||
private:
|
||||
void InstallTimer(ProtocolTimer* timer)
|
||||
{timer_mgr.InstallTimer(timer);}
|
||||
void InstallTimer(ProtocolTimer* timer) {timer_mgr.InstallTimer(timer);}
|
||||
|
||||
private:
|
||||
ProtocolTimerMgr timer_mgr;
|
||||
UdpSocketInstallFunc* socket_installer;
|
||||
const void* socket_install_data;
|
||||
NormController* controller;
|
||||
|
||||
class NormSession* top_session;
|
||||
class NormSession* top_session; // top of NormSession list
|
||||
|
||||
}; // end class NormSessionMgr
|
||||
|
||||
|
|
@ -85,33 +88,62 @@ class NormSession
|
|||
public:
|
||||
enum {DEFAULT_MESSAGE_POOL_DEPTH = 16};
|
||||
static const double DEFAULT_TRANSMIT_RATE; // in bytes per second
|
||||
|
||||
NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId);
|
||||
~NormSession();
|
||||
static const double DEFAULT_PROBE_MIN;
|
||||
static const double DEFAULT_PROBE_MAX;
|
||||
static const double DEFAULT_GRTT_ESTIMATE;
|
||||
static const double DEFAULT_GRTT_MAX;
|
||||
static const unsigned int DEFAULT_GRTT_DECREASE_DELAY;
|
||||
static const double DEFAULT_BACKOFF_FACTOR; // times GRTT = backoff max
|
||||
static const double DEFAULT_GSIZE_ESTIMATE;
|
||||
|
||||
// General methods
|
||||
const NormNodeId& LocalNodeId() {return local_node_id;}
|
||||
bool Open();
|
||||
void Close();
|
||||
bool IsOpen() {return (rx_socket.IsOpen() || tx_socket.IsOpen());}
|
||||
|
||||
const NetworkAddress& Address() {return address;}
|
||||
void SetAddress(const NetworkAddress& addr) {address = addr;}
|
||||
|
||||
|
||||
// Session parameters
|
||||
double TxRate() {return (tx_rate * 8.0);}
|
||||
void SetTxRate(double txRate) {tx_rate = txRate / 8.0;}
|
||||
double BackoffFactor() {return backoff_factor;}
|
||||
void SetBackoffFactor(double value) {backoff_factor = value;}
|
||||
void SetUnicastNacks(bool state) {unicast_nacks = state;}
|
||||
bool UnicastNacks() {return unicast_nacks;}
|
||||
void SetLoopback(bool state)
|
||||
{rx_socket.SetLoopback(state);
|
||||
tx_socket.SetLoopback(state);}
|
||||
{
|
||||
rx_socket.SetLoopback(state);
|
||||
tx_socket.SetLoopback(state);
|
||||
}
|
||||
|
||||
void Notify(NormController::Event event,
|
||||
class NormServerNode* server,
|
||||
class NormObject* object)
|
||||
{
|
||||
notify_pending = true;
|
||||
session_mgr.Notify(event, this, server, object);
|
||||
notify_pending = false;
|
||||
}
|
||||
|
||||
NormMessage* GetMessageFromPool() {return message_pool.RemoveHead();}
|
||||
void ReturnMessageToPool(NormMessage* msg) {message_pool.Append(msg);}
|
||||
void QueueMessage(NormMessage* msg);
|
||||
|
||||
// Server methods
|
||||
bool StartServer(unsigned long bufferSpace,
|
||||
UINT16 segmentSize,
|
||||
UINT16 numData,
|
||||
UINT16 numParity);
|
||||
void StopServer();
|
||||
NormStreamObject* QueueTxStream(UINT32 bufferSize,
|
||||
const char* infoPtr = NULL,
|
||||
UINT16 infoLen = 0);
|
||||
NormFileObject* QueueTxFile(const char* path,
|
||||
const char* infoPtr = NULL,
|
||||
UINT16 infoLen = 0);
|
||||
|
||||
bool IsServer() {return is_server;}
|
||||
UINT16 ServerSegmentSize() {return segment_size;}
|
||||
UINT16 ServerBlockSize() {return ndata;}
|
||||
|
|
@ -120,23 +152,30 @@ class NormSession
|
|||
void ServerSetAutoParity(UINT16 autoParity)
|
||||
{ASSERT(autoParity <= nparity); auto_parity = autoParity;}
|
||||
|
||||
double ServerGroupSize() {return gsize_measured;}
|
||||
void ServerSetGroupSize(double gsize)
|
||||
{
|
||||
gsize_measured = gsize;
|
||||
gsize_quantized = NormQuantizeGroupSize(gsize);
|
||||
gsize_advertised = NormUnquantizeGroupSize(gsize_quantized);
|
||||
}
|
||||
|
||||
void ServerEncode(const char* segment, char** parityVectorList)
|
||||
{encoder.Encode(segment, parityVectorList);}
|
||||
|
||||
NormStreamObject* QueueTxStream(UINT32 bufferSize,
|
||||
const char* infoPtr = NULL,
|
||||
UINT16 infoLen = 0);
|
||||
|
||||
NormBlock* ServerGetFreeBlock(NormObjectId objectId, NormBlockId blockId);
|
||||
char* ServerGetFreeSegment(NormObjectId objectId, NormBlockId blockId);
|
||||
void ServerPutFreeBlock(NormBlock* block)
|
||||
{
|
||||
block->EmptyToPool(segment_pool);
|
||||
block_pool.Put(block);
|
||||
}
|
||||
char* ServerGetFreeSegment(NormObjectId objectId, NormBlockId blockId);
|
||||
void ServerPutFreeSegment(char* segment) {segment_pool.Put(segment);}
|
||||
void TouchServer()
|
||||
{
|
||||
posted_tx_queue_empty = false;
|
||||
Serve();
|
||||
if (!notify_pending) Serve();
|
||||
}
|
||||
|
||||
// Client methods
|
||||
|
|
@ -145,39 +184,69 @@ class NormSession
|
|||
bool IsClient() {return is_client;}
|
||||
unsigned long RemoteServerBufferSize()
|
||||
{return remote_server_buffer_size;}
|
||||
|
||||
private:
|
||||
void QueueMessage(NormMessage* msg);
|
||||
void Serve();
|
||||
bool QueueTxObject(NormObject* obj, bool touchServer = true);
|
||||
|
||||
void InstallTimer(ProtocolTimer* timer)
|
||||
{session_mgr.InstallTimer(timer);}
|
||||
|
||||
// Debug settings
|
||||
void SetTrace(bool state) {trace = state;}
|
||||
void SetTxLoss(double percent) {tx_loss_rate = percent;}
|
||||
void SetRxLoss(double percent) {rx_loss_rate = percent;}
|
||||
|
||||
#ifdef SIMULATE
|
||||
// Simulation specific methods
|
||||
NormSimObject* QueueTxSim(unsigned long objectSize);
|
||||
bool SimSocketRecvHandler(char* buffer, unsigned short buflen,
|
||||
const NetworkAddress& src, bool unicast);
|
||||
#endif // SIMULATE
|
||||
|
||||
private:
|
||||
// Only NormSessionMgr can create/delete sessions
|
||||
NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId);
|
||||
~NormSession();
|
||||
|
||||
void Serve();
|
||||
bool QueueTxObject(NormObject* obj, bool touchServer = true);
|
||||
void DeleteTxObject(NormObject* obj);
|
||||
|
||||
bool OnTxTimeout();
|
||||
bool OnRepairTimeout();
|
||||
bool OnCommandTimeout();
|
||||
bool OnProbeTimeout();
|
||||
bool OnReportTimeout();
|
||||
|
||||
bool TxSocketRecvHandler(UdpSocket* theSocket);
|
||||
bool RxSocketRecvHandler(UdpSocket* theSocket);
|
||||
|
||||
void HandleReceiveMessage(NormMessage& msg, bool wasUnicast);
|
||||
|
||||
void ServerHandleNackMessage(NormNackMsg& nack);
|
||||
void ServerUpdateGrttEstimate(const struct timeval& grttResponse);
|
||||
bool ServerQueueSquelch(NormObjectId objectId);
|
||||
void ServerQueueFlush();
|
||||
void ServerUpdateGroupSize();
|
||||
|
||||
void ClientHandleObjectMessage(NormMessage& msg);
|
||||
void ClientHandleCommand(NormMessage& msg);
|
||||
void ClientHandleNackMessage(NormNackMsg& nack);
|
||||
|
||||
NormSessionMgr& session_mgr;
|
||||
bool notify_pending;
|
||||
ProtocolTimer tx_timer;
|
||||
UdpSocket tx_socket;
|
||||
UdpSocket rx_socket;
|
||||
NormMessageQueue message_queue;
|
||||
NormMessageQueue message_pool;
|
||||
ProtocolTimer report_timer;
|
||||
UINT16 tx_sequence;
|
||||
|
||||
// General session parameters
|
||||
NormNodeId local_node_id;
|
||||
NetworkAddress address; // session destination address
|
||||
UINT8 ttl; // session multicast ttl
|
||||
|
||||
UINT16 tx_sequence;
|
||||
double tx_rate; // bytes per second
|
||||
double backoff_factor;
|
||||
|
||||
// Server parameters
|
||||
// Server parameters and state
|
||||
bool is_server;
|
||||
UINT16 segment_size;
|
||||
UINT16 ndata;
|
||||
|
|
@ -187,20 +256,51 @@ class NormSession
|
|||
NormObjectTable tx_table;
|
||||
NormSlidingMask tx_pending_mask;
|
||||
NormSlidingMask tx_repair_mask;
|
||||
ProtocolTimer repair_timer;
|
||||
NormBlockPool block_pool;
|
||||
NormSegmentPool segment_pool;
|
||||
NormEncoder encoder;
|
||||
|
||||
NormObjectId current_tx_object_id;
|
||||
NormObjectId next_tx_object_id;
|
||||
unsigned int tx_cache_count_min;
|
||||
unsigned int tx_cache_count_max;
|
||||
NormObjectSize tx_cache_size_max;
|
||||
ProtocolTimer flush_timer;
|
||||
int flush_count;
|
||||
bool posted_tx_queue_empty;
|
||||
|
||||
ProtocolTimer probe_timer; // GRTT/congestion control probes
|
||||
double probe_interval;
|
||||
double probe_interval_min;
|
||||
double probe_interval_max;
|
||||
|
||||
double grtt_max;
|
||||
unsigned int grtt_decrease_delay_count;
|
||||
bool grtt_response;
|
||||
double grtt_current_peak;
|
||||
double grtt_measured;
|
||||
double grtt_advertised;
|
||||
UINT8 grtt_quantized;
|
||||
double gsize_measured;
|
||||
double gsize_advertised;
|
||||
UINT8 gsize_quantized;
|
||||
unsigned int gsize_nack_count;
|
||||
double gsize_nack_ave;
|
||||
double gsize_correction_factor;
|
||||
double gsize_nack_delta;
|
||||
|
||||
// Client parameters
|
||||
bool is_client;
|
||||
NormNodeTree server_tree;
|
||||
unsigned long remote_server_buffer_size;
|
||||
bool unicast_nacks;
|
||||
|
||||
// Misc
|
||||
// Protocol test/debug parameters
|
||||
bool trace;
|
||||
double tx_loss_rate; // for correlated loss
|
||||
double rx_loss_rate; // for uncorrelated loss
|
||||
|
||||
// Linkers
|
||||
NormSession* next;
|
||||
}; // end class NormSession
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,565 @@
|
|||
#include "normSimAgent.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
NormSimAgent::NormSimAgent()
|
||||
: session(NULL), stream(NULL),
|
||||
address(NULL), port(0), ttl(3),
|
||||
tx_rate(NormSession::DEFAULT_TRANSMIT_RATE),
|
||||
backoff_factor(NormSession::DEFAULT_BACKOFF_FACTOR),
|
||||
segment_size(1024), ndata(32), nparity(16), auto_parity(0),
|
||||
group_size(NormSession::DEFAULT_GSIZE_ESTIMATE),
|
||||
tx_buffer_size(1024*1024), rx_buffer_size(1024*1024),
|
||||
tx_object_size(0), tx_object_interval(0.0),
|
||||
tx_repeat_count(0), tx_repeat_interval(0.0),
|
||||
tracing(false), tx_loss(0.0), rx_loss(0.0)
|
||||
{
|
||||
// Bind NormSessionMgr to this agent and simulation environment
|
||||
session_mgr.Init(ProtoSimAgent::TimerInstaller, this,
|
||||
ProtoSimAgent::SocketInstaller, this,
|
||||
static_cast<NormController*>(this));
|
||||
|
||||
interval_timer.Init(0.0, 0, (ProtocolTimerOwner*)this,
|
||||
(ProtocolTimeoutFunc)&NormSimAgent::OnIntervalTimeout);
|
||||
}
|
||||
|
||||
NormSimAgent::~NormSimAgent()
|
||||
{
|
||||
if (address) delete address;
|
||||
}
|
||||
|
||||
|
||||
const char* const NormSimAgent::cmd_list[] =
|
||||
{
|
||||
"+debug",
|
||||
"+log",
|
||||
"-trace",
|
||||
"+txloss",
|
||||
"+rxloss",
|
||||
"+address",
|
||||
"+ttl",
|
||||
"+rate",
|
||||
"+backoff",
|
||||
"+input",
|
||||
"+output",
|
||||
"+interval",
|
||||
"+repeat",
|
||||
"+rinterval", // repeat interval
|
||||
"+segment",
|
||||
"+block",
|
||||
"+parity",
|
||||
"+auto",
|
||||
"+gsize",
|
||||
"+txbuffer",
|
||||
"+rxbuffer",
|
||||
"+start",
|
||||
"-stop",
|
||||
"+sendFile",
|
||||
NULL
|
||||
};
|
||||
|
||||
bool NormSimAgent::ProcessCommand(const char* cmd, const char* val)
|
||||
{
|
||||
CmdType type = CommandType(cmd);
|
||||
ASSERT(CMD_INVALID != type);
|
||||
unsigned int len = strlen(cmd);
|
||||
if ((CMD_ARG == type) && !val)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(%s) missing argument\n", cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strncmp("debug", cmd, len))
|
||||
{
|
||||
|
||||
int debugLevel = atoi(val);
|
||||
if ((debugLevel < 0) || (debugLevel > 12))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(segment) invalid debug level!\n");
|
||||
return false;
|
||||
}
|
||||
SetDebugLevel(debugLevel);
|
||||
}
|
||||
else if (!strncmp("log", cmd, len))
|
||||
{
|
||||
OpenDebugLog(val);
|
||||
}
|
||||
else if (!strncmp("trace", cmd, len))
|
||||
{
|
||||
tracing = true;
|
||||
if (session) session->SetTrace(true);
|
||||
}
|
||||
else if (!strncmp("txloss", cmd, len))
|
||||
{
|
||||
double txLoss = atof(val);
|
||||
if (txLoss < 0)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(txloss) invalid txRate!\n");
|
||||
return false;
|
||||
}
|
||||
tx_loss = txLoss;
|
||||
if (session) session->SetTxLoss(txLoss);
|
||||
}
|
||||
else if (!strncmp("rxloss", cmd, len))
|
||||
{
|
||||
double rxLoss = atof(val);
|
||||
if (rxLoss < 0)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(rxloss) invalid txRate!\n");
|
||||
return false;
|
||||
}
|
||||
rx_loss = rxLoss;
|
||||
if (session) session->SetRxLoss(rxLoss);
|
||||
}
|
||||
else if (!strncmp("address", cmd, len))
|
||||
{
|
||||
unsigned int len = strlen(val);
|
||||
if (address) delete address;
|
||||
if (!(address = new char[len+1]))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(address) allocation error:%s\n",
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
strcpy(address, val);
|
||||
char* ptr = strchr(address, '/');
|
||||
if (!ptr)
|
||||
{
|
||||
delete address;
|
||||
address = NULL;
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(address) missing port number!\n");
|
||||
return false;
|
||||
}
|
||||
*ptr++ = '\0';
|
||||
int portNum = atoi(ptr);
|
||||
if ((portNum < 1) || (portNum > 65535))
|
||||
{
|
||||
delete address;
|
||||
address = NULL;
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(address) invalid port number!\n");
|
||||
return false;
|
||||
}
|
||||
port = portNum;
|
||||
}
|
||||
else if (!strncmp("ttl", cmd, len))
|
||||
{
|
||||
int ttlTemp = atoi(val);
|
||||
if ((ttlTemp < 1) || (ttlTemp > 255))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(ttl) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
ttl = ttlTemp;
|
||||
}
|
||||
else if (!strncmp("rate", cmd, len))
|
||||
{
|
||||
double txRate = atof(val);
|
||||
if (txRate < 0)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(rate) invalid txRate!\n");
|
||||
return false;
|
||||
}
|
||||
tx_rate = txRate;
|
||||
if (session) session->SetTxRate(txRate);
|
||||
}
|
||||
else if (!strncmp("backoff", cmd, len))
|
||||
{
|
||||
double backoffFactor = atof(val);
|
||||
if (backoffFactor < 0)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(backoff) invalid txRate!\n");
|
||||
return false;
|
||||
}
|
||||
backoff_factor = backoffFactor;
|
||||
if (session) session->SetTxRate(backoffFactor);
|
||||
}
|
||||
else if (!strncmp("interval", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lf", &tx_object_interval))
|
||||
tx_object_interval = -1.0;
|
||||
if (tx_object_interval < 0.0)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(interval) Invalid tx object interval: %s\n",
|
||||
val);
|
||||
tx_object_interval = 0.0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("repeat", cmd, len))
|
||||
{
|
||||
tx_repeat_count = atoi(val);
|
||||
}
|
||||
else if (!strncmp("segment", cmd, len))
|
||||
{
|
||||
int segmentSize = atoi(val);
|
||||
if ((segmentSize < 0) || (segmentSize > 8000))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(segment) invalid segment size!\n");
|
||||
return false;
|
||||
}
|
||||
segment_size = segmentSize;
|
||||
}
|
||||
else if (!strncmp("block", cmd, len))
|
||||
{
|
||||
int blockSize = atoi(val);
|
||||
if ((blockSize < 1) || (blockSize > 255))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(block) invalid block size!\n");
|
||||
return false;
|
||||
}
|
||||
ndata = blockSize;
|
||||
}
|
||||
else if (!strncmp("parity", cmd, len))
|
||||
{
|
||||
int numParity = atoi(val);
|
||||
if ((numParity < 0) || (numParity > 254))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(parity) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
nparity = numParity;
|
||||
}
|
||||
else if (!strncmp("auto", cmd, len))
|
||||
{
|
||||
int autoParity = atoi(val);
|
||||
if ((autoParity < 0) || (autoParity > 254))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(auto) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
auto_parity = autoParity;
|
||||
if (session) session->ServerSetAutoParity(autoParity);
|
||||
}
|
||||
else if (!strncmp("gsize", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lf", &group_size))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(gize) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
if (session) session->ServerSetGroupSize(group_size);
|
||||
}
|
||||
else if (!strncmp("txbuffer", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lu", &tx_buffer_size))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(txbuffer) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("rxbuffer", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lu", &rx_buffer_size))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(rxbuffer) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("start", cmd, len))
|
||||
{
|
||||
if (!strcmp("server", val))
|
||||
{
|
||||
return StartServer();
|
||||
}
|
||||
else if (!strcmp("client", val))
|
||||
{
|
||||
return StartClient();
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(start) invalid value!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp("stop", cmd, len))
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
else if (!strncmp("sendFile", cmd, len))
|
||||
{
|
||||
if (1 != sscanf(val, "%lu", &tx_object_size))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(sendFile) invalid size!\n");
|
||||
return false;
|
||||
}
|
||||
if (session)
|
||||
{
|
||||
return (NULL != session->QueueTxSim(tx_object_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormSimAgent::ProcessCommand(sendFile) no session started!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} // end NormSimAgent::ProcessCommand()
|
||||
|
||||
|
||||
NormSimAgent::CmdType NormSimAgent::CommandType(const char* cmd)
|
||||
{
|
||||
if (!cmd) return CMD_INVALID;
|
||||
unsigned int len = strlen(cmd);
|
||||
bool matched = false;
|
||||
CmdType type = CMD_INVALID;
|
||||
const char* const* nextCmd = cmd_list;
|
||||
while (*nextCmd)
|
||||
{
|
||||
if (!strncmp(cmd, *nextCmd+1, len))
|
||||
{
|
||||
if (matched)
|
||||
{
|
||||
// ambiguous command (command should match only once)
|
||||
return CMD_INVALID;
|
||||
}
|
||||
else
|
||||
{
|
||||
matched = true;
|
||||
if ('+' == *nextCmd[0])
|
||||
type = CMD_ARG;
|
||||
else
|
||||
type = CMD_NOARG;
|
||||
}
|
||||
}
|
||||
nextCmd++;
|
||||
}
|
||||
return type;
|
||||
} // end NormSimAgent::CommandType()
|
||||
|
||||
|
||||
void NormSimAgent::Notify(NormController::Event event,
|
||||
class NormSessionMgr* sessionMgr,
|
||||
class NormSession* session,
|
||||
class NormServerNode* server,
|
||||
class NormObject* object)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case TX_QUEUE_EMPTY:
|
||||
// Can queue a new object or write to stream for transmission
|
||||
if (interval_timer.Interval() > 0.0)
|
||||
{
|
||||
InstallTimer(interval_timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnIntervalTimeout();
|
||||
}
|
||||
break;
|
||||
|
||||
case RX_OBJECT_NEW:
|
||||
{
|
||||
//TRACE("NormSimAgent::Notify(RX_OBJECT_NEW) ...\n");
|
||||
// It's up to the app to "accept" the object
|
||||
switch (object->GetType())
|
||||
{
|
||||
case NormObject::STREAM:
|
||||
{
|
||||
const NormObjectSize& size = object->Size();
|
||||
if (!((NormStreamObject*)object)->Accept(size.LSB()))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) stream object accept error!\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NormObject::FILE:
|
||||
{
|
||||
if (!((NormSimObject*)object)->Accept())
|
||||
{
|
||||
DMSG(0, "NormSimAgent::Notify(RX_OBJECT_NEW) sim object accept error!\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NormObject::DATA:
|
||||
DMSG(0, "NormSimAgent::Notify() FILE/DATA objects not _yet_ supported...\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RX_OBJECT_INFO:
|
||||
//TRACE("NormSimAgent::Notify(RX_OBJECT_INFO) ...\n");
|
||||
switch(object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
case NormObject::DATA:
|
||||
case NormObject::STREAM:
|
||||
break;
|
||||
} // end switch(object->GetType())
|
||||
break;
|
||||
|
||||
case RX_OBJECT_UPDATE:
|
||||
//TRACE("NormSimAgent::Notify(RX_OBJECT_UPDATE) ...\n");
|
||||
switch (object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
// (TBD) update progress
|
||||
break;
|
||||
|
||||
case NormObject::STREAM:
|
||||
{
|
||||
// Read the stream when it's updated
|
||||
char buffer[256];
|
||||
unsigned int nBytes;
|
||||
while ((nBytes = ((NormStreamObject*)object)->Read(buffer, 256)))
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NormObject::DATA:
|
||||
DMSG(0, "NormSimAgent::Notify() FILE/DATA objects not _yet_ supported...\n");
|
||||
break;
|
||||
} // end switch (object->GetType())
|
||||
break;
|
||||
case RX_OBJECT_COMPLETE:
|
||||
{
|
||||
switch(object->GetType())
|
||||
{
|
||||
case NormObject::FILE:
|
||||
//DMSG(0, "norm: Completed rx file: %s\n", ((NormFileObject*)object)->Path());
|
||||
break;
|
||||
|
||||
case NormObject::STREAM:
|
||||
ASSERT(0);
|
||||
break;
|
||||
case NormObject::DATA:
|
||||
ASSERT(0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} // end switch(event)
|
||||
} // end NormSimAgent::Notify()
|
||||
|
||||
|
||||
bool NormSimAgent::OnIntervalTimeout()
|
||||
{
|
||||
if (tx_repeat_count)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue a NORM_OBJECT_SIM as long as there are repeats
|
||||
if (tx_repeat_count > 0) tx_repeat_count--;
|
||||
if (!session->QueueTxSim(tx_object_size))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::OnIntervalTimeout() Error queueing tx object.\n");
|
||||
}
|
||||
}
|
||||
interval_timer.SetInterval(tx_object_interval);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Done
|
||||
interval_timer.Deactivate();
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end NormSimAgent::OnIntervalTimeout()
|
||||
|
||||
|
||||
bool NormSimAgent::StartServer()
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::StartServer() Error! server or client already started!\n");
|
||||
return false;
|
||||
}
|
||||
// Validate our session settings
|
||||
if (!address)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::StartServer() Error! no session address given.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new session on multicast group/port
|
||||
session = session_mgr.NewSession(address, port, GetAgentId());
|
||||
if (session)
|
||||
{
|
||||
// Common session parameters
|
||||
session->SetTxRate(tx_rate);
|
||||
session->SetBackoffFactor(backoff_factor);
|
||||
session->SetTrace(tracing);
|
||||
session->SetTxLoss(tx_loss);
|
||||
session->SetRxLoss(rx_loss);
|
||||
|
||||
// StartServer(bufferMax, segmentSize, fec_ndata, fec_nparity)
|
||||
if (!session->StartServer(tx_buffer_size, segment_size, ndata, nparity))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::OnStartup() start server error!\n");
|
||||
session_mgr.Destroy();
|
||||
return false;
|
||||
}
|
||||
session->ServerSetAutoParity(auto_parity);
|
||||
session->ServerSetGroupSize(group_size);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormSimAgent::StartServer() new session error!\n");
|
||||
return false;
|
||||
}
|
||||
} // end NormSimAgent::StartServer()
|
||||
|
||||
|
||||
bool NormSimAgent::StartClient()
|
||||
{
|
||||
|
||||
if (session)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::StartClient() Error! server or client already started!\n");
|
||||
return false;
|
||||
}
|
||||
// Validate our session settings
|
||||
if (!address)
|
||||
{
|
||||
DMSG(0, "NormSimAgent::StartClient() Error! no session address given.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a new session on multicast group/port
|
||||
session = session_mgr.NewSession(address, port, GetAgentId());
|
||||
if (session)
|
||||
{
|
||||
// Common session parameters
|
||||
session->SetTxRate(tx_rate);
|
||||
session->SetBackoffFactor(backoff_factor);
|
||||
session->SetTrace(tracing);
|
||||
session->SetTxLoss(tx_loss);
|
||||
session->SetRxLoss(rx_loss);
|
||||
|
||||
// StartClient(bufferSize)
|
||||
if (!session->StartClient(rx_buffer_size))
|
||||
{
|
||||
DMSG(0, "NormSimAgent::StartClient() start client error!\n");
|
||||
session_mgr.Destroy();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DMSG(0, "NormSimAgent::StartClient() new session error!\n");
|
||||
return false;
|
||||
}
|
||||
} // end NormSimAgent::StartServer()
|
||||
|
||||
void NormSimAgent::Stop()
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
if (session->IsServer()) session->StopServer();
|
||||
if (session->IsClient()) session->StopClient();
|
||||
session_mgr.DeleteSession(session);
|
||||
session = NULL;
|
||||
}
|
||||
} // end NormSimAgent::StopServer()
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
// normSimAgent.h - Generic (base class) NORM simulation agent
|
||||
|
||||
#include "protoLib.h"
|
||||
#include "normSession.h"
|
||||
|
||||
// Base class for Norm simulation agents (e.g. ns-2, OPNET, etc)
|
||||
class NormSimAgent : public NormController
|
||||
{
|
||||
public:
|
||||
NormSimAgent();
|
||||
virtual ~NormSimAgent();
|
||||
void Init(ProtocolTimerInstallFunc* timerInstaller,
|
||||
const void* timerInstallData,
|
||||
UdpSocketInstallFunc* socketInstaller,
|
||||
void* socketInstallData)
|
||||
{
|
||||
session_mgr.Init(timerInstaller, timerInstallData,
|
||||
socketInstaller, socketInstallData,
|
||||
this);
|
||||
}
|
||||
|
||||
bool ProcessCommand(const char* cmd, const char* val);
|
||||
|
||||
// Note: don't allow client _and_ server operation at same time
|
||||
bool StartServer();
|
||||
bool StartClient();
|
||||
void Stop();
|
||||
|
||||
|
||||
protected:
|
||||
enum CmdType {CMD_INVALID, CMD_NOARG, CMD_ARG};
|
||||
CmdType CommandType(const char* cmd);
|
||||
virtual unsigned long GetAgentId() = 0;
|
||||
|
||||
private:
|
||||
virtual void Notify(NormController::Event event,
|
||||
class NormSessionMgr* sessionMgr,
|
||||
class NormSession* session,
|
||||
class NormServerNode* server,
|
||||
class NormObject* object);
|
||||
|
||||
void InstallTimer(ProtocolTimer& theTimer)
|
||||
{session_mgr.InstallTimer(&theTimer);}
|
||||
|
||||
bool OnIntervalTimeout();
|
||||
|
||||
static const char* const cmd_list[];
|
||||
|
||||
NormSessionMgr session_mgr;
|
||||
NormSession* session;
|
||||
NormStreamObject* stream;
|
||||
|
||||
// session parameters
|
||||
char* address; // session address
|
||||
UINT16 port; // session port number
|
||||
UINT8 ttl;
|
||||
double tx_rate; // bits/sec
|
||||
double backoff_factor;
|
||||
UINT16 segment_size;
|
||||
UINT8 ndata;
|
||||
UINT8 nparity;
|
||||
UINT8 auto_parity;
|
||||
double group_size;
|
||||
unsigned long tx_buffer_size; // bytes
|
||||
unsigned long rx_buffer_size; // bytes
|
||||
|
||||
// for simulated transmission (streams or files)
|
||||
unsigned long tx_object_size;
|
||||
double tx_object_interval;
|
||||
int tx_repeat_count;
|
||||
double tx_repeat_interval;
|
||||
|
||||
ProtocolTimer interval_timer;
|
||||
|
||||
// protocol debug parameters
|
||||
bool tracing;
|
||||
double tx_loss;
|
||||
double rx_loss;
|
||||
|
||||
}; // end class NormSimAgent
|
||||
|
||||
Binary file not shown.
|
|
@ -83,3 +83,81 @@ for which the receiver is maintaining state. That range may
|
|||
be application specific and senders/receivers are anticipated
|
||||
to use relatively compatible buffer ranges/sizes based on
|
||||
application needs.
|
||||
|
||||
SENDER/RECEIVER RESOURCE MANAGEMENT
|
||||
|
||||
The NORM implementation restricts the server (sender) to a
|
||||
user-defined, fixed amount of buffer space (memory) for storing
|
||||
calculated parity segments and repair state (i.e. which
|
||||
blocks/segments are pending transmission, and any pending repairs).
|
||||
The client (receiver) is similarly limited, but on a "per-sender"
|
||||
basis (i.e. the client allocates a fixed pool of buffer space per
|
||||
sender). However, the code is designed to successfully work even
|
||||
when the buffer limits are exceeded. It does this via resource
|
||||
management (i.e. stealing less critical buffer resources to cache
|
||||
transmit or receive repair state). The buffer space consists of a
|
||||
pool of NormBlock objects (keeps send/receive state on FEC coding
|
||||
blocks) and a pool of segments (vectors for buffering NORM data or
|
||||
parity payloads). The server (sender) keeps pools on a "per-session"
|
||||
basis and the client (receiver) keeps pools on a "per-server" basis.
|
||||
The policies for managing these pools are as follows:
|
||||
|
||||
NORM Server (sender) resource management.
|
||||
|
||||
When the NORM server is transmitting ordinally new objects/ blocks (or
|
||||
is required to store repair state for previously transmitted blocks
|
||||
when receiving a NACK) and its block or segment pools are empty, it
|
||||
will attempt to "steal" block and/or segment resources from
|
||||
non-pending blocks (oldest first) of objects in its "holding queue".
|
||||
The presumption is that requests for repair of oldest objects/blocks
|
||||
is less likely since clients (receivers) request repair of oldest
|
||||
objects first and older objects/blocks in the queue are more likely to
|
||||
have already been successfully received by all of the clients. This
|
||||
results in most efficient use of CPU time on the sender side when
|
||||
BANDWIDTH*DELAY*LOSS product of the network topology falls well within
|
||||
the buffer space limits defined for the server. NORM will still
|
||||
operate successfully at extremes of bandwidth*delay*loss, but a cost
|
||||
of additional CPU demand for recalculating parity repair segments.
|
||||
When there are no "non-pending" blocks to steal, repairs may be
|
||||
ignored, or tranmission paused, until the repair process, and
|
||||
subsequent transmissions, cause blocks to be marked as non-pending (no
|
||||
pending segment transmissions or repairs).
|
||||
|
||||
Also note the oldest object is removed from the "holding queue" when
|
||||
the count of objects in the holding queue exceeds the "transmit
|
||||
hold" buffer limits when a new object is queued for transmission by
|
||||
the server (sender) application. When these objects are removed, any
|
||||
block or segment resources used by those objects are returned to the
|
||||
respective server pools. The "transmit hold" buffer limits will be
|
||||
defined as the minimum of 1) total size of objects in the queue
|
||||
exceeding a "maxSize" parameter, or 2) total count of objects in the
|
||||
queue exceeding a "maxCount" parameter. The "maxSize" limit is
|
||||
overridden if a "minCount" value of greater than one is defined. The
|
||||
"transmit hold" buffer limits define how far back the NORM server is
|
||||
willing/able to go to provide repair information previous to its
|
||||
current oridinal transmission point.
|
||||
|
||||
NORM Client (receiver) resource management.
|
||||
|
||||
The client portion of NORM similarly has pre-determined limits on the
|
||||
quantity of buffering resources (memory) it will use on a per-server
|
||||
basis. Again, NORM will still successfully operate when the
|
||||
bandwidth*delay*loss product of the network topology exceed these
|
||||
expected/allocated buffer limits. The cost, when this occurs, is a
|
||||
decrease in the repair _efficiency_ of the NORM protocol. When the
|
||||
client (receiver) becomes buffer limited, it attempts to "steal"
|
||||
buffer/segment resources from ordinally _newer_ objects/blocks for
|
||||
which it has accumulated state (Thus giving priority to the repair of
|
||||
ordinally oldest objects/blocks). When the bandwidth*delay*loss
|
||||
product of the network topology causes this to occur, the result is
|
||||
less efficient repair since the client will either "forget" about some
|
||||
segments it has already succesfully received (when newer blocks are
|
||||
"stolen") or ignore received segments for the most ordinally recent
|
||||
blocks in favor of holding state for older objects/blocks.
|
||||
|
||||
In summary, the NORM protocol will operate most efficiently when there
|
||||
is ample buffer space, but will still successfully converge when
|
||||
bandwidth*delay*loss of the network reaches extremes. The amount of
|
||||
memory used by NORM can be controlled by the application.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* AUTHORIZATION TO USE AND DISTRIBUTE
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that:
|
||||
*
|
||||
* (1) source code distributions retain this paragraph in its entirety,
|
||||
*
|
||||
* (2) distributions including binary code include this paragraph in
|
||||
* its entirety in the documentation or other materials provided
|
||||
* with the distribution, and
|
||||
*
|
||||
* (3) all advertising materials mentioning features or use of this
|
||||
* software display the following acknowledgment:
|
||||
*
|
||||
* "This product includes software written and developed
|
||||
* by Brian Adamson and Joe Macker of the Naval Research
|
||||
* Laboratory (NRL)."
|
||||
*
|
||||
* The name of NRL, the name(s) of NRL employee(s), or any entity
|
||||
* of the United States Government may not be used to endorse or
|
||||
* promote products derived from this software, nor does the
|
||||
* inclusion of the NRL written and developed software directly or
|
||||
* indirectly suggest NRL or United States Government endorsement
|
||||
* of this product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
********************************************************************/
|
||||
|
||||
#ifndef _NORM_VERSION
|
||||
#define _NORM_VERSION
|
||||
#define VERSION "1.0x1"
|
||||
#endif // _NORM_VERSION
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
NORM ns-2 Support
|
||||
|
||||
This directory contains files for incorporating the NORM protocol
|
||||
into the ns-2 network simulation tool.
|
||||
|
||||
FILES:
|
||||
|
||||
nsNormAgent.h
|
||||
nsNormAgent.cpp - C++ files which implement an ns-2 derivative of
|
||||
the NormSimAgent class defined in the
|
||||
../common/normSimAgent.h files.
|
||||
|
||||
nackCount.cpp - Source code to count sent & suppressed NACKs
|
||||
from the reports in NORM debug log files.
|
||||
(build with "gcc -o nc nackCount.cpp")
|
||||
|
||||
example.tcl - Very simple ns-2 TCL script illustrating the
|
||||
use of a NORM agent.
|
||||
|
||||
simplenorm.tcl - Parameterized ns-2 TCL script which can be
|
||||
used to evaluate NORM in a hub & spoke
|
||||
topology.
|
||||
|
||||
suppress.tcl - Executable TCL script which iteratively
|
||||
invokes "ns" with the "simplenorm.tcl"
|
||||
script and "nc" (nackCount) to evaluate
|
||||
NORM NACK suppression performance.
|
||||
|
||||
|
||||
|
||||
ns-2.1b7a-Makefile.in - Patched version of ns-2.1b7a Makefile.in
|
||||
with NORM & PROTOLIB stuff included.
|
||||
|
||||
|
||||
TO BUILD NS-2 with NORM Agent capabilities:
|
||||
|
||||
1) Put "protolib" and "norm" source tree directories into the ns-2
|
||||
source code directory.
|
||||
|
||||
2) Add PROTOLIB and NORM paths, macros, and, source objects to
|
||||
the ns-2 "Makefile.in" (See the included patched "
|
||||
ns-2.1b7-Makefile.in" as an example)
|
||||
|
||||
(A future version of this README will include detailed instructions
|
||||
to patch ns-2 for PROTOLIB and NORM support)
|
||||
|
||||
3) Run "./configure" in the ns-2 source directory.
|
||||
|
||||
4) Use "make ns" to rebuild ns-2 with the NORM/PROTOLIB support.
|
||||
|
||||
|
||||
|
||||
Brian Adamson
|
||||
<adamson@itd.nrl.navy.mil>
|
||||
11 July 2002
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
# This script describes and illustrates the commands and usage of the
|
||||
# NORM ns-2 simulation agent.
|
||||
|
||||
# 1) An ns-2 simulator instance is created an configured
|
||||
# for multicast operation with a dense mode (DM) multicast
|
||||
# routing protocol:
|
||||
set ns_ [new Simulator -multicast on]
|
||||
$ns_ multicast
|
||||
|
||||
|
||||
# 2) Trace files are opened and ns and nam
|
||||
# tracing is enabled:
|
||||
set f [open norm.tr w]
|
||||
#$ns_ trace-all $f
|
||||
set nf [open norm.nam w]
|
||||
#$ns_ namtrace-all $nf
|
||||
|
||||
set numNodes 10
|
||||
|
||||
# 3) A simple hub and spoke topology is created with
|
||||
# a NORM agent attached to each node
|
||||
|
||||
# Node 0 is the hub
|
||||
set n(0) [$ns_ node]
|
||||
|
||||
puts "Creating nodes and norm agents ..."
|
||||
for {set i 1} {$i <= $numNodes} {incr i} {
|
||||
set n($i) [$ns_ node]
|
||||
set norm($i) [new Agent/NORM]
|
||||
$ns_ attach-agent $n($i) $norm($i)
|
||||
}
|
||||
|
||||
puts "Creating topology ..."
|
||||
for {set i 1} {$i <= $numNodes} {incr i} {
|
||||
$ns_ duplex-link $n(0) $n($i) 64kb 100ms DropTail
|
||||
$ns_ queue-limit $n(0) $n($i) 100
|
||||
#$ns_ duplex-link-op $n(0) $n($i) orient right
|
||||
$ns_ duplex-link-op $n(0) $n($i) queuePos 0.5
|
||||
}
|
||||
|
||||
# 4) Configure multicast routing as needed
|
||||
set mproto DM
|
||||
set mrthandle [$ns_ mrtproto $mproto {}]
|
||||
if {$mrthandle != ""} {
|
||||
$mrthandle set_c_rp [list $n(0)]
|
||||
}
|
||||
|
||||
# 4) Allocate a multicast address to use
|
||||
set group [Node allocaddr]
|
||||
|
||||
puts "Configuring NORM agents ..."
|
||||
|
||||
# 5) Configure global NORM agent parameters (debugging/logging is global)
|
||||
# (Uncomment the "log" command to direct NORM debug output to a file)
|
||||
$norm(1) debug 2
|
||||
#$norm(1) log normLog.txt
|
||||
|
||||
# 6) Configure NORM server agent at node 1
|
||||
$norm(1) address $group/5000
|
||||
$norm(1) rate 32000
|
||||
$norm(1) block 1
|
||||
$norm(1) parity 0
|
||||
$norm(1) repeat -1
|
||||
$norm(1) interval 0.0
|
||||
$norm(1) txloss 10.0
|
||||
# Enabl NORM message tracing at the server
|
||||
#$norm(1) trace
|
||||
|
||||
# 7) Configure NORM client agents at other nodes
|
||||
for {set i 2} {$i <= $numNodes} {incr i} {
|
||||
$norm($i) address $group/5000
|
||||
$norm($i) rxbuffer 100000
|
||||
$norm($i) txloss 10.0
|
||||
#$norm($i) trace
|
||||
}
|
||||
|
||||
# 8) Start server and clients
|
||||
$ns_ at 0.0 "$norm(1) start server"
|
||||
for {set i 2} {$i <= $numNodes} {incr i} {
|
||||
$ns_ at 0.0 "$norm($i) start client"
|
||||
}
|
||||
$ns_ at 0.0 "$norm(1) sendFile 64000"
|
||||
|
||||
$ns_ at 3000.0 "finish $ns_ $f $nf"
|
||||
|
||||
proc finish {ns_ f nf} {
|
||||
$ns_ flush-trace
|
||||
close $f
|
||||
close $nf
|
||||
$ns_ halt
|
||||
delete $ns_
|
||||
}
|
||||
|
||||
$ns_ run
|
||||
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h> // for PATH_MAX
|
||||
#include <string.h>
|
||||
|
||||
#define MIN(X,Y) ((X<Y)?X:Y)
|
||||
#define MAX(X,Y) ((X>Y)?X:Y)
|
||||
|
||||
class Client
|
||||
{
|
||||
friend class ClientList;
|
||||
|
||||
public:
|
||||
Client(unsigned long theId);
|
||||
unsigned long Id() {return id;}
|
||||
unsigned long Sent() {return sent;}
|
||||
void SetSent(unsigned long count) {sent = count;}
|
||||
unsigned long Suppressed() {return suppressed;}
|
||||
void SetSuppressed(unsigned long count) {suppressed = count;}
|
||||
Client* Next() {return next;}
|
||||
|
||||
private:
|
||||
unsigned long id;
|
||||
unsigned long sent;
|
||||
unsigned long suppressed;
|
||||
Client* next;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class ClientList
|
||||
{
|
||||
public:
|
||||
ClientList();
|
||||
~ClientList();
|
||||
void AddClient(Client* theClient);
|
||||
void Destroy();
|
||||
Client* FindClientById(unsigned long theId);
|
||||
Client* Top() {return top;}
|
||||
|
||||
private:
|
||||
Client* top;
|
||||
};
|
||||
|
||||
|
||||
Client::Client(unsigned long theId)
|
||||
: id(theId), sent(0), suppressed(0), next(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
ClientList::ClientList()
|
||||
: top(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
ClientList::~ClientList()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void ClientList::AddClient(Client* theClient)
|
||||
{
|
||||
theClient->next = top;
|
||||
top = theClient;
|
||||
} // end ClientList::AddClient()
|
||||
|
||||
void ClientList::Destroy()
|
||||
{
|
||||
while (top)
|
||||
{
|
||||
Client* next = top->next;
|
||||
delete top;
|
||||
top = next;
|
||||
}
|
||||
} // end ClientList::Destroy()
|
||||
|
||||
Client* ClientList::FindClientById(unsigned long theId)
|
||||
{
|
||||
Client* next = top;
|
||||
while(next)
|
||||
{
|
||||
if (theId == next->id)
|
||||
return next;
|
||||
else
|
||||
next = next->next;
|
||||
}
|
||||
return NULL;
|
||||
} // end ClientList::FindClientById()
|
||||
|
||||
|
||||
|
||||
|
||||
const int MAX_LINE = 256;
|
||||
|
||||
class FastReader
|
||||
{
|
||||
public:
|
||||
FastReader();
|
||||
bool Read(FILE* filePtr, char* buffer, unsigned int* len);
|
||||
bool Readline(FILE* filePtr, char* buffer, unsigned int* len);
|
||||
|
||||
private:
|
||||
char savebuf[256];
|
||||
char* saveptr;
|
||||
unsigned int savecount;
|
||||
}; // end class FastReader
|
||||
|
||||
FastReader::FastReader()
|
||||
: savecount(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FastReader::Read(FILE* filePtr, char* buffer, unsigned int* len)
|
||||
{
|
||||
unsigned int want = *len;
|
||||
if (savecount)
|
||||
{
|
||||
unsigned int ncopy = MIN(want, savecount);
|
||||
memcpy(buffer, saveptr, ncopy);
|
||||
savecount -= ncopy;
|
||||
saveptr += ncopy;
|
||||
buffer += ncopy;
|
||||
want -= ncopy;
|
||||
}
|
||||
while (want)
|
||||
{
|
||||
unsigned int result = fread(savebuf, sizeof(char), 256, filePtr);
|
||||
if (result)
|
||||
{
|
||||
unsigned int ncopy= MIN(want, result);
|
||||
memcpy(buffer, savebuf, ncopy);
|
||||
savecount = result - ncopy;
|
||||
saveptr = savebuf + ncopy;
|
||||
buffer += ncopy;
|
||||
want -= ncopy;
|
||||
}
|
||||
else // end-of-file
|
||||
{
|
||||
*len -= want;
|
||||
if (*len)
|
||||
return true; // we read something
|
||||
else
|
||||
return false; // we read nothing
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} // end FastReader::Read()
|
||||
|
||||
// An OK text readline() routine (reads what will fit into buffer incl. NULL termination)
|
||||
// if *len is unchanged on return, it means the line is bigger than the buffer and
|
||||
// requires multiple reads
|
||||
|
||||
bool FastReader::Readline(FILE* filePtr, char* buffer, unsigned int* len)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
unsigned int length = *len;
|
||||
char* ptr = buffer;
|
||||
unsigned int one = 1;
|
||||
while ((count < length) && Read(filePtr, ptr, &one))
|
||||
{
|
||||
if (('\n' == *ptr) || ('\r' == *ptr))
|
||||
{
|
||||
*ptr = '\0';
|
||||
*len = count;
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
ptr++;
|
||||
}
|
||||
// Either we've filled the buffer or hit end-of-file
|
||||
if (count < length) *len = 0; // Set *len = 0 on EOF
|
||||
return false;
|
||||
} // end FastReader::Readline()
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
ClientList list;
|
||||
FastReader reader;
|
||||
char buffer[512];
|
||||
unsigned long line = 0;
|
||||
unsigned int len = 512;
|
||||
while (reader.Readline(stdin, buffer, &len))
|
||||
{
|
||||
line++;
|
||||
if (!strncmp(buffer, "Report", 6))
|
||||
{
|
||||
char* ptr = strstr(buffer, "node>");
|
||||
if (!ptr) break;
|
||||
unsigned long nodeId;
|
||||
if (1 != sscanf(ptr, "node>%lu", &nodeId))
|
||||
{
|
||||
fprintf(stderr, "nackCount: Error parsing log file "
|
||||
"at line: %lu\n", line);
|
||||
exit(-1);
|
||||
}
|
||||
// Update NACKs transmitted:
|
||||
len = 512;
|
||||
while (reader.Readline(stdin, buffer, &len))
|
||||
{
|
||||
line++;
|
||||
if ('*' == buffer[0]) break; // end of report
|
||||
len = 512;
|
||||
//fprintf(stderr, "Searching: \"%s\"\n", buffer);
|
||||
ptr = strstr(buffer, "nacks>");
|
||||
if (ptr)
|
||||
{
|
||||
Client* client = list.FindClientById(nodeId);
|
||||
if (!client)
|
||||
{
|
||||
if (!(client = new Client(nodeId)))
|
||||
{
|
||||
perror("nackCount: Error creating Client:");
|
||||
exit(-1);
|
||||
}
|
||||
list.AddClient(client);
|
||||
}
|
||||
unsigned long count;
|
||||
if (1 == sscanf(ptr, "nacks>%lu", &count))
|
||||
{
|
||||
client->SetSent(count);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error parsing log file "
|
||||
"at line: %lu \n", line);
|
||||
exit(-1);
|
||||
}
|
||||
if ((ptr = strstr(buffer, "suppressed>")))
|
||||
{
|
||||
if (1 == sscanf(ptr, "suppressed>%lu", &count))
|
||||
{
|
||||
client->SetSuppressed(count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error parsing log file "
|
||||
"at line: %lu \n", line);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
} // end while (reader.Readline("nacks/suppressed" search))
|
||||
}
|
||||
len = 512;
|
||||
} // end while (reader.Readline("Report" search))
|
||||
|
||||
unsigned long numClient = 0;
|
||||
Client* next = list.Top();
|
||||
unsigned long totalSent = 0;
|
||||
unsigned long totalSuppressed = 0;
|
||||
while (next)
|
||||
{
|
||||
totalSent += next->Sent();
|
||||
totalSuppressed += next->Suppressed();
|
||||
next = next->Next();
|
||||
numClient++;
|
||||
}
|
||||
|
||||
fprintf(stdout, "Receivers:%lu Sent:%lu Suppressed:%lu alpha:%f\n",
|
||||
numClient, totalSent, totalSuppressed,
|
||||
(double)totalSent / (double) (totalSuppressed+totalSent));
|
||||
} // end main()
|
||||
|
||||
|
|
@ -0,0 +1,432 @@
|
|||
# Copyright (c) 1994, 1995, 1996
|
||||
# The Regents of the University of California. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that: (1) source code distributions
|
||||
# retain the above copyright notice and this paragraph in its entirety, (2)
|
||||
# distributions including binary code include the above copyright notice and
|
||||
# this paragraph in its entirety in the documentation or other materials
|
||||
# provided with the distribution, and (3) all advertising materials mentioning
|
||||
# features or use of this software display the following acknowledgement:
|
||||
# ``This product includes software developed by the University of California,
|
||||
# Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
|
||||
# the University nor the names of its contributors may be used to endorse
|
||||
# or promote products derived from this software without specific prior
|
||||
# written permission.
|
||||
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
# @(#) $Header: /cvsroot/norm/norm/ns/ns-2.1b7a-Makefile.in,v 1.1 2002/07/11 15:25:11 adamson Exp $ (LBL)
|
||||
|
||||
#
|
||||
# Various configurable paths (remember to edit Makefile.in, not Makefile)
|
||||
#
|
||||
|
||||
# Top level hierarchy
|
||||
prefix = @prefix@
|
||||
# Pathname of directory to install the binary
|
||||
BINDEST = @prefix@/bin
|
||||
# Pathname of directory to install the man page
|
||||
MANDEST = @prefix@/man
|
||||
|
||||
BLANK = # make a blank space. DO NOT add anything to this line
|
||||
|
||||
# The following will be redefined under Windows (see WIN32 lable below)
|
||||
CC = @CC@
|
||||
CPP = @CXX@
|
||||
LINK = $(CPP)
|
||||
MKDEP = ./conf/mkdep
|
||||
TCLSH = @V_TCLSH@
|
||||
TCL2C = @V_TCL2CPP@
|
||||
AR = ar rc $(BLANK)
|
||||
|
||||
RANLIB = @V_RANLIB@
|
||||
INSTALL = @INSTALL@
|
||||
LN = ln
|
||||
TEST = test
|
||||
RM = rm -f
|
||||
PERL = @PERL@
|
||||
|
||||
|
||||
CCOPT = @V_CCOPT@
|
||||
STATIC = @V_STATIC@
|
||||
LDFLAGS = $(STATIC)
|
||||
LDOUT = -o $(BLANK)
|
||||
|
||||
DEFINE = -DTCP_DELAY_BIND_ALL -DNO_TK @V_DEFINE@ @V_DEFINES@ @DEFS@
|
||||
|
||||
PROTOLIB_INCLUDES = -Iprotolib/common -Iprotolib/ns
|
||||
MDP_INCLUDES = -Imdp/common -Imdp/ns -Imdp/unix
|
||||
NORM_INCLUDES = -Inorm/common -Inorm/ns
|
||||
|
||||
INCLUDES = \
|
||||
-I. @V_INCLUDE_X11@ \
|
||||
@V_INCLUDES@ \
|
||||
$(PROTOLIB_INCLUDES) $(MDP_INCLUDES) $(NORM_INCLUDES)
|
||||
|
||||
LIB = \
|
||||
@V_LIBS@ \
|
||||
@V_LIB_X11@ \
|
||||
@V_LIB@ \
|
||||
-lm @LIBS@
|
||||
# -L@libdir@ \
|
||||
|
||||
# These flags work on Linux
|
||||
PROTOLIB_CFLAGS = -DUNIX -DNS2 -DPROTO_DEBUG -DSIMULATE -DHAVE_ASSERT -DHAVE_DIRFD
|
||||
|
||||
CFLAGS = $(CCOPT) $(DEFINE) -g $(PROTOLIB_CFLAGS)
|
||||
|
||||
# Explicitly define compilation rules since SunOS 4's make doesn't like gcc.
|
||||
# Also, gcc does not remove the .o before forking 'as', which can be a
|
||||
# problem if you don't own the file but can write to the directory.
|
||||
# PROTOLIB/MDP/NORM code uses .cpp files so we added a suffix and rule
|
||||
.SUFFIXES: .cc .cpp # $(.SUFFIXES)
|
||||
|
||||
.cpp.o:
|
||||
@rm -f $@
|
||||
$(CPP) -c $(CFLAGS) $(INCLUDES) -o $@ $*.cpp
|
||||
|
||||
.cc.o:
|
||||
@rm -f $@
|
||||
$(CPP) -c $(CFLAGS) $(INCLUDES) -o $@ $*.cc
|
||||
|
||||
.c.o:
|
||||
@rm -f $@
|
||||
$(CC) -c $(CFLAGS) $(INCLUDES) -o $@ $*.c
|
||||
|
||||
|
||||
GEN_DIR = gen/
|
||||
LIB_DIR = lib/
|
||||
NS = ns
|
||||
NSX = nsx
|
||||
NSE = nse
|
||||
|
||||
# To allow conf/makefile.win overwrite this macro
|
||||
OBJ_STL = @V_STLOBJ@
|
||||
|
||||
# WIN32: uncomment the following line to include specific make for VC++
|
||||
# !include <conf/makefile.win>
|
||||
|
||||
OBJ_CC = \
|
||||
random.o rng.o ranvar.o misc.o timer-handler.o \
|
||||
scheduler.o object.o \
|
||||
packet.o ip.o route.o connector.o ttl.o \
|
||||
trace.o trace-ip.o \
|
||||
classifier.o classifier-addr.o classifier-hash.o classifier-virtual.o \
|
||||
classifier-mcast.o classifier-bst.o classifier-mpath.o replicator.o \
|
||||
classifier-mac.o classifier-port.o ump.o \
|
||||
app.o telnet.o tcplib-telnet.o \
|
||||
trafgen.o traffictrace.o pareto.o expoo.o cbr_traffic.o \
|
||||
tbf.o resv.o sa.o saack.o \
|
||||
measuremod.o estimator.o adc.o ms-adc.o timewindow-est.o acto-adc.o \
|
||||
pointsample-est.o salink.o actp-adc.o hb-adc.o expavg-est.o\
|
||||
param-adc.o null-estimator.o \
|
||||
adaptive-receiver.o vatrcvr.o consrcvr.o \
|
||||
agent.o message.o udp.o session-rtp.o rtp.o rtcp.o ivs.o \
|
||||
tcp.o tcp-sink.o tcp-reno.o tcp-newreno.o \
|
||||
tcp-vegas.o tcp-rbp.o tcp-full.o \
|
||||
scoreboard.o tcp-sack1.o tcp-fack.o \
|
||||
tcp-asym.o tcp-asym-sink.o tcp-fs.o tcp-asym-fs.o \
|
||||
tcp-int.o chost.o tcp-session.o nilist.o \
|
||||
integrator.o queue-monitor.o flowmon.o loss-monitor.o \
|
||||
queue.o drop-tail.o simple-intserv-sched.o red.o \
|
||||
semantic-packetqueue.o semantic-red.o ack-recons.o \
|
||||
sfq.o fq.o drr.o cbq.o \
|
||||
hackloss.o errmodel.o \
|
||||
delay.o snoop.o \
|
||||
dynalink.o rtProtoDV.o net-interface.o \
|
||||
ctrMcast.o mcast_ctrl.o srm.o \
|
||||
sessionhelper.o delaymodel.o srm-ssm.o \
|
||||
srm-topo.o \
|
||||
mftp.o mftp_snd.o mftp_rcv.o codeword.o \
|
||||
alloc-address.o address.o \
|
||||
$(LIB_DIR)int.Vec.o $(LIB_DIR)int.RVec.o \
|
||||
$(LIB_DIR)dmalloc_support.o \
|
||||
webcache/http.o webcache/tcp-simple.o webcache/pagepool.o \
|
||||
webcache/inval-agent.o webcache/tcpapp.o webcache/http-aux.o \
|
||||
webcache/mcache.o webcache/webtraf.o \
|
||||
realaudio/realaudio.o \
|
||||
lanRouter.o filter.o pkt-counter.o \
|
||||
Decapsulator.o Encapsulator.o encap.o \
|
||||
channel.o mac.o ll.o mac-802_11.o mac-802_3.o mac-tdma.o \
|
||||
mip.o mip-reg.o gridkeeper.o \
|
||||
propagation.o tworayground.o antenna.o omni-antenna.o \
|
||||
shadowing.o bi-connector.o node.o mobilenode.o \
|
||||
arp.o god.o dem.o topography.o modulation.o priqueue.o \
|
||||
phy.o wired-phy.o wireless-phy.o \
|
||||
mac-timers.o cmu-trace.o varp.o \
|
||||
dsdv/dsdv.o dsdv/rtable.o rtqueue.o rttable.o \
|
||||
imep/imep.o imep/dest_queue.o imep/imep_api.o \
|
||||
imep/imep_rt.o imep/rxmit_queue.o imep/imep_timers.o \
|
||||
imep/imep_util.o imep/imep_io.o \
|
||||
tora/tora.o tora/tora_api.o tora/tora_dest.o tora/tora_io.o \
|
||||
tora/tora_logs.o tora/tora_neighbor.o \
|
||||
dsr/dsragent.o dsr/hdr_sr.o dsr/mobicache.o dsr/path.o \
|
||||
dsr/requesttable.o dsr/routecache.o \
|
||||
aodv/aodv_logs.o aodv/aodv.o \
|
||||
ns-process.o \
|
||||
satgeometry.o sathandoff.o satlink.o satnode.o \
|
||||
satposition.o satroute.o sattrace.o \
|
||||
rap/raplist.o rap/rap.o rap/media-app.o rap/utilities.o \
|
||||
fsm.o tcp-abs.o \
|
||||
diffusion/diffusion.o diffusion/diff_rate.o diffusion/diff_prob.o \
|
||||
diffusion/diff_sink.o diffusion/flooding.o diffusion/omni_mcast.o \
|
||||
diffusion/hash_table.o diffusion/routing_table.o diffusion/iflist.o \
|
||||
tfrc.o tfrc-sink.o energy-model.o ping.o tcp-rfc793edu.o \
|
||||
rio.o semantic-rio.o tcp-sack-rh.o scoreboard-rh.o \
|
||||
plm/loss-monitor-plm.o plm/cbr-traffic-PP.o \
|
||||
linkstate/hdr-ls.o \
|
||||
mpls/classifier-addr-mpls.o mpls/ldp.o mpls/mpls-module.o \
|
||||
rtmodule.o classifier-hier.o addr-params.o \
|
||||
$(OBJ_STL)
|
||||
|
||||
# don't allow comments to follow continuation lines
|
||||
|
||||
# mac-csma.o mac-multihop.o\
|
||||
# sensor-nets/landmark.o mac-simple-wireless.o \
|
||||
# sensor-nets/tags.o sensor-nets/sensor-query.o \
|
||||
# sensor-nets/flood-agent.o \
|
||||
|
||||
# what was here before is now in emulate/
|
||||
OBJ_C =
|
||||
|
||||
OBJ_COMPAT = $(OBJ_GETOPT) win32.o
|
||||
#XXX compat/win32x.o compat/tkConsole.o
|
||||
|
||||
OBJ_EMULATE_CC = \
|
||||
emulate/net-ip.o \
|
||||
emulate/net.o \
|
||||
emulate/tap.o \
|
||||
emulate/ether.o \
|
||||
emulate/internet.o \
|
||||
emulate/ping_responder.o \
|
||||
emulate/arp.o \
|
||||
emulate/icmp.o \
|
||||
emulate/net-pcap.o \
|
||||
emulate/nat.o
|
||||
|
||||
OBJ_EMULATE_C = \
|
||||
emulate/inet.o
|
||||
|
||||
OBJ_PROTOLIB_CPP = \
|
||||
protolib/ns/nsProtoAgent.o protolib/common/protoSim.o \
|
||||
protolib/common/networkAddress.o protolib/common/protocolTimer.o \
|
||||
protolib/common/debug.o
|
||||
|
||||
OBJ_MDP_CPP = \
|
||||
mdp/ns/nsMdpAgent.o mdp/common/mdpSimAgent.o \
|
||||
mdp/common/mdpBitMask.o mdp/common/mdpMessage.o \
|
||||
mdp/common/mdpEncoder.o mdp/common/galois.o \
|
||||
mdp/common/mdpSession.o mdp/common/mdpMsgHandler.o \
|
||||
mdp/common/mdpNode.o mdp/common/mdpObject.o \
|
||||
mdp/unix/mdpFile.o
|
||||
|
||||
OBJ_NORM_CPP = \
|
||||
norm/ns/nsNormAgent.o norm/common/normSimAgent.o \
|
||||
norm/common/normMessage.o norm/common/normSession.o \
|
||||
norm/common/normNode.o norm/common/normObject.o \
|
||||
norm/common/normSegment.o norm/common/normBitmask.o \
|
||||
norm/common/normEncoder.o norm/common/galois.o \
|
||||
norm/common/normFile.o
|
||||
|
||||
OBJ_GEN = $(GEN_DIR)version.o $(GEN_DIR)ns_tcl.o $(GEN_DIR)ptypes.o
|
||||
|
||||
SRC = $(OBJ_C:.o=.c) $(OBJ_CC:.o=.cc) \
|
||||
$(OBJ_EMULATE_C:.o=.c) $(OBJ_EMULATE_CC:.o=.cc) \
|
||||
tclAppInit.cc tkAppInit.cc \
|
||||
$(OBJ_PROTOLIB_CPP:.o=.cpp) $(OBJ_MDP_CPP:.o=.cpp) \
|
||||
$(OBJ_NORM_CPP:.o=.cpp)
|
||||
|
||||
OBJ = $(OBJ_C) $(OBJ_CC) $(OBJ_GEN) $(OBJ_COMPAT) \
|
||||
$(OBJ_PROTOLIB_CPP) $(OBJ_MDP_CPP) $(OBJ_NORM_CPP)
|
||||
|
||||
CLEANFILES = ns nsx ns.dyn $(OBJ) $(OBJ_EMULATE_CC) \
|
||||
$(OBJ_EMULATE_C) tclAppInit.o \
|
||||
$(GEN_DIR)* $(NS).core core core.$(NS) core.$(NSX) core.$(NSE) \
|
||||
ptypes2tcl ptypes2tcl.o
|
||||
|
||||
SUBDIRS=\
|
||||
indep-utils/cmu-scen-gen/setdest \
|
||||
indep-utils/webtrace-conv/dec \
|
||||
indep-utils/webtrace-conv/epa \
|
||||
indep-utils/webtrace-conv/nlanr \
|
||||
indep-utils/webtrace-conv/ucb
|
||||
|
||||
all: $(NS) all-recursive
|
||||
|
||||
all-recursive:
|
||||
for i in $(SUBDIRS); do ( cd $$i; $(MAKE) all; ) done
|
||||
|
||||
$(NS): $(OBJ) tclAppInit.o Makefile
|
||||
$(LINK) $(LDFLAGS) $(LDOUT)$@ \
|
||||
tclAppInit.o $(OBJ) $(LIB)
|
||||
|
||||
Makefile: Makefile.in
|
||||
@echo "Makefile.in is newer than Makefile."
|
||||
@echo "You need to re-run configure."
|
||||
false
|
||||
|
||||
$(NSE): $(OBJ) tclAppInit.o $(OBJ_EMULATE_CC) $(OBJ_EMULATE_C)
|
||||
$(LINK) $(LDFLAGS) $(LDOUT)$@ \
|
||||
tclAppInit.o $(OBJ) \
|
||||
$(OBJ_EMULATE_CC) $(OBJ_EMULATE_C) $(LIB) -lpcap
|
||||
|
||||
ns.dyn: $(OBJ) tclAppInit.o
|
||||
$(LINK) $(LDFLAGS) -o $@ \
|
||||
tclAppInit.o $(OBJ) $(LIB)
|
||||
|
||||
PURIFY = purify -cache-dir=/tmp
|
||||
ns-pure: $(OBJ) tclAppInit.o
|
||||
$(PURIFY) $(LINK) $(LDFLAGS) -o $@ \
|
||||
tclAppInit.o $(OBJ) $(LIB)
|
||||
|
||||
NS_TCL_LIB = \
|
||||
tcl/lib/ns-compat.tcl \
|
||||
tcl/lib/ns-default.tcl \
|
||||
tcl/lib/ns-errmodel.tcl \
|
||||
tcl/lib/ns-lib.tcl \
|
||||
tcl/lib/ns-link.tcl \
|
||||
tcl/lib/ns-mobilenode.tcl \
|
||||
tcl/lib/ns-sat.tcl \
|
||||
tcl/lib/ns-cmutrace.tcl \
|
||||
tcl/lib/ns-node.tcl \
|
||||
tcl/lib/ns-rtmodule.tcl \
|
||||
tcl/lib/ns-hiernode.tcl \
|
||||
tcl/lib/ns-packet.tcl \
|
||||
tcl/lib/ns-queue.tcl \
|
||||
tcl/lib/ns-source.tcl \
|
||||
tcl/lib/ns-nam.tcl \
|
||||
tcl/lib/ns-trace.tcl \
|
||||
tcl/lib/ns-agent.tcl \
|
||||
tcl/lib/ns-random.tcl \
|
||||
tcl/lib/ns-namsupp.tcl \
|
||||
tcl/lib/ns-address.tcl \
|
||||
tcl/lib/ns-intserv.tcl \
|
||||
tcl/lib/ns-autoconf.tcl \
|
||||
tcl/rtp/session-rtp.tcl \
|
||||
tcl/lib/ns-mip.tcl \
|
||||
tcl/rtglib/dynamics.tcl \
|
||||
tcl/rtglib/route-proto.tcl \
|
||||
tcl/rtglib/algo-route-proto.tcl \
|
||||
tcl/rtglib/ns-rtProtoLS.tcl \
|
||||
tcl/interface/ns-iface.tcl \
|
||||
tcl/mcast/BST.tcl \
|
||||
tcl/mcast/ns-mcast.tcl \
|
||||
tcl/mcast/McastProto.tcl \
|
||||
tcl/mcast/DM.tcl \
|
||||
tcl/mcast/srm.tcl \
|
||||
tcl/mcast/srm-adaptive.tcl \
|
||||
tcl/mcast/srm-ssm.tcl \
|
||||
tcl/mcast/timer.tcl \
|
||||
tcl/mcast/McastMonitor.tcl \
|
||||
tcl/mcast/mftp_snd.tcl \
|
||||
tcl/mcast/mftp_rcv.tcl \
|
||||
tcl/mcast/mftp_rcv_stat.tcl \
|
||||
tcl/mobility/dsdv.tcl \
|
||||
tcl/mobility/dsr.tcl \
|
||||
tcl/ctr-mcast/CtrMcast.tcl \
|
||||
tcl/ctr-mcast/CtrMcastComp.tcl \
|
||||
tcl/ctr-mcast/CtrRPComp.tcl \
|
||||
tcl/rlm/rlm.tcl \
|
||||
tcl/rlm/rlm-ns.tcl \
|
||||
tcl/session/session.tcl \
|
||||
tcl/lib/ns-route.tcl \
|
||||
tcl/emulate/ns-emulate.tcl \
|
||||
tcl/lan/vlan.tcl \
|
||||
tcl/lan/ns-ll.tcl \
|
||||
tcl/lan/ns-mac.tcl \
|
||||
tcl/webcache/http-agent.tcl \
|
||||
tcl/webcache/http-server.tcl \
|
||||
tcl/webcache/http-cache.tcl \
|
||||
tcl/webcache/http-mcache.tcl \
|
||||
tcl/webcache/webtraf.tcl \
|
||||
tcl/plm/plm.tcl \
|
||||
tcl/plm/plm-ns.tcl \
|
||||
tcl/plm/plm-topo.tcl \
|
||||
tcl/mpls/ns-mpls-classifier.tcl \
|
||||
tcl/mpls/ns-mpls-ldpagent.tcl \
|
||||
tcl/mpls/ns-mpls-node.tcl \
|
||||
tcl/mpls/ns-mpls-simulator.tcl
|
||||
|
||||
$(GEN_DIR)ns_tcl.cc: $(NS_TCL_LIB)
|
||||
$(TCLSH) bin/tcl-expand.tcl tcl/lib/ns-lib.tcl tcl/lib/ns-stl.tcl | $(TCL2C) et_ns_lib > $@
|
||||
|
||||
$(GEN_DIR)version.c: VERSION
|
||||
$(RM) $@
|
||||
$(TCLSH) bin/string2c.tcl version_string < VERSION > $@
|
||||
|
||||
$(GEN_DIR)ptypes.cc: ptypes2tcl packet.h
|
||||
./ptypes2tcl > $@
|
||||
|
||||
ptypes2tcl: ptypes2tcl.o
|
||||
$(LINK) $(LDFLAGS) $(LDOUT)$@ ptypes2tcl.o
|
||||
|
||||
ptypes2tcl.o: ptypes2tcl.cc packet.h
|
||||
|
||||
install: force install-ns install-man install-recursive
|
||||
|
||||
install-ns: force
|
||||
$(INSTALL) -m 555 -o bin -g bin ns $(DESTDIR)$(BINDEST)
|
||||
|
||||
install-man: force
|
||||
$(INSTALL) -m 444 -o bin -g bin ns.1 $(DESTDIR)$(MANDEST)/man1
|
||||
|
||||
install-recursive: force
|
||||
for i in $(SUBDIRS); do cd $$i; $(MAKE) install; done
|
||||
|
||||
clean:
|
||||
$(RM) $(CLEANFILES)
|
||||
|
||||
AUTOCONF_GEN = tcl/lib/ns-autoconf.tcl tcl/lib/ns-stl.tcl
|
||||
distclean:
|
||||
$(RM) $(CLEANFILES) Makefile config.cache config.log config.status \
|
||||
gnuc.h os-proto.h $(AUTOCONF_GEN)
|
||||
|
||||
tags: force
|
||||
ctags -wtd *.cc *.h webcache/*.cc webcache/*.h dsdv/*.cc dsdv/*.h \
|
||||
dsr/*.cc dsr/*.h webcache/*.cc webcache/*.h lib/*.cc lib/*.h \
|
||||
../Tcl/*.cc ../Tcl/*.h
|
||||
|
||||
TAGS: force
|
||||
etags *.cc *.h webcache/*.cc webcache/*.h dsdv/*.cc dsdv/*.h \
|
||||
dsr/*.cc dsr/*.h webcache/*.cc webcache/*.h lib/*.cc lib/*.h \
|
||||
../Tcl/*.cc ../Tcl/*.h
|
||||
|
||||
tcl/lib/TAGS: force
|
||||
( \
|
||||
cd tcl/lib; \
|
||||
$(TCLSH) ../../bin/tcl-expand.tcl ns-lib.tcl | grep '^### tcl-expand.tcl: begin' | awk '{print $$5}' >.tcl_files; \
|
||||
etags --lang=none -r '/^[ \t]*proc[ \t]+\([^ \t]+\)/\1/' `cat .tcl_files`; \
|
||||
etags --append --lang=none -r '/^\([A-Z][^ \t]+\)[ \t]+\(instproc\|proc\)[ \t]+\([^ \t]+\)[ \t]+/\1::\3/' `cat .tcl_files`; \
|
||||
)
|
||||
|
||||
depend: $(SRC)
|
||||
$(MKDEP) $(CFLAGS) $(INCLUDES) $(SRC)
|
||||
|
||||
srctar:
|
||||
@cwd=`pwd` ; dir=`basename $$cwd` ; \
|
||||
name=ns-`cat VERSION | tr A-Z a-z` ; \
|
||||
tar=ns-src-`cat VERSION`.tar.gz ; \
|
||||
list="" ; \
|
||||
for i in `cat FILES` ; do list="$$list $$name/$$i" ; done; \
|
||||
echo \
|
||||
"(rm -f $$tar; cd .. ; ln -s $$dir $$name)" ; \
|
||||
(rm -f $$tar; cd .. ; ln -s $$dir $$name) ; \
|
||||
echo \
|
||||
"(cd .. ; tar cfh $$tar [lots of files])" ; \
|
||||
(cd .. ; tar cfh - $$list) | gzip -c > $$tar ; \
|
||||
echo \
|
||||
"rm ../$$name; chmod 444 $$tar" ; \
|
||||
rm ../$$name; chmod 444 $$tar
|
||||
|
||||
force:
|
||||
|
||||
test: force
|
||||
./validate
|
||||
|
||||
# Create makefile.vc for Win32 development by replacing:
|
||||
# "# !include ..." -> "!include ..."
|
||||
makefile.vc: Makefile.in
|
||||
$(PERL) bin/gen-vcmake.pl < Makefile.in > makefile.vc
|
||||
# $(PERL) -pe 's/^# (\!include)/\!include/o' < Makefile.in > makefile.vc
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* AUTHORIZATION TO USE AND DISTRIBUTE
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that:
|
||||
*
|
||||
* (1) source code distributions retain this paragraph in its entirety,
|
||||
*
|
||||
* (2) distributions including binary code include this paragraph in
|
||||
* its entirety in the documentation or other materials provided
|
||||
* with the distribution, and
|
||||
*
|
||||
* (3) all advertising materials mentioning features or use of this
|
||||
* software display the following acknowledgment:
|
||||
*
|
||||
* "This product includes software written and developed
|
||||
* by Brian Adamson and Joe Macker of the Naval Research
|
||||
* Laboratory (NRL)."
|
||||
*
|
||||
* The name of NRL, the name(s) of NRL employee(s), or any entity
|
||||
* of the United States Government may not be used to endorse or
|
||||
* promote products derived from this software, nor does the
|
||||
* inclusion of the NRL written and developed software directly or
|
||||
* indirectly suggest NRL or United States Government endorsement
|
||||
* of this product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
********************************************************************/
|
||||
|
||||
#include "ip.h" // for hdr_ip def
|
||||
#include "flags.h" // for hdr_flags def
|
||||
|
||||
#include "nsNormAgent.h"
|
||||
|
||||
static class NsNormAgentClass : public TclClass
|
||||
{
|
||||
public:
|
||||
NsNormAgentClass() : TclClass("Agent/NORM") {}
|
||||
TclObject *create(int argc, const char*const* argv)
|
||||
{return (new NsNormAgent());}
|
||||
} class_norm_agent;
|
||||
|
||||
|
||||
NsNormAgent::NsNormAgent()
|
||||
{
|
||||
NormSimAgent::Init(ProtoSimAgent::TimerInstaller,
|
||||
static_cast<ProtoSimAgent*>(this),
|
||||
ProtoSimAgent::SocketInstaller,
|
||||
static_cast<ProtoSimAgent*>(this));
|
||||
}
|
||||
|
||||
NsNormAgent::~NsNormAgent()
|
||||
{
|
||||
}
|
||||
|
||||
int NsNormAgent::command(int argc, const char*const* argv)
|
||||
{
|
||||
// Process commands, passing unknown commands to base Agent class
|
||||
int i = 1;
|
||||
while (i < argc)
|
||||
{
|
||||
NormSimAgent::CmdType cmdType = CommandType(argv[i]);
|
||||
switch (cmdType)
|
||||
{
|
||||
case NormSimAgent::CMD_NOARG:
|
||||
if (!ProcessCommand(argv[i], NULL))
|
||||
{
|
||||
DMSG(0, "NsNormAgent::command() ProcessCommand(%s) error\n",
|
||||
argv[i]);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
|
||||
case NormSimAgent::CMD_ARG:
|
||||
if (!ProcessCommand(argv[i], argv[i+1]))
|
||||
{
|
||||
DMSG(0, "NsNormAgent::command() ProcessCommand(%s, %s) error\n",
|
||||
argv[i], argv[i+1]);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case NormSimAgent::CMD_INVALID:
|
||||
return NsProtoAgent::command(argc, argv);
|
||||
}
|
||||
}
|
||||
return TCL_OK;
|
||||
} // end NsNormAgent::command()
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* AUTHORIZATION TO USE AND DISTRIBUTE
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that:
|
||||
*
|
||||
* (1) source code distributions retain this paragraph in its entirety,
|
||||
*
|
||||
* (2) distributions including binary code include this paragraph in
|
||||
* its entirety in the documentation or other materials provided
|
||||
* with the distribution, and
|
||||
*
|
||||
* (3) all advertising materials mentioning features or use of this
|
||||
* software display the following acknowledgment:
|
||||
*
|
||||
* "This product includes software written and developed
|
||||
* by Brian Adamson and Joe Macker of the Naval Research
|
||||
* Laboratory (NRL)."
|
||||
*
|
||||
* The name of NRL, the name(s) of NRL employee(s), or any entity
|
||||
* of the United States Government may not be used to endorse or
|
||||
* promote products derived from this software, nor does the
|
||||
* inclusion of the NRL written and developed software directly or
|
||||
* indirectly suggest NRL or United States Government endorsement
|
||||
* of this product.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
********************************************************************/
|
||||
|
||||
#ifndef _NS_NORM_AGENT
|
||||
#define _NS_NORM_AGENT
|
||||
|
||||
#include "nsProtoAgent.h" // from ProtoLib
|
||||
#include "normSimAgent.h"
|
||||
|
||||
// The "NsNormAgent" is based on the ns agent class.
|
||||
// This lets us have a send/recv attachment to the NS
|
||||
// simulation environment
|
||||
|
||||
// IMPORTANT NOTE! NsProtoAgent must be listed _first_ here
|
||||
// (because we can't dynamic_cast install_data void* pointers)
|
||||
class NsNormAgent : public NormSimAgent, public NsProtoAgent
|
||||
{
|
||||
public:
|
||||
NsNormAgent();
|
||||
~NsNormAgent();
|
||||
|
||||
// NsProtoAgent base class overrides
|
||||
int command(int argc, const char*const* argv);
|
||||
unsigned long GetAgentId() {return (unsigned long)addr();}
|
||||
}; // end class NsNormAgent
|
||||
|
||||
|
||||
#endif // _NS_NORM_AGENT
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
|
||||
# This script describes and illustrates the commands and usage of the
|
||||
# NORM ns-2 simulation agent.
|
||||
|
||||
proc SimpleNORM {optionList} {
|
||||
|
||||
#Some default parameters for NORM
|
||||
set groupSize "10"
|
||||
set backoffFactor "4.0"
|
||||
set sendRate "64kb"
|
||||
set duration "120.0"
|
||||
set nsTracing 0
|
||||
|
||||
#Parse optionList for parameters
|
||||
set state flag
|
||||
foreach option $optionList {
|
||||
switch -- $state {
|
||||
"flag" {
|
||||
switch -glob -- $option {
|
||||
"gsize" {set state "gsize"}
|
||||
"rate" {set state "rate"}
|
||||
"backoff" {set state "backoff"}
|
||||
"duration" {set state "duration"}
|
||||
"trace" {set nsTracing 1}
|
||||
default {error "simplenorm: Bad option argument $option"}
|
||||
}
|
||||
|
||||
}
|
||||
"gsize" {
|
||||
set groupSize $option
|
||||
set state "flag"
|
||||
}
|
||||
"backoff" {
|
||||
set backoffFactor $option
|
||||
set state "flag"
|
||||
}
|
||||
"rate" {
|
||||
set sendRate $option
|
||||
set state "flag"
|
||||
}
|
||||
"duration" {
|
||||
set duration $option
|
||||
set state "flag"
|
||||
}
|
||||
default {
|
||||
error "simplenorm: Bad option parse state!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
# 1) An ns-2 simulator instance is created an configured
|
||||
# for multicast operation with a dense mode (DM) multicast
|
||||
# routing protocol:
|
||||
set ns_ [new Simulator -multicast on]
|
||||
$ns_ multicast
|
||||
|
||||
|
||||
# 2) Trace files are opened and ns and nam
|
||||
# tracing is enabled:
|
||||
if {$nsTracing} {
|
||||
set f [open simplenorm.tr w]
|
||||
$ns_ trace-all $f
|
||||
set nf [open simplenorm.nam w]
|
||||
$ns_ namtrace-all $nf
|
||||
} else {
|
||||
set f 0
|
||||
set nf 0
|
||||
}
|
||||
|
||||
set numNodes [expr $groupSize + 1]
|
||||
|
||||
# 3) A simple hub and spoke topology is created with
|
||||
# a NORM agent attached to each spoke node
|
||||
|
||||
# Node 0 is the hub of our hub & spoke topology
|
||||
# Note there is _not_ a NORM agent at the hub.
|
||||
set n(0) [$ns_ node]
|
||||
|
||||
puts "simplenorm: Creating $numNodes nodes with norm agents ..."
|
||||
for {set i 1} {$i <= $numNodes} {incr i} {
|
||||
set n($i) [$ns_ node]
|
||||
set norm($i) [new Agent/NORM]
|
||||
$ns_ attach-agent $n($i) $norm($i)
|
||||
}
|
||||
|
||||
puts "simplenorm: Creating spoke links ..."
|
||||
set linkRate [expr $groupSize * [bw_parse $sendRate]]
|
||||
for {set i 1} {$i <= $numNodes} {incr i} {
|
||||
$ns_ duplex-link $n(0) $n($i) $linkRate 100ms DropTail
|
||||
$ns_ queue-limit $n(0) $n($i) 100
|
||||
#$ns_ duplex-link-op $n(0) $n($i) orient right
|
||||
$ns_ duplex-link-op $n(0) $n($i) queuePos 0.5
|
||||
}
|
||||
|
||||
# 4) Configure multicast routing for topology
|
||||
set mproto DM
|
||||
set mrthandle [$ns_ mrtproto $mproto {}]
|
||||
if {$mrthandle != ""} {
|
||||
$mrthandle set_c_rp [list $n(0)]
|
||||
}
|
||||
|
||||
# 5) Allocate a multicast address to use
|
||||
set group [Node allocaddr]
|
||||
|
||||
puts "simplenorm: Configuring NORM agents ..."
|
||||
|
||||
# 6) Configure global NORM agent commands (using norm(1))
|
||||
$norm(1) debug 2
|
||||
$norm(1) log normLog.txt
|
||||
|
||||
# 7) Configure NORM server agent at node 1
|
||||
$norm(1) address $group/5000
|
||||
$norm(1) rate [bw_parse $sendRate]
|
||||
$norm(1) backoff $backoffFactor
|
||||
$norm(1) parity 0
|
||||
$norm(1) repeat -1
|
||||
$norm(1) interval 0.0
|
||||
$norm(1) txloss 10.0
|
||||
$norm(1) gsize $groupSize
|
||||
#$norm(1) trace
|
||||
|
||||
# 8) Configure NORM client agents at other nodes
|
||||
for {set i 2} {$i <= $numNodes} {incr i} {
|
||||
$norm($i) address $group/5000
|
||||
$norm($i) backoff $backoffFactor
|
||||
$norm($i) rxbuffer 100000
|
||||
$norm($i) txloss 10.0
|
||||
$norm($i) gsize $groupSize
|
||||
#$norm($i) trace
|
||||
}
|
||||
|
||||
# 9) Start server and clients
|
||||
$ns_ at 0.0 "$norm(1) start server"
|
||||
for {set i 2} {$i <= $numNodes} {incr i} {
|
||||
$ns_ at 0.0 "$norm($i) start client"
|
||||
}
|
||||
|
||||
$ns_ at 0.0 "$norm(1) sendFile 64000"
|
||||
|
||||
$ns_ at $duration "finish $ns_ $f $nf $nsTracing"
|
||||
|
||||
puts "simplenorm: Running simulation (gsize:$groupSize rate:$sendRate backoff $backoffFactor duration:$duration) ..."
|
||||
$ns_ run
|
||||
|
||||
}
|
||||
|
||||
proc finish {ns_ f nf nsTracing} {
|
||||
puts "simplenorm: Done."
|
||||
$ns_ flush-trace
|
||||
if {$nsTracing} {
|
||||
close $f
|
||||
close $nf
|
||||
}
|
||||
$ns_ halt
|
||||
delete $ns_
|
||||
}
|
||||
|
||||
# Run a set of trials with optional command-line parameters
|
||||
|
||||
#Usage:
|
||||
#ns simplenorm.tcl [gsize <count>][rate <bps>][backoff <k>][duration <sec>][trace]
|
||||
|
||||
puts "Running simplenorm: $argv"
|
||||
SimpleNORM $argv
|
||||
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h> // for PATH_MAX
|
||||
#include <string.h>
|
||||
|
||||
#define MIN(X,Y) ((X<Y)?X:Y)
|
||||
#define MAX(X,Y) ((X>Y)?X:Y)
|
||||
|
||||
const int MAX_LINE = 256;
|
||||
|
||||
class FastReader
|
||||
{
|
||||
public:
|
||||
FastReader();
|
||||
bool Read(FILE* filePtr, char* buffer, unsigned int* len);
|
||||
bool Readline(FILE* filePtr, char* buffer, unsigned int* len);
|
||||
|
||||
private:
|
||||
char savebuf[MAX_LINE];
|
||||
char* saveptr;
|
||||
unsigned int savecount;
|
||||
}; // end class FastReader
|
||||
|
||||
FastReader::FastReader()
|
||||
: savecount(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool FastReader::Read(FILE* filePtr, char* buffer, unsigned int* len)
|
||||
{
|
||||
unsigned int want = *len;
|
||||
if (savecount)
|
||||
{
|
||||
unsigned int ncopy = MIN(want, savecount);
|
||||
memcpy(buffer, saveptr, ncopy);
|
||||
savecount -= ncopy;
|
||||
saveptr += ncopy;
|
||||
buffer += ncopy;
|
||||
want -= ncopy;
|
||||
}
|
||||
while (want)
|
||||
{
|
||||
unsigned int result = fread(savebuf, sizeof(char), MAX_LINE, filePtr);
|
||||
if (result)
|
||||
{
|
||||
unsigned int ncopy= MIN(want, result);
|
||||
memcpy(buffer, savebuf, ncopy);
|
||||
savecount = result - ncopy;
|
||||
saveptr = savebuf + ncopy;
|
||||
buffer += ncopy;
|
||||
want -= ncopy;
|
||||
}
|
||||
else // end-of-file
|
||||
{
|
||||
*len -= want;
|
||||
if (*len)
|
||||
return true; // we read something
|
||||
else
|
||||
return false; // we read nothing
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} // end FastReader::Read()
|
||||
|
||||
// An OK text readline() routine (reads what will fit into buffer incl. NULL termination)
|
||||
// if *len is unchanged on return, it means the line is bigger than the buffer and
|
||||
// requires multiple reads
|
||||
|
||||
bool FastReader::Readline(FILE* filePtr, char* buffer, unsigned int* len)
|
||||
{
|
||||
unsigned int count = 0;
|
||||
unsigned int length = *len;
|
||||
char* ptr = buffer;
|
||||
unsigned int one = 1;
|
||||
while ((count < length) && Read(filePtr, ptr, &one))
|
||||
{
|
||||
if (('\n' == *ptr) || ('\r' == *ptr))
|
||||
{
|
||||
*ptr = '\0';
|
||||
*len = count;
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
ptr++;
|
||||
}
|
||||
// Either we've filled the buffer or hit end-of-file
|
||||
if (count < length) *len = 0; // Set *len = 0 on EOF
|
||||
return false;
|
||||
} // end FastReader::Readline()
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
FastReader reader;
|
||||
char buffer[MAX_LINE];
|
||||
unsigned long line = 0;
|
||||
unsigned int len = MAX_LINE;
|
||||
const char* header = "NormSession::ServerUpdateGroupSize";
|
||||
unsigned int headerLen = strlen(header);
|
||||
|
||||
double totalSize = 0.0;
|
||||
unsigned long sizeCount = 0;
|
||||
while (reader.Readline(stdin, buffer, &len))
|
||||
{
|
||||
line++;
|
||||
if (!strncmp(buffer, header, headerLen))
|
||||
{
|
||||
char* ptr = strstr(buffer, "size:");
|
||||
if (!ptr) break;
|
||||
double size;
|
||||
if (1 != sscanf(ptr, "size: %lf", &size))
|
||||
{
|
||||
fprintf(stderr, "sizeAve: Error parsing log file "
|
||||
"at line: %lu\n", line);
|
||||
exit(-1);
|
||||
}
|
||||
totalSize += size;
|
||||
sizeCount++;
|
||||
}
|
||||
len = MAX_LINE;
|
||||
} // end while (reader.Readline("Report" search))
|
||||
|
||||
double sizeAve = 0.0;
|
||||
if (sizeCount)
|
||||
{
|
||||
sizeAve = totalSize / (double)sizeCount;
|
||||
}
|
||||
|
||||
fprintf(stdout, "Average group size estimate:%lf\n", sizeAve);
|
||||
} // end main()
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/tclsh
|
||||
|
||||
# Runs ns simplenorm.tcl for Nack stats vs. group size
|
||||
|
||||
set sizeList {10 20 35 50 75 100 150 250 350 500 750 1000}
|
||||
|
||||
set rate 32kb
|
||||
set duration 120.0
|
||||
set backoff 4.0
|
||||
|
||||
set fileName "results.gp"
|
||||
|
||||
exec rm -f $fileName
|
||||
|
||||
# Gnuplot header
|
||||
set outFile [open $fileName "w"]
|
||||
puts $outFile "set title 'NACK Suppression Performance'"
|
||||
puts $outFile "set data style lines"
|
||||
puts $outFile "set xlabel 'Number of Receivers'"
|
||||
puts $outFile "set ylabel 'NACK Transmission Fraction'"
|
||||
puts $outFile "plot \\"
|
||||
puts $outFile "'-' using 1:2 \\"
|
||||
puts $outFile "'Receivers:%lf alpha:%lf' t 'Theoretical Multicast Nacking', \\"
|
||||
puts $outFile "'-' using 1:2 \\"
|
||||
puts $outFile "'Receivers:%lf Sent:%lf Suppressed:%lf alpha:%lf' t 'Measured Multicast Nacking'\n"
|
||||
close $outFile
|
||||
|
||||
puts "Computing theoretical results ..."
|
||||
# T = number of GRTT for NACK backoff timers ...
|
||||
set T $backoff
|
||||
|
||||
# Theoretical Multicast NACK results
|
||||
set outFile [open $fileName "a"]
|
||||
puts $outFile "#Theoretical Multicast NACK Transmission Fraction"
|
||||
foreach groupSize $sizeList {
|
||||
set lambda [expr log($groupSize) + 1.0]
|
||||
set N [expr exp((1.2/(2.0 * $T)) * ($lambda))]
|
||||
set alpha [expr $N / $groupSize]
|
||||
puts $outFile "Receivers:$groupSize alpha:$alpha"
|
||||
}
|
||||
puts $outFile "e\n"
|
||||
close $outFile
|
||||
|
||||
# Measured Multicast NACK results
|
||||
set outFile [open $fileName "a"]
|
||||
puts $outFile "#Measured Multicast NACK Transmission Fraction"
|
||||
close $outFile
|
||||
foreach groupSize $sizeList {
|
||||
puts "Starting simulation run for groupSize:$groupSize ..."
|
||||
puts " ns simplenorm.tcl gsize $groupSize backoff $backoff rate $rate duration $duration"
|
||||
catch {eval exec ns simplenorm.tcl gsize $groupSize backoff $backoff rate $rate duration $duration}
|
||||
puts " cat normLog.txt | nc >> $fileName"
|
||||
catch {eval exec cat normLog.txt | nc >> $fileName}
|
||||
}
|
||||
puts "Finished."
|
||||
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
SHELL=/bin/sh
|
||||
|
||||
.SUFFIXES: .cpp $(.SUFFIXES)
|
||||
.SUFFIXES: .cpp -sim.o $(.SUFFIXES)
|
||||
|
||||
PROTOLIB = ../protolib
|
||||
COMMON = ../common
|
||||
UNIX = ./
|
||||
UNIX = ../unix
|
||||
NS = ../ns
|
||||
|
||||
INCLUDES = $(TCL_INCL_PATH) $(SYSTEM_INCLUDES) -I$(UNIX) -I$(COMMON) -I$(PROTOLIB)/common
|
||||
|
|
@ -54,26 +54,37 @@ $(PROTOLIB)/unix/libProto.a:
|
|||
NORM_SRC = $(COMMON)/normMessage.cpp $(COMMON)/normSession.cpp \
|
||||
$(COMMON)/normNode.cpp $(COMMON)/normObject.cpp \
|
||||
$(COMMON)/normSegment.cpp $(COMMON)/normBitmask.cpp \
|
||||
$(COMMON)/normEncoder.cpp $(COMMON)/galois.cpp
|
||||
$(COMMON)/normEncoder.cpp $(COMMON)/galois.cpp \
|
||||
$(COMMON)/normFile.cpp
|
||||
|
||||
NORM_OBJ = $(NORM_SRC:.cpp=.o)
|
||||
|
||||
LIB_SRC = $(NORM_SRC)
|
||||
LIB_OBJ = $(LIB_SRC:.cpp=.o)
|
||||
|
||||
libNorm.a: $(LIB_OBJ)
|
||||
libnorm.a: $(LIB_OBJ)
|
||||
ar rcv $@ $(LIB_OBJ)
|
||||
ranlib $@
|
||||
|
||||
.cpp-sim.o:
|
||||
$(CC) -c $(CFLAGS) -DSIMULATE -o $*-sim.o $*.cpp
|
||||
|
||||
SIM_SRC = $(NORM_SRC) $(COMMON)/normSimAgent.cpp
|
||||
SIM_OBJ = $(SIM_SRC:.cpp=-sim.o)
|
||||
|
||||
libnormSim.a: $(SIM_OBJ)
|
||||
ar rcv $@ $(SIM_OBJ)
|
||||
ranlib $@
|
||||
|
||||
# (mdp) command-line file broadcaster/receiver
|
||||
APP_SRC = $(COMMON)/normApp.cpp
|
||||
APP_OBJ = $(APP_SRC:.cpp=.o)
|
||||
|
||||
norm: $(APP_OBJ) libNorm.a $(LIBPROTO)
|
||||
$(CC) $(CFLAGS) -o $@ $(APP_OBJ) $(LDFLAGS) $(LIBS) libNorm.a $(LIBPROTO)
|
||||
norm: $(APP_OBJ) libnorm.a $(LIBPROTO)
|
||||
$(CC) $(CFLAGS) -o $@ $(APP_OBJ) $(LDFLAGS) $(LIBS) libnorm.a $(LIBPROTO)
|
||||
|
||||
clean:
|
||||
rm -f $(COMMON)/*.o $(UNIX)/*.o $(UNIX)/libNorm.a $(UNIX)/norm;
|
||||
rm -f $(COMMON)/*.o $(UNIX)/*.o $(UNIX)/libnorm.a $(UNIX)/norm $(NS)/*.o;
|
||||
make -C $(PROTOLIB)/unix -f Makefile.common clean
|
||||
|
||||
# DO NOT DELETE THIS LINE -- mkdep uses it.
|
||||
|
|
|
|||
Loading…
Reference in New Issue