//| Copyright: (C) 2009-2020 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. #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; ierr, 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; inodeId != 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 || msgCntudpH); ++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; irpt,"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; }