added 'ack auto' option and self-assigned node id to normCast

pull/9/head
bebopagogo 2020-01-11 21:09:57 -05:00
parent 6d79812341
commit 9775aa3744
1 changed files with 101 additions and 28 deletions

View File

@ -10,13 +10,42 @@
#include <sys/select.h> #include <sys/select.h>
#endif // if/else WIN32/UNIX #endif // if/else WIN32/UNIX
#define SHOOT_FIRST 1 // When the "ack" option _and_ flushing is used, there are a couple of strategies
// that can be used. Here, the two options implemented are titled
// "SHOOT_FIRST" and "ACK_LATER". If "SHOOT_FIRST" is #defined, then
// NormSetWatermark() is called immediately after each enqueued transmit
// object. This call to "NormSetWatermark()" cancels any previous
// watermark request, immediately advancing the "watermark" to the current
// transmit object id. Generally, this is OK, but in very high bandwidth*delay,
// this may cause some dead air time since flow control might not be advanced any
// until the ack for watermark is received. When SHOOT_FIRST is _not_ #defined,
// the NormSetWatermark() call is deferred if an existing flow control watermark
// request is pending acknowledgment. This potentially allows the flow control
// to be advanced sooner instead of the sender "chasing its own tail" with the
// SHOOT_FIRST strategy. Both strategies are implemented here, because under
// certain cases, the SHOOT_FIRST _may_ have benefit. Try them both, and you
// decide ...
// I usually avoid using Protlib stuff for NORM API examples to keep things clearer, // By the way, this SHOOT_FIRST/ACK_LATER only applies to the case when both
// the "ack" and "flush" options are used. The "flush" option controls the
// NORM sender behavior when object objects are transmitted intermittently.
// I.e., determines if/when NORM_CMD(FLUSH) messages are sent prior to
// final end-of-transmission (e.g., when a pause in transmission occurs).
// This will be applicable when normCast gets a few additional options like
// repeat iterations of transmit file/directory list on user-controlled
// timer interval, etc. For example, "flush active" means NORM_CMD(FLUSH)
// would be sent during such pauses, while "flush none" would result in
// sender immediately going silent at pauses in file transmission.
// Uncomment this to enable the "SHOOT_FIRST" strategy, as opposed to "ACK_LATER"
// #define SHOOT_FIRST 1
// I usually avoid using Protolib stuff for NORM API examples to keep things clearer,
// but the couple of classes here are useful helpers from the Protolib C++ toolkit. // but the couple of classes here are useful helpers from the Protolib C++ toolkit.
#include "protoFile.h" // for ProtoFile::PathList and iterator for tx file/directory queue #include "protoFile.h" // for ProtoFile::PathList and iterator for tx file/directory queue
#include "protoString.h" // for ProtoTokenator #include "protoString.h" // for ProtoTokenator
#include "protoAddress.h" // for ProtoAddress
class NormCaster class NormCaster
{ {
@ -35,6 +64,8 @@ class NormCaster
NormNodeId nodeId); NormNodeId nodeId);
void CloseNormSession(); void CloseNormSession();
NormSessionHandle GetSession() const {return norm_session;}
void SetNormCongestionControl(CCMode ccMode); void SetNormCongestionControl(CCMode ccMode);
void SetNormTxRate(double bitsPerSecond) void SetNormTxRate(double bitsPerSecond)
@ -102,6 +133,13 @@ class NormCaster
{return rx_cache_path;} {return rx_cache_path;}
// These can only be called post-OpenNormSession() // These can only be called post-OpenNormSession()
void SetAutoAck(bool enable)
{
NormTrackingStatus trackingMode = enable? NORM_TRACK_RECEIVERS : NORM_TRACK_NONE;
NormSetAutoAckingNodes(norm_session, trackingMode);
norm_acking = enable;
}
void SetSilentReceiver(bool state) void SetSilentReceiver(bool state)
{NormSetSilentReceiver(norm_session, true);} {NormSetSilentReceiver(norm_session, true);}
void SetTxLoss(double txloss) void SetTxLoss(double txloss)
@ -257,7 +295,7 @@ bool NormCaster::Start(bool sender, bool receiver)
return false; return false;
} }
// Note: NormPreallocateRemoteSender() MUST be called AFTER NormStartReceiver() // Note: NormPreallocateRemoteSender() MUST be called AFTER NormStartReceiver()
NormPreallocateRemoteSender(norm_session, bufferSize, segmentSize, blockSize, numParity, bufferSize); //NormPreallocateRemoteSender(norm_session, bufferSize, segmentSize, blockSize, numParity, bufferSize);
NormSetRxSocketBuffer(norm_session, rxSockBufferSize); NormSetRxSocketBuffer(norm_session, rxSockBufferSize);
fprintf(stderr, "normCast: receiver ready ...\n"); fprintf(stderr, "normCast: receiver ready ...\n");
} }
@ -332,9 +370,9 @@ bool NormCaster::StageNextTxFile()
} }
else else
{ {
// Adjust the prefix len so only the file basename is conveyed. // Set the tx_pending_prefix_len" so only the file basename is conveyed.
// (We use a reverse ProtoTokenator tokenization limited to a // (We use a reverse ProtoTokenator tokenization limited to a single
// single split to get the file basename string). // split to get the file basename string).
ProtoTokenator tk(tx_pending_path, PROTO_PATH_DELIMITER, true, 1, true); ProtoTokenator tk(tx_pending_path, PROTO_PATH_DELIMITER, true, 1, true);
const char* basename = tk.GetNextItem(); const char* basename = tk.GetNextItem();
ASSERT(NULL != basename); ASSERT(NULL != basename);
@ -367,7 +405,7 @@ void NormCaster::SendFiles()
if (!StageNextTxFile()) if (!StageNextTxFile())
{ {
// We have reach end of tx_file_list, so either // We have reach end of tx_file_list, so either
// we're done or we reset tx_file_iterator // we're done or we reset tx_file_iterator (when 'repeat' option (TBD) is enabled)
// If we're done and requesting ACK, finish up nicely with final waterrmark // If we're done and requesting ACK, finish up nicely with final waterrmark
if (norm_acking) if (norm_acking)
{ {
@ -489,6 +527,7 @@ void NormCaster::HandleNormEvent(const NormEvent& event)
norm_flow_control_pending = false; norm_flow_control_pending = false;
if (NORM_OBJECT_INVALID != norm_flush_object) if (NORM_OBJECT_INVALID != norm_flush_object)
{ {
// Set the deferred watermark per our ACK_LATER strategy
NormSetWatermark(norm_session, norm_flush_object, true); NormSetWatermark(norm_session, norm_flush_object, true);
norm_last_object = norm_flush_object; norm_last_object = norm_flush_object;
norm_flush_object = NORM_OBJECT_INVALID; norm_flush_object = NORM_OBJECT_INVALID;
@ -526,10 +565,19 @@ void NormCaster::HandleNormEvent(const NormEvent& event)
if(event.object == norm_flush_object) if(event.object == norm_flush_object)
norm_flush_object = NORM_OBJECT_INVALID; norm_flush_object = NORM_OBJECT_INVALID;
// This is where we could delete the associated tx file if desired // This is where we could delete the associated tx file if desired
// (e.g., for an "outbox" use case // (e.g., for an "outbox" use case)
break; break;
} }
case NORM_ACKING_NODE_NEW:
{
NormNodeId id = NormNodeGetId(event.sender);
UINT32 tmp = htonl(id);
ProtoAddress addr;
addr.SetRawHostAddress(ProtoAddress::IPv4, (char*)&tmp, 4);
fprintf(stderr, "normCast: new acking node: %lu (IP address: %s)\n", (unsigned long)id, addr.GetHostString());
}
case NORM_REMOTE_SENDER_INACTIVE: case NORM_REMOTE_SENDER_INACTIVE:
//fprintf(stderr, "REMOTE SENDER INACTIVE node: %u\n", NormNodeGetId(event.sender)); //fprintf(stderr, "REMOTE SENDER INACTIVE node: %u\n", NormNodeGetId(event.sender));
//NormNodeDelete(event.sender); //NormNodeDelete(event.sender);
@ -608,6 +656,7 @@ int main(int argc, char* argv[])
strcpy(sessionAddr, "224.1.2.3"); strcpy(sessionAddr, "224.1.2.3");
unsigned int sessionPort = 6003; unsigned int sessionPort = 6003;
bool autoAck = false;
NormNodeId ackingNodeList[256]; NormNodeId ackingNodeList[256];
unsigned int ackingNodeCount = 0; unsigned int ackingNodeCount = 0;
bool flushing = false; bool flushing = false;
@ -721,20 +770,28 @@ int main(int argc, char* argv[])
return -1; return -1;
} }
const char* alist = argv[i++]; const char* alist = argv[i++];
while ((NULL != alist) && (*alist != '\0')) if (0 == strcmp("auto", alist))
{ {
// TBD - Do we need to skip leading white space? autoAck = true;
int id; }
if (1 != sscanf(alist, "%d", &id)) else
{
autoAck = false;
while ((NULL != alist) && (*alist != '\0'))
{ {
fprintf(stderr, "normCast error: invalid acking node list!\n"); // TBD - Do we need to skip leading white space?
Usage(); int id;
return -1; if (1 != sscanf(alist, "%d", &id))
{
fprintf(stderr, "normCast error: invalid acking node list!\n");
Usage();
return -1;
}
ackingNodeList[ackingNodeCount] = NormNodeId(id);
ackingNodeCount++;
alist = strchr(alist, ',');
if (NULL != alist) alist++; // point past comma
} }
ackingNodeList[ackingNodeCount] = NormNodeId(id);
ackingNodeCount++;
alist = strchr(alist, ',');
if (NULL != alist) alist++; // point past comma
} }
} }
else if (0 == strncmp(cmd, "flush", len)) else if (0 == strncmp(cmd, "flush", len))
@ -860,12 +917,6 @@ int main(int argc, char* argv[])
Usage(); Usage();
return -1; return -1;
} }
if (NORM_NODE_NONE == nodeId)
{
fprintf(stderr, "normCast error: no local 'id' provided!\n");
Usage();
return -1;
}
// TBD - should provide more error checking of calls // TBD - should provide more error checking of calls
NormInstanceHandle normInstance = NormCreateInstance(); NormInstanceHandle normInstance = NormCreateInstance();
@ -887,11 +938,33 @@ int main(int argc, char* argv[])
return -1; return -1;
} }
if (NORM_NODE_NONE == nodeId)
{
// local node id was auto-assigned so let's see what it was assigned
nodeId = NormGetLocalNodeId(normCast.GetSession());
// For cross-platform, use ProtoAddress to see if a reasonable IP address was used
UINT32 tmp = htonl(nodeId);
ProtoAddress addr;
addr.SetRawHostAddress(ProtoAddress::IPv4, (char*)&tmp, 4);
fprintf(stderr, "normCast: auto assigned NormNodeId: %lu (IP address: %s)\n",
(unsigned long)nodeId, addr.GetHostString());
}
if (silentReceiver) normCast.SetSilentReceiver(true); if (silentReceiver) normCast.SetSilentReceiver(true);
if (txloss > 0.0) normCast.SetTxLoss(txloss); if (txloss > 0.0) normCast.SetTxLoss(txloss);
for (unsigned int i = 0; i < ackingNodeCount; i++)
normCast.AddAckingNode(ackingNodeList[i]); if (autoAck)
{
normCast.SetAutoAck(true);
}
else
{
for (unsigned int i = 0; i < ackingNodeCount; i++)
normCast.AddAckingNode(ackingNodeList[i]);
}
normCast.SetNormCongestionControl(ccMode); normCast.SetNormCongestionControl(ccMode);
if (NormCaster::NORM_FIXED == ccMode) if (NormCaster::NORM_FIXED == ccMode)
@ -1019,7 +1092,7 @@ int main(int argc, char* argv[])
normCast.Destroy(); normCast.Destroy();
fprintf(stderr, "normCast done.\n"); fprintf(stderr, "normCast: done.\n");
} // end main() } // end main()