libcm/cmUdpNet.c

731 line
18 KiB
C

#include "cmPrefix.h"
#include "cmGlobal.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmUdpPort.h"
#include "cmJson.h"
#include "cmUdpNet.h"
#include "cmTime.h"
typedef struct cmUdpNode_str
{
cmChar_t* label;
unsigned id;
struct sockaddr_in addr;
cmUdpPort_t port;
struct cmUdpNode_str* link;
} cmUdpNode_t;
typedef struct
{
cmErr_t err;
cmUdpNetCallback_t cbFunc;
void* cbArg;
unsigned timeOutMs;
unsigned nodeId;
cmChar_t* nodeLabel;
cmUdpNode_t* list;
cmUdpH_t udpH;
} cmUdpNet_t;
cmUdpNetH_t cmUdpNetNullHandle = cmSTATIC_NULL_HANDLE;
cmUdpNet_t* _cmUnHandleToPtr( cmUdpNetH_t h )
{
cmUdpNet_t* p = (cmUdpNet_t*)h.h;
assert(p !=NULL );
return p;
}
cmUnRC_t _cmUdpNetFinal( cmUdpNet_t* p )
{
// release the node list
while( p->list != NULL)
{
cmUdpNode_t* np = p->list->link;
cmMemFree(p->list->label);
cmMemFree(p->list);
p->list = np;
}
cmMemFree(p->nodeLabel);
p->nodeLabel = NULL;
p->cbFunc = NULL;
p->cbArg = NULL;
p->nodeId = cmInvalidId;
return kOkUnRC;
}
cmUnRC_t _cmUdpNetFree( cmUdpNet_t* p )
{
cmUnRC_t rc;
if((rc = _cmUdpNetFinal(p)) != kOkUnRC )
return rc;
// release the UDP port
if( cmUdpFree(&p->udpH) != kOkUdpRC )
return cmErrMsg(&p->err,kUdpPortFailUnRC,"The UDP port release failed.");
cmMemFree(p);
return kOkUnRC;
}
cmUdpNode_t* _cmUnFindLabel( cmUdpNet_t* p, const cmChar_t* label )
{
cmUdpNode_t* np = p->list;
for(; np != NULL; np = np->link )
if( strcmp(np->label,label) == 0 )
return np;
return NULL;
}
cmUdpNode_t* _cmUnFindId( cmUdpNet_t* p, unsigned id )
{
cmUdpNode_t* np = p->list;
for(; np != NULL; np = np->link )
if( np->id == id )
return np;
return NULL;
}
void _cmUdpNetCallback( void* cbArg, const char* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
{
cmUdpNet_t* p = (cmUdpNet_t*)cbArg;
if( p->cbFunc != NULL )
{
cmUdpNetH_t h;
cmUdpNode_t* np = p->list;
h.h = p;
// locate the source node in the node list to get the source node id
for(; np != NULL; np = np->link)
if( np->addr.sin_addr.s_addr == fromAddr->sin_addr.s_addr )
{
// forward the call to the networks callback function
p->cbFunc(p->cbArg,h,data,dataByteCnt,np->id);
break;
}
if( np == NULL )
cmErrMsg(&p->err,kNodeNotFoundUnRC,"A callback source node could not be identified.");
}
}
cmUnRC_t cmUdpNetAlloc(cmCtx_t* ctx, cmUdpNetH_t* hp)
{
cmUnRC_t rc;
if((rc = cmUdpNetFree(hp)) != kOkUnRC )
return rc;
cmUdpNet_t* p = cmMemAllocZ(cmUdpNet_t,1);
cmErrSetup(&p->err,&ctx->rpt,"UDP Network");
if( cmUdpAlloc(ctx,&p->udpH) != kOkUdpRC )
return cmErrMsg(&p->err,kUdpPortFailUnRC,"UDP port allocation failed.");
p->nodeId = cmInvalidId;
hp->h = p;
return kOkUnRC;
}
cmUnRC_t cmUdpNetAllocJson(
cmCtx_t* ctx,
cmUdpNetH_t* hp,
cmJsonH_t jsH,
cmUdpNetCallback_t cbFunc,
void* cbArg,
unsigned flags)
{
cmUnRC_t rc;
if((rc = cmUdpNetAlloc(ctx,hp)) != kOkUnRC )
return rc;
if((rc = cmUdpNetInitJson(*hp,jsH,cbFunc,cbArg)) != kOkUnRC )
{
if( cmIsFlag(flags,kNetOptionalUnFl) )
rc = kOkUnRC;
goto errLabel;
}
if((rc = cmUdpNetEnableListen(*hp,cmIsFlag(flags,kListenUnFl))) != kOkUnRC )
goto errLabel;
errLabel:
if( rc != kOkUnRC )
cmUdpNetFree(hp);
return rc;
}
cmUnRC_t cmUdpNetFree( cmUdpNetH_t* hp )
{
cmUnRC_t rc = kOkUnRC;
if( hp == NULL || cmUdpNetIsValid(*hp)==false )
return kOkUnRC;
cmUdpNet_t* p = _cmUnHandleToPtr(*hp);
if((rc = _cmUdpNetFree(p)) != kOkUnRC )
return rc;
hp->h = NULL;
return rc;
}
cmUnRC_t cmUdpNetInit(
cmUdpNetH_t h,
const cmChar_t* nodeLabel,
unsigned nodeId,
cmUdpPort_t nodeSocketPort,
cmUdpNetCallback_t cbFunc,
void* cbArg,
unsigned recvBufByteCnt,
unsigned socketRecvTimeOutMs )
{
cmUnRC_t rc;
cmUdpNet_t* p = _cmUnHandleToPtr(h);
if((rc = _cmUdpNetFinal(p)) != kOkUnRC )
return rc;
// create the UDP port
if( cmUdpInit(p->udpH,nodeSocketPort,kBlockingUdpFl,_cmUdpNetCallback,p,NULL,0,recvBufByteCnt,socketRecvTimeOutMs) != kOkUdpRC )
{
rc = cmErrMsg(&p->err,kUdpPortFailUnRC,"The UDP port create failed.");
goto errLabel;
}
p->cbFunc = cbFunc;
p->cbArg = cbArg;
p->timeOutMs = socketRecvTimeOutMs;
p->nodeId = nodeId;
p->nodeLabel = cmMemAllocStr(nodeLabel);
p->list = NULL;
errLabel:
if( rc != kOkUnRC )
_cmUdpNetFinal(p);
return rc;
}
cmUnRC_t _cmUnParseMemberErr( cmUdpNet_t* p, cmJsRC_t jsRC, const cmChar_t* errLabel, const cmChar_t* objectLabel )
{
if( jsRC == kNodeNotFoundJsRC && errLabel != NULL )
return cmErrMsg(&p->err,kJsonFailUnRC,"The required field '%s'was not found in the UDP network resource tree in the object '%s'.",errLabel,cmStringNullGuard(objectLabel));
return cmErrMsg(&p->err,kJsonFailUnRC,"JSON parsing failed on the UDP network resource object '%s'.",cmStringNullGuard(objectLabel));
}
cmUnRC_t cmUdpNetInitJson(
cmUdpNetH_t h,
cmJsonH_t jsH,
cmUdpNetCallback_t cbFunc,
void* cbArg)
{
cmUnRC_t rc;
cmUdpNet_t* p = _cmUnHandleToPtr(h);
cmJsonNode_t* unp;
cmJsonNode_t* nap;
const cmChar_t* errLabelPtr = NULL;
cmJsRC_t jsRC;
unsigned port,recvBufByteCnt,timeOutMs;
const cmChar_t* localLabel;
const cmChar_t* topLabel = "udpnet";
unsigned nodeCnt = 0;
unsigned i;
typedef struct
{
const cmChar_t* label;
const cmChar_t* addr;
unsigned id;
} unNode_t;
unNode_t* a = NULL;
unNode_t* localPtr = NULL;
if((rc = _cmUdpNetFinal(p)) != kOkUnRC )
return rc;
if((unp = cmJsonFindValue(jsH,topLabel,NULL,kObjectTId )) == NULL )
{
return cmErrMsg(&p->err,kJsonFailUnRC,"The JSON 'udpnet' element was not found.");
}
if(( jsRC = cmJsonMemberValues( unp, &errLabelPtr,
"port", kIntTId, &port,
"recvBufByteCnt", kIntTId, &recvBufByteCnt,
"timeOutMs", kIntTId, &timeOutMs,
"local", kStringTId,&localLabel,
"nodeArray", kArrayTId, &nap,
NULL )) != kOkJsRC )
{
rc = _cmUnParseMemberErr(p, jsRC, errLabelPtr, topLabel );
goto errLabel;
}
// get a count of the number of nodes
if((nodeCnt = cmJsonChildCount(nap)) == 0 )
{
rc = cmErrMsg(&p->err,kJsonFailUnRC,"The JSON 'udpnet' node array appears to be empty.");
goto errLabel;
}
// allocate a temporary array to hold the node list
a = cmMemAllocZ(unNode_t,nodeCnt);
// for each remote node
for(i=0; i<nodeCnt; ++i)
{
const cmJsonNode_t* naep = cmJsonArrayElementC(nap,i);
// read the JSON recd
if(( jsRC = cmJsonMemberValues( naep, &errLabelPtr,
"label", kStringTId, &a[i].label,
"id", kIntTId, &a[i].id,
"addr", kStringTId, &a[i].addr,
NULL )) != kOkJsRC )
{
rc = _cmUnParseMemberErr(p, jsRC, errLabelPtr, "nodeArray" );
goto errLabel;
}
// track which node is the local node
if( strcmp(localLabel,a[i].label) == 0 )
localPtr = a + i;
}
// if no local node was located
if( localPtr == NULL )
{
rc = cmErrMsg(&p->err, kJsonFailUnRC,"The local node label '%s' was not found.",cmStringNullGuard(localLabel));
goto errLabel;
}
// initialize the network object
if((rc = cmUdpNetInit(h,localPtr->label, localPtr->id, port, cbFunc, cbArg, recvBufByteCnt, timeOutMs )) != kOkUnRC )
goto errLabel;
// register each remote node
for(i=0; i<nodeCnt; ++i)
if( a + i != localPtr )
if((rc = cmUdpNetRegisterRemote(h,a[i].label,a[i].id,a[i].addr,port)) != kOkUnRC )
goto errLabel;
errLabel:
cmMemFree(a);
if( rc != kOkUnRC )
_cmUdpNetFinal(p);
return rc;
}
bool cmUdpNetIsInitialized( cmUdpNetH_t h )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
return p->nodeId != cmInvalidId;
}
cmUnRC_t cmUdpNetFinal( cmUdpNetH_t h )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
return _cmUdpNetFinal(p);
}
cmUnRC_t cmUdpNetEnableListen( cmUdpNetH_t h, bool enableFl )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
if( cmUdpEnableListen(p->udpH,enableFl) != kOkUdpRC )
return cmErrMsg(&p->err,kUdpPortFailUnRC,"UDP port listen %s failed.",enableFl?"enable":"disable");
return kOkUnRC;
}
bool cmUdpNetIsValid( cmUdpNetH_t h )
{ return h.h != NULL; }
unsigned cmUdpNetLocalNodeId( cmUdpNetH_t h )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
return p->nodeId;
}
const cmChar_t* cmUdpNetLocalNodeLabel( cmUdpNetH_t h )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
return p->nodeLabel;
}
unsigned cmUdpNetNodeLabelToId( cmUdpNetH_t h, const cmChar_t* label )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
// if the network was not initialized then nodeLabel == NULL
if( p->nodeLabel == NULL )
return cmInvalidId;
// if 'label' refers to the local node
if( strcmp(label,p->nodeLabel) == 0 )
return p->nodeId;
// otherwise search the remote node list
cmUdpNode_t* np = p->list;
for(; np != NULL; np = np->link )
if( strcmp(np->label,label) == 0 )
return np->id;
return cmInvalidId;
}
const cmChar_t* cmUdpNetNodeIdToLabel( cmUdpNetH_t h, unsigned id )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
// if 'id' refers to the local node
if( id == p->nodeId )
return p->nodeLabel;
// otherwise search the remote node list
cmUdpNode_t* np = p->list;
for(; np != NULL; np = np->link )
if( np->id == id )
return np->label;
return NULL;
}
unsigned cmUdpNetNodeCount( cmUdpNetH_t h )
{
unsigned cnt = 0;
cmUdpNet_t* p = _cmUnHandleToPtr(h);
cmUdpNode_t* np = p->list;
for(; np != NULL; np = np->link )
++cnt;
return cnt;
}
unsigned cmUdpNetNodeId( cmUdpNetH_t h, unsigned nodeIdx )
{
unsigned cnt = 0;
cmUdpNet_t* p = _cmUnHandleToPtr(h);
cmUdpNode_t* np = p->list;
for(; np != NULL; np = np->link )
if( cnt == nodeIdx )
return np->id;
return cmInvalidId;
}
cmUnRC_t cmUdpNetRegisterRemote(
cmUdpNetH_t h,
const cmChar_t* remoteNodeLabel,
unsigned remoteNodeId,
const char* remoteNodeSockAddr,
cmUdpPort_t remoteNodePort )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
struct sockaddr_in addr;
if( remoteNodeLabel == NULL || strlen(remoteNodeLabel)==0 )
return cmErrMsg(&p->err,kInvalidNodeLabelUnRC,"The node label must contain a non-empty string.");
if( _cmUnFindLabel(p,remoteNodeLabel) != NULL )
return cmErrMsg(&p->err,kDuplicateNodeLabelUnRC,"The node label '%s' is already in use.",cmStringNullGuard(remoteNodeLabel));
if( _cmUnFindId(p,remoteNodeId) != NULL )
return cmErrMsg(&p->err,kDuplicateNodeIdUnRC,"The node id '%i' is already in use.",remoteNodeId);
if( cmUdpInitAddr(p->udpH, remoteNodeSockAddr, remoteNodePort, &addr ) != kOkUdpRC )
return cmErrMsg(&p->err,kInvalidNodeAddrUnRC,"The node address '%s' port:%i is not valid.",cmStringNullGuard(remoteNodeSockAddr),remoteNodePort);
cmUdpNode_t* np = cmMemAllocZ(cmUdpNode_t,1);
np->label = cmMemAllocStr(remoteNodeLabel);
np->id = remoteNodeId;
np->addr = addr;
np->port = remoteNodePort;
np->link = p->list;
p->list = np;
return kOkUnRC;
}
cmUnRC_t cmUdpNetSendById( cmUdpNetH_t h, unsigned remoteNodeId, const void* data, unsigned dataByteCnt )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
cmUdpNode_t* np;
if((np = _cmUnFindId(p,remoteNodeId)) == NULL )
return cmErrMsg(&p->err,kNodeNotFoundUnRC,"The remote node (id=%i) was not found.",remoteNodeId);
if( cmUdpSendTo(p->udpH, data, dataByteCnt, &np->addr ) != kOkUdpRC )
return cmErrMsg(&p->err,kSendFailUnRC,"An attempt to send to a remote node with id=%i failed.",remoteNodeId);
return kOkUnRC;
}
cmUnRC_t cmUdpNetSendByLabel( cmUdpNetH_t h, const cmChar_t* remoteNodeLabel, const void* data, unsigned dataByteCnt )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
cmUdpNode_t* np;
if((np = _cmUnFindLabel(p,remoteNodeLabel)) == NULL )
return cmErrMsg(&p->err,kNodeNotFoundUnRC,"The remote node (label=%s) was not found.",cmStringNullGuard(remoteNodeLabel));
if( cmUdpSendTo(p->udpH, data, dataByteCnt, &np->addr ) != kOkUdpRC )
return cmErrMsg(&p->err,kSendFailUnRC,"An attempt to send to the remote node labeled:%s failed.",cmStringNullGuard(remoteNodeLabel));
return kOkUnRC;
}
cmUnRC_t cmUdpNetReceive( cmUdpNetH_t h, unsigned* msgCntPtr )
{
cmUnRC_t rc = kOkUnRC;
unsigned maxMsgCnt = 0;
unsigned msgCnt = 0;
if( msgCntPtr == NULL )
maxMsgCnt = cmInvalidCnt;
else
maxMsgCnt = *msgCntPtr;
cmUdpNet_t* p = _cmUnHandleToPtr(h);
if( cmUdpIsValid(p->udpH ) )
{
for(msgCnt=0; (maxMsgCnt==cmInvalidCnt || msgCnt<maxMsgCnt) && cmUdpAvailDataByteCount(p->udpH); ++msgCnt )
{
if( cmUdpGetAvailData(p->udpH,NULL,NULL,NULL) != kOkUdpRC )
{
rc = cmErrMsg(&p->err,kGetDataFailUnRC,"An attempt to get available message data failed.");
break;
}
}
}
if( msgCntPtr != NULL )
*msgCntPtr = msgCnt;
return rc;
}
cmUnRC_t cmUdpNetPrintNodes( cmUdpNetH_t h, cmRpt_t* rpt )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
cmUdpNode_t* np = p->list;
unsigned i;
for(i=0; np != NULL; np = np->link, ++i )
cmRptPrintf(rpt,"%5i %5i %s\n",i,np->id,np->label);
return kOkUnRC;
}
void cmUdpNetReport( cmUdpNetH_t h, cmRpt_t* rpt )
{
cmUdpNet_t* p = _cmUnHandleToPtr(h);
cmUdpReport(p->udpH,rpt);
}
//=========================================================================================
typedef struct
{
cmTimeSpec_t t;
unsigned localNodeId;
cmUdpNetH_t h;
} _cmUdpNetTestMsg_t;
void _cmUdpNetTestCb( void* cbArg, cmUdpNetH_t h, const char* data, unsigned dataByteCnt, unsigned remoteNodeId )
{
cmRpt_t* rpt = (cmRpt_t*)cbArg;
cmRptPrintf(rpt,"recv - id:%i %s\n",remoteNodeId, data);
}
void _cmUdpNetTestCb2( void* cbArg, cmUdpNetH_t h, const char* data, unsigned dataByteCnt, unsigned remoteNodeId )
{
_cmUdpNetTestMsg_t* m = (_cmUdpNetTestMsg_t*)data;
cmRpt_t* rpt = (cmRpt_t*)cbArg;
assert( dataByteCnt == sizeof(_cmUdpNetTestMsg_t));
if( m->localNodeId == cmUdpNetLocalNodeId(h) )
{
cmTimeSpec_t t;
cmTimeGet(&t);
cmRptPrintf(rpt,"elapsed:%i\n", cmTimeElapsedMicros(&m->t,&t));
}
else
{
cmRptPrintf(rpt,"echo\n");
cmUdpNetSendById(h,m->localNodeId,&m,sizeof(*m));
}
}
const cmChar_t* _cmUdpNetTestGetOpt( int argc, const char* argv[], const char* opt )
{
unsigned i;
for(i=0; i<argc; ++i)
if( strcmp(argv[i],opt) == 0 )
return argv[i+1];
return NULL;
}
cmRC_t cmUdpNetTest( cmCtx_t* ctx, int argc, const char* argv[] )
{
cmUdpNetH_t h = cmUdpNetNullHandle;
cmJsonH_t jsH = cmJsonNullHandle;
cmErr_t err;
cmRC_t rc = cmOkRC;
unsigned i = 0;
const cmChar_t* jsonFn;
const cmChar_t* remoteNodeLabel;
unsigned strCharCnt = 31;
cmChar_t str[ strCharCnt + 1 ];
bool msgFl = false;
enum { kUdpNetTestErrRC = 1 };
cmErrSetup(&err,&ctx->rpt,"UDP Net Test");
// get the JSON file name
if(( jsonFn = _cmUdpNetTestGetOpt(argc,argv,"-j")) == NULL)
return cmErrMsg(&err,kUdpNetTestErrRC,"No JSON file name was given.");
// get the remote node label to send too
if(( remoteNodeLabel= _cmUdpNetTestGetOpt(argc,argv,"-n")) == NULL )
return cmErrMsg(&err,kUdpNetTestErrRC,"No remote node label was given.");
// create the JSON tree
if( cmJsonInitializeFromFile(&jsH, jsonFn, ctx ) != kOkJsRC )
{
rc = cmErrMsg(&err,kUdpNetTestErrRC,"JSON file init failed on '%s'.",cmStringNullGuard(jsonFn));
goto errLabel;
}
if( msgFl )
{
// alloc/init and put the network into listening mode
if( cmUdpNetAllocJson(ctx,&h,jsH,_cmUdpNetTestCb2,&ctx->rpt,kListenUnFl) != kOkUnRC )
{
rc = cmErrMsg(&err,kUdpNetTestErrRC,"UDP alloc/init/listen failed.");
goto errLabel;
}
}
else
{
if( cmUdpNetAlloc(ctx,&h) != kOkUnRC )
return cmErrMsg(&err,kUdpNetTestErrRC,"UDP alloc failed.");
if( cmUdpNetInitJson(h,jsH,_cmUdpNetTestCb,&ctx->rpt) != kOkUnRC )
{
rc = cmErrMsg(&err,kUdpNetTestErrRC,"UDP net init failed.");
goto errLabel;
}
if( cmUdpNetEnableListen(h,true) != kOkUnRC )
{
rc = cmErrMsg(&err,kUdpNetTestErrRC,"UDP listen enable failed.");
goto errLabel;
}
}
cmRptPrintf(&ctx->rpt,"s=send p=print r=report\n");
char c;
while((c=getchar()) != 'q')
{
bool promptFl = true;
switch(c)
{
case 's':
{
// form the emessage
snprintf(str,strCharCnt,"msg=%i",i);
// send a message to the remote node
if( !msgFl )
if( cmUdpNetSendByLabel(h,remoteNodeLabel,str,strlen(str)+1 ) != kOkUnRC )
cmErrMsg(&err,kUdpNetTestErrRC,"UDP net send failed.");
++i;
}
break;
case 't':
{
_cmUdpNetTestMsg_t m;
cmTimeGet(&m.t);
m.localNodeId = cmUdpNetLocalNodeId(h);
m.h = h;
// send a message to the remote node
if( msgFl )
if( cmUdpNetSendByLabel(h,remoteNodeLabel,&m,sizeof(m) ) != kOkUnRC )
cmErrMsg(&err,kUdpNetTestErrRC,"UDP net send failed.");
}
break;
case 'p':
cmUdpNetPrintNodes(h,&ctx->rpt);
break;
case 'r':
cmUdpNetReport(h,&ctx->rpt);
break;
default:
promptFl = false;
}
cmUdpNetReceive(h,NULL);
if( promptFl )
cmRptPrintf(&ctx->rpt,"%i> ",i);
}
errLabel:
if( cmUdpNetFree(&h) != kOkUnRC )
rc = cmErrMsg(&err,kUdpNetTestErrRC,"UDP free failed.");
if( cmJsonFinalize(&jsH) != kOkJsRC )
rc = cmErrMsg(&err,kUdpNetTestErrRC,"JSON final fail.");
return rc;
}