pull/2/head
Jeff Weston 2019-09-11 11:22:31 -04:00
parent 565afa5529
commit 3456db14f2
33 changed files with 7983 additions and 897 deletions

View File

@ -1,43 +1,10 @@
NORM PRELIMINARY CODE RELEASE NORM PRELIMINARY CODE RELEASE
These directories contain source code for a very preliminary The NORM code here is still preliminary but a functional "norm"
implementation of the Nack Oriented Reliable Multicast (NORM) command-line application can be built which can send and receive
protocol. Currently, a very dumb "norm" command-line application a set of files _or_ a stream piped to/from stdin/stdout. The command-
(which currently takes no command-line arguments) is line options are not yet fully documented.
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 current code only has Makefiles for Unix platforms, but the The current code only has Makefiles for Unix platforms, but the
code could be assembled as Win32 project under VC++. code could be assembled as Win32 project under VC++.

View File

@ -34,7 +34,7 @@
#include "galois.h" #include "galois.h"
/* Galois field inversion table */ /* 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, 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, 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[] }; // end GINV[]
// Galois field exponent table // 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, 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, 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[] }; // end GEXP[]
// Galois field multiplication table // 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,
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} 0x8e, 0x71, 0x6d, 0x92, 0x55, 0xaa, 0xb6, 0x49, 0x25, 0xda, 0xc6, 0x39, 0xfe, 0x01, 0x1d, 0xe2}
}; // end GMULT[] }; // end GMULT[]

View File

@ -30,15 +30,16 @@
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
********************************************************************/ ********************************************************************/
#ifndef _GALOIS #ifndef _NORM_GALOIS
#define _GALOIS #define _NORM_GALOIS
namespace Norm
{
extern const unsigned char GINV[256]; extern const unsigned char GINV[256];
extern const unsigned char GEXP[512]; extern const unsigned char GEXP[512];
extern const unsigned char GMULT[256][256]; extern const unsigned char GMULT[256][256];
}
inline unsigned char gexp(unsigned int x) {return GEXP[x];} inline unsigned char gexp(unsigned int x) {return Norm::GEXP[x];}
inline unsigned char gmult(unsigned int x, unsigned int y) {return GMULT[x][y];} inline unsigned char gmult(unsigned int x, unsigned int y) {return Norm::GMULT[x][y];}
inline unsigned char ginv(unsigned int x) {return GINV[x];} inline unsigned char ginv(unsigned int x) {return Norm::GINV[x];}
#endif // _NORM_GALOIS
#endif // _GALOIS

View File

@ -6,7 +6,10 @@
#include <stdio.h> // for stdout/stderr printouts #include <stdio.h> // for stdout/stderr printouts
#include <signal.h> // for SIGTERM/SIGINT handling #include <signal.h> // for SIGTERM/SIGINT handling
#include <errno.h>
// Command-line application using Protolib EventDispatcher
class NormApp : public NormController class NormApp : public NormController
{ {
public: public:
@ -16,14 +19,25 @@ class NormApp : public NormController
int MainLoop() {return dispatcher.Run();} int MainLoop() {return dispatcher.Run();}
void Stop(int exitCode) {dispatcher.Stop(exitCode);} void Stop(int exitCode) {dispatcher.Stop(exitCode);}
void OnShutdown(); void OnShutdown();
bool ProcessCommand(const char* cmd, const char* val);
bool ParseCommandLine(int argc, char* argv[]);
private: private:
enum CmdType {CMD_INVALID, CMD_NOARG, CMD_ARG};
CmdType CommandType(const char* cmd);
virtual void Notify(NormController::Event event, virtual void Notify(NormController::Event event,
class NormSessionMgr* sessionMgr, class NormSessionMgr* sessionMgr,
class NormSession* session, class NormSession* session,
class NormServerNode* server, class NormServerNode* server,
class NormObject* object); class NormObject* object);
void InstallTimer(ProtocolTimer* timer)
{dispatcher.InstallTimer(timer);}
bool OnIntervalTimeout();
static const char* const cmd_list[];
static void SignalHandler(int sigNum); static void SignalHandler(int sigNum);
@ -31,9 +45,396 @@ class NormApp : public NormController
NormSessionMgr session_mgr; NormSessionMgr session_mgr;
NormSession* session; NormSession* session;
NormStreamObject* stream; 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 }; // 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, void NormApp::Notify(NormController::Event event,
class NormSessionMgr* sessionMgr, class NormSessionMgr* sessionMgr,
@ -44,85 +445,286 @@ void NormApp::Notify(NormController::Event event,
switch (event) switch (event)
{ {
case TX_QUEUE_EMPTY: case TX_QUEUE_EMPTY:
//TRACE("NormApp::Notify(TX_QUEUE_EMPTY) ...\n"); // Write to stream as needed
if (object == stream) //DMSG(0, "NormApp::Notify(TX_QUEUE_EMPTY) ...\n");
{ if (object && (object == stream))
char text[256]; {
memset(text, 'a', 256); bool flush = false;
stream->Write(text, 256); if (input)
} {
break; if (input_index >= input_length)
{
size_t result = fread(input_buffer, sizeof(char), 512, input);
if (result)
{
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;
}
}
}
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: case RX_OBJECT_NEW:
{
//TRACE("NormApp::Notify(RX_OBJECT_NEW) ...\n"); //TRACE("NormApp::Notify(RX_OBJECT_NEW) ...\n");
// It's up to the app to "accept" the object
switch (object->GetType())
{ {
switch (object->GetType()) case NormObject::STREAM:
{ {
case NormObject::STREAM: const NormObjectSize& size = object->Size();
if (!((NormStreamObject*)object)->Accept(size.LSB()))
{ {
const NormObjectSize& size = object->Size(); DMSG(0, "NormApp::Notify(RX_OBJECT_NEW) stream object accept error!\n");
if (!((NormStreamObject*)object)->Accept(size.LSB())) }
}
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)
{ {
TRACE("stream object accept error!\n"); 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");
} }
} }
break; else
case NormObject::FILE: {
case NormObject::DATA: DMSG(0, "NormApp::Notify(RX_OBJECT_NEW) no rx cache for file\n");
TRACE("NormApp::Notify() FILE/DATA objects not supported...\n"); }
break; }
} 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; break;
case RX_OBJECT_UPDATE: case RX_OBJECT_UPDATE:
//TRACE("NormApp::Notify(RX_OBJECT_UPDATE) ...\n"); //TRACE("NormApp::Notify(RX_OBJECT_UPDATE) ...\n");
switch (object->GetType()) switch (object->GetType())
{ {
case NormObject::FILE:
// (TBD) update progress
break;
case NormObject::STREAM: case NormObject::STREAM:
{ {
// Read the stream // Read the stream when it's updated
char buffer[2048]; ASSERT(output);
char buffer[256];
unsigned int nBytes; 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"); put += result;
break;
} }
} else
buffer[32] = '\0'; {
DMSG(0, "NormApp::Notify() stream read %u bytes: \"%s\"\n", nBytes, buffer); 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: 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;
} }
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);
} }
} else if (tx_repeat_count)
{
NormApp::NormApp() // (TBD) When repeating, remove previous instance from tx queue???
: session(NULL), stream(NULL) if (tx_repeat_count > 0) tx_repeat_count--;
{ tx_file_list.ResetIterator();
// Init tx_timer for 1.0 second interval, infinite repeats if (tx_repeat_interval > tx_object_interval)
session_mgr.Init(EventDispatcher::TimerInstaller, &dispatcher, {
EventDispatcher::SocketInstaller, &dispatcher, if (interval_timer.IsActive()) interval_timer.Deactivate();
this); interval_timer.SetInterval(tx_repeat_interval = tx_object_interval);
} InstallTimer(&interval_timer);
return false;
NormApp::~NormApp() }
{ else
{
} return OnIntervalTimeout();
}
}
else
{
DMSG(0, "norm: End of tx file list reached.\n");
}
return true;
} // end NormApp::OnIntervalTimeout()
bool NormApp::OnStartup() bool NormApp::OnStartup()
{ {
@ -137,34 +739,69 @@ bool NormApp::OnStartup()
signal(SIGTERM, SignalHandler); signal(SIGTERM, SignalHandler);
signal(SIGINT, 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)
{ {
if (!session->StartServer(1024*1024, 256, 20, 8)) // Common session parameters
{ session->SetTxRate(tx_rate);
DMSG(0, "NormApp::OnStartup() start server error!\n"); session->SetTrace(tracing);
session_mgr.Destroy(); session->SetTxLoss(tx_loss);
return false; session->SetRxLoss(rx_loss);
}
session->ServerSetAutoParity(4);
//session->SetLoopback(true);
// Open a stream object to write to if (input || !tx_file_list.IsEmpty())
stream = session->QueueTxStream(10*1024);
if (!stream)
{ {
DMSG(0, "NormApp::OnStartup() queue tx stream error!\n"); // StartServer(bufferMax, segmentSize, fec_ndata, fec_nparity)
session_mgr.Destroy(); if (!session->StartServer(tx_buffer_size, segment_size, ndata, nparity))
return false; {
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; return true;
} }
else else
{ {
DMSG(0, "NormApp::OnStartup() new session error!\n");
return false; return false;
} }
} // end NormApp::OnStartup() } // end NormApp::OnStartup()
@ -172,6 +809,10 @@ bool NormApp::OnStartup()
void NormApp::OnShutdown() void NormApp::OnShutdown()
{ {
session_mgr.Destroy(); session_mgr.Destroy();
if (input) fclose(input);
input = NULL;
if (output) fclose(output);
output = NULL;
} // end NormApp::OnShutdown() } // end NormApp::OnShutdown()
@ -180,14 +821,18 @@ NormApp theApp;
// Use "main()" for UNIX and WIN32 console apps, // Use "main()" for UNIX and WIN32 console apps,
// "WinMain()" for non-console WIN32 // "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) 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 #else
int main(int argc, char* argv[]) int main(int argc, char* argv[])
#endif
{ {
#endif
#ifdef WIN32 #ifdef WIN32
// Hack to determine if Win32 console application was launched // Hack to determine if Win32 console application was launched
// independently or from a pre-existing console window // independently or from a pre-existing console window
@ -207,16 +852,29 @@ int main(int argc, char* argv[])
OpenDebugWindow(); OpenDebugWindow();
pauseForUser = true; pauseForUser = true;
} }
#endif // WIN32 #endif // WIN32
int exitCode = 0; int exitCode = 0;
if (theApp.OnStartup()) if (theApp.ParseCommandLine(argc, argv))
{ {
exitCode = theApp.MainLoop(); if (theApp.OnStartup())
theApp.OnShutdown(); {
fprintf(stderr, "norm: Done.\n"); 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 #ifdef WIN32
// If Win32 console is going to disappear, pause for user before exiting // If Win32 console is going to disappear, pause for user before exiting
if (pauseForUser) if (pauseForUser)
@ -224,13 +882,7 @@ int main(int argc, char* argv[])
printf ("Program Finished - Hit <Enter> to exit"); printf ("Program Finished - Hit <Enter> to exit");
getchar(); getchar();
} }
#endif // WIN32 #endif // WIN32
}
else
{
fprintf(stderr, "norm: Error initializing application!\n");
return -1;
}
return exitCode; // exitCode contains "signum" causing exit return exitCode; // exitCode contains "signum" causing exit
} // end main(); } // end main();
@ -240,7 +892,7 @@ void NormApp::SignalHandler(int sigNum)
{ {
case SIGTERM: case SIGTERM:
case SIGINT: case SIGINT:
theApp.Stop(sigNum); // causes theApp's main loop to exit theApp.Stop(sigNum); // causes theApp's main loop to exit
break; break;
default: default:

View File

@ -1,5 +1,7 @@
#include "normBitmask.h" #include "normBitmask.h"
#include "debug.h"
#include "sysdefs.h"
// Hamming weights for given one-byte bit masks // Hamming weights for given one-byte bit masks
static unsigned char WEIGHT[256] = static unsigned char WEIGHT[256] =
@ -377,7 +379,9 @@ bool NormBitmask::XCopy(const NormBitmask& b)
{ {
if (b.num_bits > num_bits) return false; if (b.num_bits > num_bits) return false;
unsigned int len = b.mask_len; 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]; mask[i] = b.mask[i] & ~mask[i];
if (len < mask_len) memset(&mask[len], 0, mask_len - len); if (len < mask_len) memset(&mask[len], 0, mask_len - len);
if (b.first_set < first_set) if (b.first_set < first_set)
@ -601,6 +605,7 @@ bool NormSlidingMask::Unset(unsigned long index)
mask[(pos >> 3)] &= ~(0x80 >> (pos & 0x07)); mask[(pos >> 3)] &= ~(0x80 >> (pos & 0x07));
if (start == end) if (start == end)
{ {
ASSERT(pos == start);
start = end = num_bits; start = end = num_bits;
return true; return true;
} }
@ -1133,7 +1138,7 @@ bool NormSlidingMask::Add(const NormSlidingMask& b)
} // end NormSlidingMask::Add() } // end NormSlidingMask::Add()
// A sort of logical subtraction "this = (this & ~b)" // 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) bool NormSlidingMask::Subtract(const NormSlidingMask& b)
{ {
if (b.IsSet()) if (b.IsSet())
@ -1150,11 +1155,15 @@ bool NormSlidingMask::Subtract(const NormSlidingMask& b)
} }
} }
} }
else
{
Copy(b);
}
return true; return true;
} // end NormSlidingMask::Subtract() } // end NormSlidingMask::Subtract()
// A sort of logical subtraction "this = (~this & b)" // 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) bool NormSlidingMask::XCopy(const NormSlidingMask& b)
{ {
if (b.IsSet()) if (b.IsSet())
@ -1174,6 +1183,10 @@ bool NormSlidingMask::XCopy(const NormSlidingMask& b)
Set(index); Set(index);
index++; index++;
} }
}
else
{
Copy(b);
} }
} }
return true; return true;
@ -1232,3 +1245,18 @@ void NormSlidingMask::Display(FILE* stream)
if (0x3f == (i & 0x3f)) fprintf(stream, "\n"); if (0x3f == (i & 0x3f)) fprintf(stream, "\n");
} }
} // end NormSlidingMask::Display() } // 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()

View File

@ -128,6 +128,7 @@ class NormSlidingMask
} }
void Reset(unsigned long index = 0) void Reset(unsigned long index = 0)
{ {
ASSERT(num_bits);
memset(mask, 0xff, mask_len); memset(mask, 0xff, mask_len);
mask[mask_len-1] = 0x00ff << ((8 - (num_bits & 0x07)) & 0x07); mask[mask_len-1] = 0x00ff << ((8 - (num_bits & 0x07)) & 0x07);
start = 0; start = 0;
@ -171,6 +172,7 @@ class NormSlidingMask
bool Xor(const NormSlidingMask & b); // this = this ^ b bool Xor(const NormSlidingMask & b); // this = this ^ b
void Display(FILE* stream); void Display(FILE* stream);
void Debug(long theCount);
private: private:
unsigned char* mask; unsigned char* mask;

986
common/normFile.cpp Normal file
View File

@ -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()

196
common/normFile.h Normal file
View File

@ -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

View File

@ -1,7 +1,5 @@
#include "normMessage.h" #include "normMessage.h"
#include "debug.h"
NormRepairRequest::NormRepairRequest() NormRepairRequest::NormRepairRequest()
: form(INVALID), flags(0), length(0), buffer(NULL), buffer_len(0) : form(INVALID), flags(0), length(0), buffer(NULL), buffer_len(0)
{ {
@ -11,7 +9,13 @@ bool NormRepairRequest::AppendRepairItem(const NormObjectId& objectId,
const NormBlockId& blockId, const NormBlockId& blockId,
UINT16 symbolId) 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); UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+CONTENT_OFFSET+length, &temp16, 2); memcpy(buffer+CONTENT_OFFSET+length, &temp16, 2);
@ -35,7 +39,10 @@ bool NormRepairRequest::AppendRepairRange(const NormObjectId& startObjectId,
const NormBlockId& endBlockId, const NormBlockId& endBlockId,
UINT16 endSymbolId) 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 // range start
UINT16 temp16; UINT16 temp16;
@ -257,6 +264,28 @@ NormMessage* NormMessageQueue::RemoveTail()
} }
} // end 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 bool NormObjectSize::operator<(const NormObjectSize& size) const
{ {
UINT16 diff = msb - size.msb; UINT16 diff = msb - size.msb;
@ -314,16 +343,15 @@ NormObjectSize NormObjectSize::operator*(const NormObjectSize& b) const
return result; return result;
} // end NormObjectSize::operator*(NormObjectSize size) } // end NormObjectSize::operator*(NormObjectSize size)
// Note: This always rounds up if there is _any_ remainder
NormObjectSize NormObjectSize::operator/(const NormObjectSize& b) const NormObjectSize NormObjectSize::operator/(const NormObjectSize& b) const
{ {
// Zero dividend is special case // Zero dividend is special case
if ((0 == lsb) && (0 == msb)) return NormObjectSize(0, 0); if ((0 == lsb) && (0 == msb)) return NormObjectSize(0, 0);
// Zero divisor is special case // Zero divisor is special case
if ((0 == b.lsb) && (0 == b.msb)) return NormObjectSize(0xffff, 0xffffffff); if ((0 == b.lsb) && (0 == b.msb)) return NormObjectSize(0xffff, 0xffffffff);
// Dividend equals divisor is special case // Divisor >= dividend is special case
if (*this == b) return NormObjectSize(0,1); 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 // divisor
UINT32 divisor[2]; UINT32 divisor[2];
divisor[0] = (UINT32)b.msb; divisor[0] = (UINT32)b.msb;

View File

@ -2,12 +2,13 @@
#define _NORM_MESSAGE #define _NORM_MESSAGE
// PROTOLIB includes // PROTOLIB includes
#include <networkAddress.h> // for NetworkAddress class #include "networkAddress.h" // for NetworkAddress class
#include <sysdefs.h> // for UINT typedefs #include "sysdefs.h" // for UINT typedefs
#include <debug.h> #include "debug.h"
// standard includes // standard includes
#include <string.h> // for memcpy(), etc #include <string.h> // for memcpy(), etc
#include <math.h>
// (TBD) Alot of the "memcpy()" calls could be eliminated by // (TBD) Alot of the "memcpy()" calls could be eliminated by
// taking advantage of the alignment of NORM messsages // taking advantage of the alignment of NORM messsages
@ -15,6 +16,32 @@
const UINT16 NORM_MSG_SIZE_MAX = 8192; const UINT16 NORM_MSG_SIZE_MAX = 8192;
const int NORM_ROBUST_FACTOR = 20; // default robust factor 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" // This class is used to describe object "size" and/or "offset"
class NormObjectSize class NormObjectSize
{ {
@ -139,9 +166,8 @@ class NormMsg
UINT32 temp32 = htonl(sender); UINT32 temp32 = htonl(sender);
memcpy(buffer+SENDER_OFFSET, &temp32, 4); memcpy(buffer+SENDER_OFFSET, &temp32, 4);
} }
void SetDestination(const NetworkAddress& dst) {addr = dst;}
void SetDestination(const NetworkAddress& dest) void SetLength(UINT16 len) {length = len;}
{addr = dest;}
// Message processing routines // Message processing routines
UINT8 GetVersion() const {return buffer[VERSION_OFFSET];} UINT8 GetVersion() const {return buffer[VERSION_OFFSET];}
@ -158,13 +184,14 @@ class NormMsg
memcpy(&temp32, buffer+SENDER_OFFSET, 4); memcpy(&temp32, buffer+SENDER_OFFSET, 4);
return (ntohl(temp32)); return (ntohl(temp32));
} }
const NetworkAddress& GetDestination() {return addr;}
const NetworkAddress& GetSource() {return addr;}
UINT16 GetLength() {return length;}
// For message buffer transmission/reception and misc. // For message buffer transmission/reception and misc.
NetworkAddress* Src() {return &addr;} char* AccessBuffer() {return buffer;}
NetworkAddress* Dest() {return &addr;} UINT16 AccessLength() const {return length;}
char* GetBuffer() {return buffer;} NetworkAddress& AccessAddress() {return addr;}
UINT16 GetLength() const {return length;}
void SetLength(UINT16 len) {length = len;}
protected: protected:
@ -230,9 +257,9 @@ class NormObjectMsg : public NormMsg
} }
UINT16 GetFecBlockLen() const UINT16 GetFecBlockLen() const
{ {
UINT16 nparity; UINT16 ndata;
memcpy(&nparity, buffer+FEC_NDATA_OFFSET, 2); memcpy(&ndata, buffer+FEC_NDATA_OFFSET, 2);
return (ntohs(nparity)); return (ntohs(ndata));
} }
NormObjectId GetObjectId() const NormObjectId GetObjectId() const
{ {
@ -242,7 +269,7 @@ class NormObjectMsg : public NormMsg
} }
// Message building routines // Message building routines
void ResetFlags() {buffer[FLAGS_OFFSET] = 0;}
void SetFlag(NormObjectMsg::Flag flag) void SetFlag(NormObjectMsg::Flag flag)
{buffer[FLAGS_OFFSET] |= flag;} {buffer[FLAGS_OFFSET] |= flag;}
void SetGrtt(UINT8 grtt) {buffer[GRTT_OFFSET] = grtt;} void SetGrtt(UINT8 grtt) {buffer[GRTT_OFFSET] = grtt;}
@ -370,10 +397,10 @@ class NormDataMsg : public NormObjectMsg
} }
bool IsData() const {return (GetFecSymbolId() < GetFecBlockLen());} bool IsData() const {return (GetFecSymbolId() < GetFecBlockLen());}
const char* GetData() {return (buffer + DATA_OFFSET);} 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);} 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());} bool IsParity() const {return (GetFecSymbolId() >= GetFecBlockLen());}
// (Note: "payload_len" and "offset" field spaces are in the FEC payload) // (Note: "payload_len" and "offset" field spaces are in the FEC payload)
@ -447,9 +474,9 @@ class NormCmdMsg : public NormMsg
{buffer[FLAVOR_OFFSET] = flavor;} {buffer[FLAVOR_OFFSET] = flavor;}
// Message processing // Message processing
UINT8 GetGrtt() {return buffer[GRTT_OFFSET];} UINT8 GetGrtt() const {return buffer[GRTT_OFFSET];}
UINT8 GetGroupSize() {return buffer[GSIZE_OFFSET];} UINT8 GetGroupSize() const {return buffer[GSIZE_OFFSET];}
NormCmdMsg::Flavor GetFlavor() {return (Flavor)buffer[FLAVOR_OFFSET];} NormCmdMsg::Flavor GetFlavor() const {return (Flavor)buffer[FLAVOR_OFFSET];}
protected: protected:
enum enum
@ -466,6 +493,11 @@ class NormCmdFlushMsg : public NormCmdMsg
enum Flag {NORM_FLUSH_FLAG_EOT = 0x01}; enum Flag {NORM_FLUSH_FLAG_EOT = 0x01};
// Message building // Message building
void Reset()
{
buffer[FLAGS_OFFSET] = 0;
length = SYMBOL_ID_OFFSET + 2;
}
void SetFlag(NormCmdFlushMsg::Flag flag) void SetFlag(NormCmdFlushMsg::Flag flag)
{buffer[FLAGS_OFFSET] |= flag;} {buffer[FLAGS_OFFSET] |= flag;}
void UnsetFlag(NormCmdFlushMsg::Flag flag) void UnsetFlag(NormCmdFlushMsg::Flag flag)
@ -542,12 +574,13 @@ class NormCmdSquelchMsg : public NormCmdMsg
} }
void ResetInvalidObjectList() {length = OBJECT_LIST_OFFSET;} 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); UINT16 temp16 = htons((UINT16)objectId);
memcpy(buffer+length, &temp16, 2); memcpy(buffer+length, &temp16, 2);
length += 2; length += 2;
return length; return true;
} }
// Message processing // Message processing
@ -716,8 +749,10 @@ class NormCmdApplicationMsg : public NormCmdMsg
class NormRepairRequest class NormRepairRequest
{ {
friend class NormRepairRequest::Iterator;
public: public:
class Iterator;
friend class NormRepairRequest::Iterator;
enum Form enum Form
{ {
INVALID, INVALID,
@ -748,8 +783,9 @@ class NormRepairRequest
// Repair request building // Repair request building
void SetForm(NormRepairRequest::Form theForm) {form = theForm;} void SetForm(NormRepairRequest::Form theForm) {form = theForm;}
void ResetFlags() {flags = 0;}
void SetFlag(NormRepairRequest::Flag theFlag) {flags |= theFlag;} 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;} void UnsetFlag(NormRepairRequest::Flag theFlag) {flags &= ~theFlag;}
@ -917,7 +953,7 @@ class NormReportMsg : public NormMsg
// do some unions so we can easily use these // do some unions so we can easily use these
// via casting or dereferencing the union members // via casting or dereferencing the union members
class NormCommandMessage class NormCommandMsg
{ {
public: public:
union union
@ -930,7 +966,7 @@ class NormCommandMessage
NormCmdCCMsg cc; NormCmdCCMsg cc;
NormCmdApplicationMsg app; NormCmdApplicationMsg app;
}; };
}; // end class NormCommandMessage }; // end class NormCommandMsg
class NormMessage class NormMessage
{ {
@ -942,7 +978,7 @@ class NormMessage
NormObjectMsg object; NormObjectMsg object;
NormInfoMsg info; NormInfoMsg info;
NormDataMsg data; NormDataMsg data;
NormCommandMessage cmd; NormCommandMsg cmd;
NormNackMsg nack; NormNackMsg nack;
NormAckMsg ack; NormAckMsg ack;
NormReportMsg report; NormReportMsg report;

File diff suppressed because it is too large Load Diff

View File

@ -46,16 +46,19 @@ class NormServerNode : public NormNode
NormServerNode(class NormSession* theSession, NormNodeId nodeId); NormServerNode(class NormSession* theSession, NormNodeId nodeId);
~NormServerNode(); ~NormServerNode();
void HandleCommand(NormCommandMsg& cmd);
void HandleObjectMessage(NormMessage& msg); void HandleObjectMessage(NormMessage& msg);
void HandleNackMessage(NormNackMsg& nack);
bool Open(UINT16 segmentSize, UINT16 numData, UINT16 numParity); bool Open(UINT16 segmentSize, UINT16 numData, UINT16 numParity);
void Close(); void Close();
bool IsOpen() const {return is_open;} bool IsOpen() const {return is_open;}
bool SyncTest(const NormMessage& msg) const; bool SyncTest(const NormMessage& msg) const;
void Sync(NormObjectId objectId); void Sync(NormObjectId objectId);
ObjectStatus GetObjectStatus(NormObjectId objectId) const; ObjectStatus UpdateSyncStatus(const NormObjectId& objectId);
void SetPending(NormObjectId objectId); void SetPending(NormObjectId objectId);
ObjectStatus GetObjectStatus(const NormObjectId& objectId) const;
void DeleteObject(NormObject* obj); void DeleteObject(NormObject* obj);
@ -77,7 +80,7 @@ class NormServerNode : public NormNode
void SetErasureLoc(UINT16 index, UINT16 value) void SetErasureLoc(UINT16 index, UINT16 value)
{ {
ASSERT(index < (nparity)); ASSERT(index < nparity);
erasure_loc[index] = value; erasure_loc[index] = value;
} }
UINT16 GetErasureLoc(UINT16 index) UINT16 GetErasureLoc(UINT16 index)
@ -87,7 +90,28 @@ class NormServerNode : public NormNode
return decoder.Decode(segmentList, numData, erasureCount, erasure_loc); 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: private:
void RepairCheck(NormObject::CheckLevel checkLevel, void RepairCheck(NormObject::CheckLevel checkLevel,
NormObjectId objectId, NormObjectId objectId,
@ -116,6 +140,22 @@ class NormServerNode : public NormNode
ProtocolTimer repair_timer; ProtocolTimer repair_timer;
NormObjectId current_object_id; // index for repair 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 }; // end class NodeServerNode

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,8 @@
#define _NORM_OBJECT #define _NORM_OBJECT
#include "normSegment.h" // NORM segmentation classes #include "normSegment.h" // NORM segmentation classes
#include "normEncoder.h"
#include "normFile.h"
#include <stdio.h> #include <stdio.h>
@ -30,9 +32,12 @@ class NormObject
virtual ~NormObject(); virtual ~NormObject();
NormObject::Type GetType() const {return type;} NormObject::Type GetType() const {return type;}
const NormObjectId& Id() const {return id;} 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);}
bool IsStream() {return (STREAM == type);} const char* GetInfo() const {return info;}
UINT16 InfoLength() const {return info_len;}
bool IsStream() const {return (STREAM == type);}
NormNodeId LocalNodeId() const; NormNodeId LocalNodeId() const;
// Opens (inits) object for tx operation // Opens (inits) object for tx operation
@ -47,30 +52,66 @@ class NormObject
virtual bool WriteSegment(NormBlockId blockId, virtual bool WriteSegment(NormBlockId blockId,
NormSegmentId segmentId, NormSegmentId segmentId,
const char* buffer) = 0; const char* buffer) = 0;
virtual bool ReadSegment(NormBlockId blockId,
virtual UINT16 ReadSegment(NormBlockId blockId, NormSegmentId segmentId, NormSegmentId segmentId,
NormObjectSize* offset, char* buffer, UINT16 maxlen) = 0; char* buffer) = 0;
// These are only valid after object is open // These are only valid after object is open
NormBlockId LastBlockId(); NormBlockId LastBlockId() const {return last_block_id;}
NormSegmentId LastSegmentId(); NormSegmentId LastBlockSize() const {return last_block_size;}
NormSegmentId LastSegmentId(NormBlockId blockId); 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 bool IsPending(bool flush = true) const;
// flush == false !!! bool IsRepairPending() const;
bool IsPending(bool flush = true) bool IsPendingInfo() {return pending_info;}
{return (pending_info || pending_mask.IsSet());} NormBlockId FirstPending() {return pending_mask.FirstSet();}
// Methods available to server for transmission // Methods available to server for transmission
bool NextServerMsg(NormMessage* msg); 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 // 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 // Methods available to client for reception
bool Accepted() {return accepted;} bool Accepted() {return accepted;}
void HandleObjectMessage(NormMessage& msg); void HandleObjectMessage(NormMessage& msg,
NormMsgType msgType,
NormBlockId blockId,
NormSegmentId segmentId);
bool StreamUpdateStatus(NormBlockId blockId); bool StreamUpdateStatus(NormBlockId blockId);
// Used by remote server node for resource management scheme // Used by remote server node for resource management scheme
@ -82,7 +123,15 @@ class NormObject
NormSegmentId segmentId, NormSegmentId segmentId,
bool timerActive); bool timerActive);
bool IsRepairPending(bool flush); 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: protected:
NormObject(NormObject::Type theType, NormObject(NormObject::Type theType,
@ -109,7 +158,8 @@ class NormObject
NormBlockId current_block_id; NormBlockId current_block_id;
NormSegmentId next_segment_id; NormSegmentId next_segment_id;
NormBlockId last_block_id; NormBlockId last_block_id;
NormSegmentId last_segment_id; NormSegmentId last_block_size;
UINT16 last_segment_size;
char* info; char* info;
UINT16 info_len; UINT16 info_len;
@ -125,6 +175,47 @@ class NormObject
}; // end 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 class NormStreamObject : public NormObject
{ {
@ -144,14 +235,29 @@ class NormStreamObject : public NormObject
virtual bool WriteSegment(NormBlockId blockId, virtual bool WriteSegment(NormBlockId blockId,
NormSegmentId segmentId, NormSegmentId segmentId,
const char* buffer); const char* buffer);
virtual bool ReadSegment(NormBlockId blockId,
virtual UINT16 ReadSegment(NormBlockId blockId, NormSegmentId segmentId, NormSegmentId segmentId,
NormObjectSize* offset, char* buffer, UINT16 maxlen); char* buffer);
unsigned long Read(char* buffer, unsigned long len); unsigned long Read(char* buffer, unsigned long len);
unsigned long Write(char* buffer, unsigned long len, bool flush = false); unsigned long Write(char* buffer, unsigned long len, bool flush = false);
// For receive stream, we can rewind to earliest buffered offset // For receive stream, we can rewind to earliest buffered offset
void Rewind(); 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: private:
class Index class Index
@ -167,20 +273,52 @@ class NormStreamObject : public NormObject
NormObjectSize write_offset; NormObjectSize write_offset;
Index read_index; Index read_index;
NormObjectSize read_offset; NormObjectSize read_offset;
bool flush_pending;
}; // end class NormStreamObject }; // 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 class NormObjectTable
{ {
friend class NormObjectTable::Iterator;
public: public:
class Iterator;
friend class NormObjectTable::Iterator;
NormObjectTable(); NormObjectTable();
~NormObjectTable(); ~NormObjectTable();
bool Init(UINT16 rangeMax, UINT16 tableSize = 256); bool Init(UINT16 rangeMax, UINT16 tableSize = 256);
void Destroy(); void Destroy();
bool IsInited() {return (NULL != table);} bool IsInited() {return (NULL != table);}
bool CanInsert(NormObjectId objectId) const;
bool Insert(NormObject* theObject); bool Insert(NormObject* theObject);
bool Remove(const NormObject* theObject); bool Remove(const NormObject* theObject);
NormObject* Find(const NormObjectId& objectId) const; NormObject* Find(const NormObjectId& objectId) const;
@ -188,7 +326,9 @@ class NormObjectTable
NormObjectId RangeLo() {return range_lo;} NormObjectId RangeLo() {return range_lo;}
NormObjectId RangeHi() {return range_hi;} NormObjectId RangeHi() {return range_hi;}
bool IsEmpty() {return (0 == range);} bool IsEmpty() {return (0 == range);}
unsigned long Count() {return count;}
const NormObjectSize& Size() {return size;}
class Iterator class Iterator
{ {
public: public:
@ -213,6 +353,8 @@ class NormObjectTable
unsigned long range; // zero if "object table" is empty unsigned long range; // zero if "object table" is empty
NormObjectId range_lo; NormObjectId range_lo;
NormObjectId range_hi; NormObjectId range_hi;
unsigned long count;
NormObjectSize size;
}; // end class NormObjectTable }; // end class NormObjectTable
#endif // _NORM_OBJECT #endif // _NORM_OBJECT

View File

@ -67,7 +67,7 @@ char* NormSegmentPool::Get()
{ {
seg_list = *ptr; seg_list = *ptr;
seg_count--; seg_count--;
#ifdef NORM_DEBUG //#ifdef NORM_DEBUG
overrun_flag = false; overrun_flag = false;
unsigned int usage = seg_total - seg_count; unsigned int usage = seg_total - seg_count;
if (usage > peak_usage) peak_usage = usage; if (usage > peak_usage) peak_usage = usage;
@ -79,7 +79,7 @@ char* NormSegmentPool::Get()
overruns++; overruns++;
overrun_flag = true; overrun_flag = true;
} }
#endif // NORM_DEBUG //#endif // NORM_DEBUG
} }
return ((char*)ptr); return ((char*)ptr);
} // end NormSegmentPool::GetSegment() } // end NormSegmentPool::GetSegment()
@ -122,6 +122,7 @@ bool NormBlock::Init(UINT16 blockSize)
size = blockSize; size = blockSize;
erasure_count = 0; erasure_count = 0;
parity_count = 0; parity_count = 0;
parity_offset = 0;;
return true; return true;
} // end NormBlock::Init() } // end NormBlock::Init()
@ -133,7 +134,10 @@ void NormBlock::Destroy()
if (segment_table) if (segment_table)
{ {
for (unsigned int i = 0; i < size; i++) for (unsigned int i = 0; i < size; i++)
{
ASSERT(!segment_table[i]);
if (segment_table[i]) delete []segment_table[i]; if (segment_table[i]) delete []segment_table[i];
}
delete []segment_table; delete []segment_table;
segment_table = (char**)NULL; segment_table = (char**)NULL;
} }
@ -161,17 +165,319 @@ bool NormBlock::IsEmpty()
return true; return true;
} // end NormBlock::EmptyToPool() } // 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()); // Clients ask for a block of parity to fulfill their
repair_mask.SetBits(end, repair_mask.Size() - end); // 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); repair_mask.XCopy(pending_mask);
return (repair_mask.IsSet() ? (repair_mask.FirstSet() < end) : false); return (repair_mask.IsSet());
} // end NormBlock::IsRepairPending() } // 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() 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 (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]; NormBlock* theBlock = table[((UINT32)blockId) & hash_mask];
//printf("NormBlockBuffer::Find() table[%lu] = %p\n", ((UINT32)blockId) & hash_mask, theBlock); while (theBlock && (blockId != theBlock->Id()))
while (theBlock && (blockId != theBlock->Id())) theBlock = theBlock->next; theBlock = theBlock->next;
return theBlock; return theBlock;
} }
else else
@ -318,56 +625,49 @@ bool NormBlockBuffer::CanInsert(NormBlockId blockId) const
bool NormBlockBuffer::Insert(NormBlock* theBlock) bool NormBlockBuffer::Insert(NormBlock* theBlock)
{ {
if (range < range_max) const NormBlockId& blockId = theBlock->Id();
if (!range)
{ {
const NormBlockId& blockId = theBlock->Id(); range_lo = range_hi = blockId;
if (!range) range = 1;
{
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;
} }
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 else
{ table[index] = theBlock;
return false; ASSERT((entry ? (blockId != entry->Id()) : true));
} theBlock->next = entry;
return true;
} // end NormBlockBuffer::Insert() } // end NormBlockBuffer::Insert()
bool NormBlockBuffer::Remove(const NormBlock* theBlock) bool NormBlockBuffer::Remove(const NormBlock* theBlock)
{ {
ASSERT(theBlock); ASSERT(theBlock);
const NormBlockId& blockId = theBlock->Id();
if (range) if (range)
{ {
const NormBlockId& blockId = theBlock->Id();
if ((blockId < range_lo) || (blockId > range_hi)) return false; if ((blockId < range_lo) || (blockId > range_hi)) return false;
UINT32 index = ((UINT32)blockId) & hash_mask; UINT32 index = ((UINT32)blockId) & hash_mask;
NormBlock* prev = NULL; NormBlock* prev = NULL;
@ -377,11 +677,12 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
prev = entry; prev = entry;
entry = entry->next; entry = entry->next;
} }
if (entry != theBlock) return false; if (!entry) return false;
if (prev) if (prev)
prev->next = entry->next; prev->next = entry->next;
else else
table[index] = (NormBlock*)NULL; table[index] = entry->next;
if (range > 1) if (range > 1)
{ {
if (blockId == range_lo) if (blockId == range_lo)
@ -390,7 +691,7 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
UINT32 i = index; UINT32 i = index;
UINT32 endex; UINT32 endex;
if (range <= hash_mask) if (range <= hash_mask)
endex = (index + range) & hash_mask; endex = (index + range - 1) & hash_mask;
else else
endex = index; endex = index;
entry = NULL; entry = NULL;
@ -405,8 +706,8 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
NormBlockId id = (UINT32)index + offset; NormBlockId id = (UINT32)index + offset;
while(entry && (entry->Id() != id)) while(entry && (entry->Id() != id))
{ {
if ((entry->Id() > blockId) && (entry->Id() < nextId)) if ((entry->Id() > blockId) &&
nextId = entry->Id(); (entry->Id() < nextId)) nextId = entry->Id();
entry = entry->next; entry = entry->next;
} }
@ -414,19 +715,10 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
} }
} while (i != endex); } while (i != endex);
if (entry) if (entry)
{
range_lo = entry->Id(); 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 else
{ range_lo = nextId;
range = 0; range = range_hi - range_lo + 1;
}
} }
else if (blockId == range_hi) else if (blockId == range_hi)
{ {
@ -434,7 +726,7 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
UINT32 i = index; UINT32 i = index;
UINT32 endex; UINT32 endex;
if (range <= hash_mask) if (range <= hash_mask)
endex = (index - range) & hash_mask; endex = (index - range + 1) & hash_mask;
else else
endex = index; endex = index;
entry = NULL; entry = NULL;
@ -451,27 +743,18 @@ bool NormBlockBuffer::Remove(const NormBlock* theBlock)
//printf("Looking for id:%lu at index:%lu\n", (UINT32)id, i); //printf("Looking for id:%lu at index:%lu\n", (UINT32)id, i);
while(entry && (entry->Id() != id)) while(entry && (entry->Id() != id))
{ {
if ((entry->Id() < blockId) && (entry->Id() > prevId)) if ((entry->Id() < blockId) &&
prevId = entry->Id(); (entry->Id() > prevId)) prevId = entry->Id();
entry = entry->next; entry = entry->next;
} }
if (entry) break; if (entry) break;
} }
} while (i != endex); } while (i != endex);
if (entry) if (entry)
{
range_hi = entry->Id(); range_hi = entry->Id();
else
}
else if (prevId != range_lo)
{
range_hi = prevId; range_hi = prevId;
range = range_hi - range_lo + 1; range = range_hi - range_lo + 1;
}
else
{
range = 0;
}
} }
} }
else else
@ -510,13 +793,13 @@ NormBlock* NormBlockBuffer::Iterator::GetNextBlock()
{ {
if (buffer.range && if (buffer.range &&
(index < buffer.range_hi) && (index < buffer.range_hi) &&
!(index < buffer.range_lo)) (index >= buffer.range_lo))
{ {
// Find next entry _after_ current "index" // Find next entry _after_ current "index"
UINT32 i = index; UINT32 i = index;
UINT32 endex = buffer.range_hi - index; UINT32 endex;
if (endex <= buffer.hash_mask) if ((UINT32)(buffer.range_hi - index) <= buffer.hash_mask)
endex = (index + endex) & buffer.hash_mask; endex = buffer.range_hi & buffer.hash_mask;
else else
endex = index; endex = index;
UINT32 offset = 0; UINT32 offset = 0;
@ -526,8 +809,9 @@ NormBlock* NormBlockBuffer::Iterator::GetNextBlock()
++i &= buffer.hash_mask; ++i &= buffer.hash_mask;
offset++; offset++;
NormBlockId id = (UINT32)index + offset; NormBlockId id = (UINT32)index + offset;
ASSERT(i < 256);
NormBlock* entry = buffer.table[i]; NormBlock* entry = buffer.table[i];
while ((NULL != entry )& (entry->Id() != id)) while ((NULL != entry ) && (entry->Id() != id))
{ {
if ((entry->Id() > index) && (entry->Id() < nextId)) if ((entry->Id() > index) && (entry->Id() < nextId))
nextId = entry->Id(); nextId = entry->Id();

View File

@ -26,14 +26,18 @@ class NormSegmentPool
} }
bool IsEmpty() {return (NULL == seg_list);} 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: private:
unsigned int seg_size; unsigned int seg_size;
unsigned int seg_count; unsigned int seg_count;
unsigned int seg_total; unsigned int seg_total;
char* seg_list; char* seg_list;
unsigned int peak_usage; unsigned long peak_usage;
unsigned int overruns; unsigned long overruns;
bool overrun_flag; bool overrun_flag;
}; // end class NormSegmentPool }; // end class NormSegmentPool
@ -45,8 +49,7 @@ class NormBlock
public: public:
enum Flag enum Flag
{ {
PARITY_READY = 0x01, IN_REPAIR = 0x01
IN_REPAIR = 0x02
}; };
NormBlock(); NormBlock();
@ -57,8 +60,11 @@ class NormBlock
void Destroy(); void Destroy();
void SetFlag(NormBlock::Flag flag) {flags |= flag;} void SetFlag(NormBlock::Flag flag) {flags |= flag;}
bool ParityReady() {return (0 != (flags & PARITY_READY));}
bool InRepair() {return (0 != (flags & IN_REPAIR));} 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** SegmentList(UINT16 index = 0) {return &segment_table[index];}
char* Segment(NormSegmentId sid) char* Segment(NormSegmentId sid)
@ -71,7 +77,6 @@ class NormBlock
ASSERT(sid < size); ASSERT(sid < size);
ASSERT(!segment_table[sid]); ASSERT(!segment_table[sid]);
segment_table[sid] = segment; segment_table[sid] = segment;
erasure_count--;
} }
char* DetachSegment(NormSegmentId sid) char* DetachSegment(NormSegmentId sid)
{ {
@ -87,25 +92,62 @@ class NormBlock
segment_table[sid] = segment; segment_table[sid] = segment;
} }
// Server routines
void TxInit(NormBlockId& blockId, UINT16 ndata, UINT16 autoParity) void TxInit(NormBlockId& blockId, UINT16 ndata, UINT16 autoParity)
{ {
id = blockId; id = blockId;
pending_mask.Clear(); pending_mask.Clear();
pending_mask.SetBits(0, ndata+autoParity); pending_mask.SetBits(0, ndata+autoParity);
repair_mask.Clear(); repair_mask.Clear();
erasure_count = size - ndata; erasure_count = 0;
parity_count = 0; parity_count = 0;
parity_offset = autoParity;
flags = 0; flags = 0;
} }
void RxInit(NormBlockId& blockId, UINT16 ndata) void TxRecover(NormBlockId& blockId, UINT16 ndata, UINT16 nparity)
{ {
id = blockId; id = blockId;
pending_mask.Reset(); pending_mask.Clear();
repair_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_count = 0;
parity_offset = 0;
flags = 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 NormSymbolId FirstPending() const
{return pending_mask.FirstSet();} {return pending_mask.FirstSet();}
@ -113,12 +155,21 @@ class NormBlock
{return repair_mask.FirstSet();} {return repair_mask.FirstSet();}
bool SetPending(NormSymbolId s) bool SetPending(NormSymbolId s)
{return pending_mask.Set(s);} {return pending_mask.Set(s);}
bool SetPending(NormSymbolId firstId, UINT16 count)
{return pending_mask.SetBits(firstId, count);}
void UnsetPending(NormSymbolId s) void UnsetPending(NormSymbolId s)
{pending_mask.Unset(s);} {pending_mask.Unset(s);}
void ClearPending() void ClearPending()
{pending_mask.Clear();} {pending_mask.Clear();}
bool SetRepair(NormSymbolId s) bool SetRepair(NormSymbolId s)
{return repair_mask.Set(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) void UnsetRepair(NormSymbolId s)
{repair_mask.Unset(s);} {repair_mask.Unset(s);}
void ClearRepairs() void ClearRepairs()
@ -126,12 +177,23 @@ class NormBlock
bool IsPending(NormSymbolId s) const bool IsPending(NormSymbolId s) const
{return pending_mask.Test(s);} {return pending_mask.Test(s);}
bool IsPending() const bool IsPending() const
{return pending_mask.IsSet();} {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 NormSymbolId NextPending(NormSymbolId index) const
{return pending_mask.NextSet(index);} {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);} //void DisplayPendingMask(FILE* f) {pending_mask.Display(f);}
bool IsEmpty(); bool IsEmpty();
@ -145,6 +207,7 @@ class NormBlock
int flags; int flags;
UINT16 erasure_count; UINT16 erasure_count;
UINT16 parity_count; UINT16 parity_count;
UINT16 parity_offset;
NormBitmask pending_mask; NormBitmask pending_mask;
NormBitmask repair_mask; NormBitmask repair_mask;
@ -164,6 +227,15 @@ class NormBlockPool
{ {
NormBlock* b = head; NormBlock* b = head;
head = b ? b->next : NULL; head = b ? b->next : NULL;
if (b)
{
overrun_flag = false;
}
else if (!overrun_flag)
{
overruns++;
overrun_flag = true;
}
return b; return b;
} }
void Put(NormBlock* b) void Put(NormBlock* b)
@ -171,16 +243,20 @@ class NormBlockPool
b->next = head; b->next = head;
head = b; head = b;
} }
unsigned long OverrunCount() {return overruns;}
private: private:
NormBlock* head; NormBlock* head;
unsigned long overruns;
bool overrun_flag;
}; // end class NormBlockPool }; // end class NormBlockPool
class NormBlockBuffer class NormBlockBuffer
{ {
friend class NormBlockBuffer::Iterator;
public: public:
class Iterator;
friend class NormBlockBuffer::Iterator;
NormBlockBuffer(); NormBlockBuffer();
~NormBlockBuffer(); ~NormBlockBuffer();
bool Init(unsigned long rangeMax, unsigned long tableSize = 256); bool Init(unsigned long rangeMax, unsigned long tableSize = 256);

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,9 @@ class NormController
{ {
TX_QUEUE_EMPTY, TX_QUEUE_EMPTY,
RX_OBJECT_NEW, RX_OBJECT_NEW,
RX_OBJECT_UPDATE RX_OBJECT_INFO,
RX_OBJECT_UPDATE,
RX_OBJECT_COMPLETE,
}; };
virtual void Notify(NormController::Event event, virtual void Notify(NormController::Event event,
@ -51,6 +53,8 @@ class NormSessionMgr
UINT16 sessionPort, UINT16 sessionPort,
NormNodeId localNodeId = NormNodeId localNodeId =
NORM_NODE_ANY); NORM_NODE_ANY);
void DeleteSession(class NormSession* theSession);
UdpSocketInstallFunc* SocketInstaller() {return socket_installer;} UdpSocketInstallFunc* SocketInstaller() {return socket_installer;}
const void* SocketInstallData() {return socket_install_data;} const void* SocketInstallData() {return socket_install_data;}
@ -63,17 +67,16 @@ class NormSessionMgr
if (controller) if (controller)
controller->Notify(event, this, session, server, object); 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; ProtocolTimerMgr timer_mgr;
UdpSocketInstallFunc* socket_installer; UdpSocketInstallFunc* socket_installer;
const void* socket_install_data; const void* socket_install_data;
NormController* controller; NormController* controller;
class NormSession* top_session; class NormSession* top_session; // top of NormSession list
}; // end class NormSessionMgr }; // end class NormSessionMgr
@ -85,33 +88,62 @@ class NormSession
public: public:
enum {DEFAULT_MESSAGE_POOL_DEPTH = 16}; enum {DEFAULT_MESSAGE_POOL_DEPTH = 16};
static const double DEFAULT_TRANSMIT_RATE; // in bytes per second static const double DEFAULT_TRANSMIT_RATE; // in bytes per second
static const double DEFAULT_PROBE_MIN;
NormSession(NormSessionMgr& sessionMgr, NormNodeId localNodeId); static const double DEFAULT_PROBE_MAX;
~NormSession(); 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 // General methods
const NormNodeId& LocalNodeId() {return local_node_id;} const NormNodeId& LocalNodeId() {return local_node_id;}
bool Open(); bool Open();
void Close(); void Close();
bool IsOpen() {return (rx_socket.IsOpen() || tx_socket.IsOpen());} bool IsOpen() {return (rx_socket.IsOpen() || tx_socket.IsOpen());}
const NetworkAddress& Address() {return address;}
void SetAddress(const NetworkAddress& addr) {address = addr;} 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) 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, void Notify(NormController::Event event,
class NormServerNode* server, class NormServerNode* server,
class NormObject* object) class NormObject* object)
{ {
notify_pending = true;
session_mgr.Notify(event, this, server, object); 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 // Server methods
bool StartServer(unsigned long bufferSpace, bool StartServer(unsigned long bufferSpace,
UINT16 segmentSize, UINT16 segmentSize,
UINT16 numData, UINT16 numData,
UINT16 numParity); UINT16 numParity);
void StopServer(); 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;} bool IsServer() {return is_server;}
UINT16 ServerSegmentSize() {return segment_size;} UINT16 ServerSegmentSize() {return segment_size;}
UINT16 ServerBlockSize() {return ndata;} UINT16 ServerBlockSize() {return ndata;}
@ -120,23 +152,30 @@ class NormSession
void ServerSetAutoParity(UINT16 autoParity) void ServerSetAutoParity(UINT16 autoParity)
{ASSERT(autoParity <= nparity); auto_parity = 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) void ServerEncode(const char* segment, char** parityVectorList)
{encoder.Encode(segment, parityVectorList);} {encoder.Encode(segment, parityVectorList);}
NormStreamObject* QueueTxStream(UINT32 bufferSize,
const char* infoPtr = NULL,
UINT16 infoLen = 0);
NormBlock* ServerGetFreeBlock(NormObjectId objectId, NormBlockId blockId); NormBlock* ServerGetFreeBlock(NormObjectId objectId, NormBlockId blockId);
char* ServerGetFreeSegment(NormObjectId objectId, NormBlockId blockId);
void ServerPutFreeBlock(NormBlock* block) void ServerPutFreeBlock(NormBlock* block)
{ {
block->EmptyToPool(segment_pool); block->EmptyToPool(segment_pool);
block_pool.Put(block); block_pool.Put(block);
} }
char* ServerGetFreeSegment(NormObjectId objectId, NormBlockId blockId);
void ServerPutFreeSegment(char* segment) {segment_pool.Put(segment);}
void TouchServer() void TouchServer()
{ {
posted_tx_queue_empty = false; posted_tx_queue_empty = false;
Serve(); if (!notify_pending) Serve();
} }
// Client methods // Client methods
@ -145,39 +184,69 @@ class NormSession
bool IsClient() {return is_client;} bool IsClient() {return is_client;}
unsigned long RemoteServerBufferSize() unsigned long RemoteServerBufferSize()
{return remote_server_buffer_size;} {return remote_server_buffer_size;}
private:
void QueueMessage(NormMessage* msg);
void Serve();
bool QueueTxObject(NormObject* obj, bool touchServer = true);
void InstallTimer(ProtocolTimer* timer) void InstallTimer(ProtocolTimer* timer)
{session_mgr.InstallTimer(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 OnTxTimeout();
bool OnRepairTimeout();
bool OnCommandTimeout();
bool OnProbeTimeout();
bool OnReportTimeout();
bool TxSocketRecvHandler(UdpSocket* theSocket); bool TxSocketRecvHandler(UdpSocket* theSocket);
bool RxSocketRecvHandler(UdpSocket* theSocket); bool RxSocketRecvHandler(UdpSocket* theSocket);
void HandleReceiveMessage(NormMessage& msg, bool wasUnicast); 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 ClientHandleObjectMessage(NormMessage& msg);
void ClientHandleCommand(NormMessage& msg);
void ClientHandleNackMessage(NormNackMsg& nack);
NormSessionMgr& session_mgr; NormSessionMgr& session_mgr;
bool notify_pending;
ProtocolTimer tx_timer; ProtocolTimer tx_timer;
UdpSocket tx_socket; UdpSocket tx_socket;
UdpSocket rx_socket; UdpSocket rx_socket;
NormMessageQueue message_queue; NormMessageQueue message_queue;
NormMessageQueue message_pool; NormMessageQueue message_pool;
ProtocolTimer report_timer;
UINT16 tx_sequence;
// General session parameters // General session parameters
NormNodeId local_node_id; NormNodeId local_node_id;
NetworkAddress address; // session destination address NetworkAddress address; // session destination address
UINT8 ttl; // session multicast ttl UINT8 ttl; // session multicast ttl
UINT16 tx_sequence;
double tx_rate; // bytes per second double tx_rate; // bytes per second
double backoff_factor;
// Server parameters // Server parameters and state
bool is_server; bool is_server;
UINT16 segment_size; UINT16 segment_size;
UINT16 ndata; UINT16 ndata;
@ -187,20 +256,51 @@ class NormSession
NormObjectTable tx_table; NormObjectTable tx_table;
NormSlidingMask tx_pending_mask; NormSlidingMask tx_pending_mask;
NormSlidingMask tx_repair_mask; NormSlidingMask tx_repair_mask;
ProtocolTimer repair_timer;
NormBlockPool block_pool; NormBlockPool block_pool;
NormSegmentPool segment_pool; NormSegmentPool segment_pool;
NormEncoder encoder; 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; int flush_count;
bool posted_tx_queue_empty; 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 // Client parameters
bool is_client; bool is_client;
NormNodeTree server_tree; NormNodeTree server_tree;
unsigned long remote_server_buffer_size; unsigned long remote_server_buffer_size;
bool unicast_nacks;
// Protocol test/debug parameters
bool trace;
double tx_loss_rate; // for correlated loss
double rx_loss_rate; // for uncorrelated loss
// Misc // Linkers
NormSession* next; NormSession* next;
}; // end class NormSession }; // end class NormSession

565
common/normSimAgent.cpp Normal file
View File

@ -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()

82
common/normSimAgent.h Normal file
View File

@ -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

BIN
common/normSimAgent.o Normal file

Binary file not shown.

View File

@ -83,3 +83,81 @@ for which the receiver is maintaining state. That range may
be application specific and senders/receivers are anticipated be application specific and senders/receivers are anticipated
to use relatively compatible buffer ranges/sizes based on to use relatively compatible buffer ranges/sizes based on
application needs. 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.

37
common/version.h Normal file
View File

@ -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

55
ns/README.txt Normal file
View File

@ -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

96
ns/example.tcl Normal file
View File

@ -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

266
ns/nackCount.cpp Normal file
View File

@ -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()

432
ns/ns-2.1b7a-Makefile.in Normal file
View File

@ -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

97
ns/nsNormAgent.cpp Normal file
View File

@ -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()

57
ns/nsNormAgent.h Normal file
View File

@ -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

167
ns/simplenorm.tcl Normal file
View File

@ -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

132
ns/sizeAve.cpp Normal file
View File

@ -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()

55
ns/suppress.tcl Executable file
View File

@ -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."

View File

@ -4,11 +4,11 @@
SHELL=/bin/sh SHELL=/bin/sh
.SUFFIXES: .cpp $(.SUFFIXES) .SUFFIXES: .cpp -sim.o $(.SUFFIXES)
PROTOLIB = ../protolib PROTOLIB = ../protolib
COMMON = ../common COMMON = ../common
UNIX = ./ UNIX = ../unix
NS = ../ns NS = ../ns
INCLUDES = $(TCL_INCL_PATH) $(SYSTEM_INCLUDES) -I$(UNIX) -I$(COMMON) -I$(PROTOLIB)/common 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 \ NORM_SRC = $(COMMON)/normMessage.cpp $(COMMON)/normSession.cpp \
$(COMMON)/normNode.cpp $(COMMON)/normObject.cpp \ $(COMMON)/normNode.cpp $(COMMON)/normObject.cpp \
$(COMMON)/normSegment.cpp $(COMMON)/normBitmask.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) NORM_OBJ = $(NORM_SRC:.cpp=.o)
LIB_SRC = $(NORM_SRC) LIB_SRC = $(NORM_SRC)
LIB_OBJ = $(LIB_SRC:.cpp=.o) LIB_OBJ = $(LIB_SRC:.cpp=.o)
libNorm.a: $(LIB_OBJ) libnorm.a: $(LIB_OBJ)
ar rcv $@ $(LIB_OBJ) ar rcv $@ $(LIB_OBJ)
ranlib $@ 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 # (mdp) command-line file broadcaster/receiver
APP_SRC = $(COMMON)/normApp.cpp APP_SRC = $(COMMON)/normApp.cpp
APP_OBJ = $(APP_SRC:.cpp=.o) APP_OBJ = $(APP_SRC:.cpp=.o)
norm: $(APP_OBJ) libNorm.a $(LIBPROTO) norm: $(APP_OBJ) libnorm.a $(LIBPROTO)
$(CC) $(CFLAGS) -o $@ $(APP_OBJ) $(LDFLAGS) $(LIBS) libNorm.a $(LIBPROTO) $(CC) $(CFLAGS) -o $@ $(APP_OBJ) $(LDFLAGS) $(LIBS) libnorm.a $(LIBPROTO)
clean: 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 make -C $(PROTOLIB)/unix -f Makefile.common clean
# DO NOT DELETE THIS LINE -- mkdep uses it. # DO NOT DELETE THIS LINE -- mkdep uses it.