libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmUdpPort.c 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #include "cmPrefix.h"
  4. #include "cmGlobal.h"
  5. #include "cmRpt.h"
  6. #include "cmErr.h"
  7. #include "cmCtx.h"
  8. #include "cmMem.h"
  9. #include "cmMallocDebug.h"
  10. #include "cmThread.h"
  11. #include <sys/socket.h>
  12. #include <netinet/in.h>
  13. #include <arpa/inet.h>
  14. #include <fcntl.h>
  15. #include <unistd.h> // close
  16. #include "cmUdpPort.h"
  17. #define cmUdp_SYS_ERR (-1)
  18. #define cmUdp_NULL_SOCK (-1)
  19. #ifndef HOST_NAME_MAX
  20. #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
  21. #endif
  22. enum
  23. {
  24. kIsConnectedUdpFl = 0x01,
  25. kIsBlockingUdpFl = 0x02,
  26. kQueueingUdpFl = 0x04
  27. };
  28. typedef struct
  29. {
  30. cmErr_t err;
  31. int sockH;
  32. cmUdpCallback_t cbFunc;
  33. void* cbArg;
  34. unsigned timeOutMs;
  35. unsigned flags;
  36. cmThreadH_t thH;
  37. cmTs1p1cH_t qH;
  38. unsigned recvBufByteCnt;
  39. char* tempBuf;
  40. unsigned timeOutCnt;
  41. unsigned recvCnt;
  42. unsigned queCbCnt;
  43. unsigned errCnt;
  44. struct sockaddr_in sockaddr;
  45. cmChar_t ntopBuf[ INET_ADDRSTRLEN+1 ]; // use INET6_ADDRSTRLEN for IPv6
  46. cmChar_t hnameBuf[ HOST_NAME_MAX+1 ];
  47. } cmUdp_t;
  48. cmUdpH_t cmUdpNullHandle = cmSTATIC_NULL_HANDLE;
  49. #define _cmUdpClear_errno() errno = 0
  50. cmUdp_t* _cmUdpHandleToPtr( cmUdpH_t h )
  51. {
  52. cmUdp_t* p = (cmUdp_t*)h.h;
  53. assert(p != NULL);
  54. return p;
  55. }
  56. cmUdpRC_t _cmUdpFinal( cmUdp_t* p )
  57. {
  58. cmUdpRC_t rc = kOkUdpRC;
  59. if( cmThreadIsValid(p->thH) )
  60. if( cmThreadDestroy(&p->thH) != kOkThRC )
  61. return cmErrMsg(&p->err,kThreadFailUdpRC,"Listener thread destroy failed.");
  62. if( cmTs1p1cIsValid(p->qH) )
  63. if( cmTs1p1cDestroy(&p->qH) != kOkThRC )
  64. cmErrMsg(&p->err,kQueueFailUdpRC,"Receive data queue destroy failed.");
  65. cmMemPtrFree(&p->tempBuf);
  66. // close the socket
  67. if( p->sockH != cmUdp_NULL_SOCK )
  68. {
  69. _cmUdpClear_errno();
  70. if( close(p->sockH) != 0 )
  71. cmErrSysMsg(&p->err,kSockCloseFailUdpRC,errno,"The socket close failed." );
  72. p->sockH = cmUdp_NULL_SOCK;
  73. }
  74. return rc;
  75. }
  76. cmUdpRC_t _cmUdpFree( cmUdp_t* p )
  77. {
  78. cmUdpRC_t rc;
  79. if((rc = _cmUdpFinal(p)) != kOkUdpRC )
  80. return rc;
  81. cmMemFree(p);
  82. return rc;
  83. }
  84. cmUdpRC_t _cmUdpInitAddr( cmUdp_t* p, const char* addrStr, cmUdpPort_t portNumber, struct sockaddr_in* retAddrPtr )
  85. {
  86. memset(retAddrPtr,0,sizeof(struct sockaddr_in));
  87. if( portNumber == kInvalidUdpPortNumber )
  88. return cmErrMsg(&p->err,kInvalidPortNumbUdpRC,"The port number %i cannot be used.",kInvalidUdpPortNumber);
  89. if( addrStr == NULL )
  90. retAddrPtr->sin_addr.s_addr = htonl(INADDR_ANY);
  91. else
  92. {
  93. _cmUdpClear_errno();
  94. if(inet_pton(AF_INET,addrStr,&retAddrPtr->sin_addr) == 0 )
  95. //if(( retAddrPtr->sin_addr.s_addr = inet_addr(addrStr)) == INADDR_NONE )
  96. return cmErrSysMsg(&p->err,kPtoNFailUdpRC,errno, "The network address string '%s' could not be converted to a netword address structure.",cmStringNullGuard(addrStr) );
  97. }
  98. //retAddrPtr->sin_len = sizeof(struct sockaddr_in);
  99. retAddrPtr->sin_family = AF_INET;
  100. retAddrPtr->sin_port = htons(portNumber);
  101. return kOkUdpRC;
  102. }
  103. cmUdpRC_t _cmUdpConnect( cmUdp_t* p, const char* remoteAddr, cmUdpPort_t remotePort )
  104. {
  105. struct sockaddr_in addr;
  106. cmUdpRC_t rc;
  107. // create the remote address
  108. if((rc = _cmUdpInitAddr(p, remoteAddr, remotePort, &addr )) != kOkUdpRC )
  109. return rc;
  110. _cmUdpClear_errno();
  111. // ... and connect this socket to the remote address/port
  112. if( connect(p->sockH, (struct sockaddr*)&addr, sizeof(addr)) == cmUdp_SYS_ERR )
  113. return cmErrSysMsg(&p->err,kSockConnectFailUdpRC, errno, "Socket connect failed." );
  114. p->flags = cmSetFlag(p->flags,kIsConnectedUdpFl);
  115. return rc;
  116. }
  117. cmUdpRC_t cmUdpAlloc( cmCtx_t* ctx, cmUdpH_t* hp )
  118. {
  119. cmUdpRC_t rc;
  120. if((rc = cmUdpFree(hp)) != kOkUdpRC )
  121. return rc;
  122. cmUdp_t* p = cmMemAllocZ(cmUdp_t,1);
  123. cmErrSetup(&p->err,&ctx->rpt,"UDP Port");
  124. p->sockH = cmUdp_NULL_SOCK;
  125. hp->h = p;
  126. return rc;
  127. }
  128. cmUdpRC_t cmUdpFree( cmUdpH_t* hp )
  129. {
  130. cmUdpRC_t rc = kOkUdpRC;
  131. if( hp == NULL || cmUdpIsValid(*hp)==false)
  132. return rc;
  133. cmUdp_t* p = _cmUdpHandleToPtr(*hp);
  134. if((rc = _cmUdpFree(p)) != kOkUdpRC )
  135. return rc;
  136. hp->h = NULL;
  137. return rc;
  138. }
  139. cmUdpRC_t cmUdpInit(
  140. cmUdpH_t h,
  141. cmUdpPort_t port,
  142. unsigned flags,
  143. cmUdpCallback_t cbFunc,
  144. void* cbArg,
  145. const char* remoteAddr,
  146. cmUdpPort_t remotePort,
  147. unsigned recvBufByteCnt,
  148. unsigned timeOutMs )
  149. {
  150. cmUdpRC_t rc;
  151. cmUdp_t* p = _cmUdpHandleToPtr(h);
  152. if((rc = _cmUdpFinal(p)) != kOkUdpRC )
  153. return rc;
  154. _cmUdpClear_errno();
  155. // get a handle to the socket
  156. if(( p->sockH = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == cmUdp_SYS_ERR )
  157. return cmErrSysMsg(&p->err, kSockCreateFailUdpRC, errno, "Socket create failed." );
  158. // create the local address
  159. if((rc = _cmUdpInitAddr(p, NULL, port, &p->sockaddr )) != kOkUdpRC )
  160. goto errLabel;
  161. // bind the socket to a local address/port
  162. if( (bind( p->sockH, (struct sockaddr*)&p->sockaddr, sizeof(p->sockaddr))) == cmUdp_SYS_ERR )
  163. {
  164. rc = cmErrSysMsg(&p->err,kSockBindFailUdpRC,errno,"Socket bind failed." );
  165. goto errLabel;
  166. }
  167. // if a remote addr was given connect this socket to it
  168. if( remoteAddr != NULL )
  169. if((rc = _cmUdpConnect(p,remoteAddr,remotePort)) != kOkUdpRC )
  170. goto errLabel;
  171. // if this socket should block
  172. if( cmIsFlag(flags,kBlockingUdpFl) )
  173. {
  174. struct timeval timeOut;
  175. // set the socket time out
  176. timeOut.tv_sec = timeOutMs/1000;
  177. timeOut.tv_usec = (timeOutMs - (timeOut.tv_sec * 1000)) * 1000;
  178. if( setsockopt( p->sockH, SOL_SOCKET, SO_RCVTIMEO, &timeOut, sizeof(timeOut) ) == cmUdp_SYS_ERR )
  179. {
  180. rc = cmErrSysMsg(&p->err,kSockOptSetFailUdpRC,errno, "Attempt to set the socket timeout failed." );
  181. goto errLabel;
  182. }
  183. p->flags = cmSetFlag(p->flags,kIsBlockingUdpFl);
  184. }
  185. else
  186. {
  187. int opts;
  188. // get the socket options flags
  189. if( (opts = fcntl(p->sockH,F_GETFL)) < 0 )
  190. {
  191. rc = cmErrSysMsg(&p->err,kSockOptSetFailUdpRC,errno, "Attempt to get the socket options flags failed." );
  192. goto errLabel;
  193. }
  194. opts = (opts | O_NONBLOCK);
  195. // set the socket options flags
  196. if(fcntl(p->sockH,F_SETFL,opts) < 0)
  197. {
  198. rc = cmErrSysMsg(&p->err,kSockOptSetFailUdpRC,errno, "Attempt to set the socket to non-blocking failed." );
  199. goto errLabel;
  200. }
  201. }
  202. // if broadcast option was requested.
  203. if( cmIsFlag(flags,kBroadcastUdpFl) )
  204. {
  205. int bcastFl = 1;
  206. if( setsockopt( p->sockH, SOL_SOCKET, SO_BROADCAST, &bcastFl, sizeof(bcastFl) ) == cmUdp_SYS_ERR )
  207. {
  208. rc = cmErrSysMsg(&p->err,kSockOptSetFailUdpRC,errno, "Attempt to set the socket broadcast attribute failed." );
  209. goto errLabel;
  210. }
  211. }
  212. if( recvBufByteCnt != 0 )
  213. p->tempBuf = cmMemAlloc(char,recvBufByteCnt );
  214. p->timeOutMs = timeOutMs;
  215. p->cbFunc = cbFunc;
  216. p->cbArg = cbArg;
  217. p->recvBufByteCnt = recvBufByteCnt;
  218. p->timeOutCnt = 0;
  219. p->recvCnt = 0;
  220. p->queCbCnt = 0;
  221. p->errCnt = 0;
  222. if( cmIsFlag(flags,kNoQueueUdpFl) == false )
  223. p->flags = cmSetFlag(p->flags,kQueueingUdpFl);
  224. errLabel:
  225. if( rc != kOkUdpRC )
  226. _cmUdpFree(p);
  227. return rc;
  228. }
  229. cmUdpRC_t cmUdpFinal( cmUdpH_t h )
  230. {
  231. cmUdp_t* p = _cmUdpHandleToPtr(h);
  232. return _cmUdpFinal(p);
  233. }
  234. bool cmUdpIsValid( cmUdpH_t h )
  235. { return h.h != NULL; }
  236. const struct sockaddr_in* cmUdpLocalAddr( cmUdpH_t h )
  237. {
  238. cmUdp_t* p = _cmUdpHandleToPtr(h);
  239. return &p->sockaddr;
  240. }
  241. cmUdpRC_t cmUdpConnect( cmUdpH_t h, const char* remoteAddr, cmUdpPort_t remotePort )
  242. {
  243. cmUdp_t* p = _cmUdpHandleToPtr(h);
  244. return _cmUdpConnect(p,remoteAddr,remotePort);
  245. }
  246. cmUdpRC_t cmUdpSend( cmUdpH_t h, const char* data, unsigned dataByteCnt )
  247. {
  248. cmUdp_t* p = _cmUdpHandleToPtr(h);
  249. _cmUdpClear_errno();
  250. if( cmIsFlag(p->flags,kIsConnectedUdpFl) == false )
  251. return cmErrMsg(&p->err,kNotConnectedUdpRC,"cmUdpSend() only works with connected sockets.");
  252. if( send( p->sockH, data, dataByteCnt, 0 ) == cmUdp_SYS_ERR )
  253. return cmErrSysMsg(&p->err,kSockSendFailUdpRC,errno,"Send failed.");
  254. return kOkUdpRC;
  255. }
  256. cmUdpRC_t cmUdpSendTo( cmUdpH_t h, const char* data, unsigned dataByteCnt, const struct sockaddr_in* remoteAddr )
  257. {
  258. cmUdp_t* p = _cmUdpHandleToPtr(h);
  259. _cmUdpClear_errno();
  260. if( sendto(p->sockH, data, dataByteCnt, 0, (struct sockaddr*)remoteAddr, sizeof(*remoteAddr)) == cmUdp_SYS_ERR )
  261. return cmErrSysMsg(&p->err,kSockSendFailUdpRC,errno,"SendTo failed.");
  262. return kOkUdpRC;
  263. }
  264. cmUdpRC_t cmUdpSend2( cmUdpH_t h, const char* data, unsigned dataByteCnt, const char* remoteAddr, cmUdpPort_t remotePort )
  265. {
  266. cmUdpRC_t rc;
  267. cmUdp_t* p = _cmUdpHandleToPtr(h);
  268. struct sockaddr_in addr;
  269. if((rc = _cmUdpInitAddr(p,remoteAddr,remotePort,&addr)) != kOkUdpRC )
  270. return rc;
  271. return cmUdpSendTo( h, data, dataByteCnt, &addr );
  272. }
  273. cmUdpRC_t cmUdpRecv( cmUdpH_t h, char* data, unsigned dataByteCnt, struct sockaddr_in* fromAddr, unsigned* recvByteCntPtr )
  274. {
  275. cmUdp_t* p = _cmUdpHandleToPtr(h);
  276. cmUdpRC_t rc = kOkUdpRC;
  277. ssize_t retVal = 0;
  278. socklen_t sizeOfRemoteAddr = fromAddr==NULL ? 0 : sizeof(struct sockaddr_in);
  279. _cmUdpClear_errno();
  280. if( recvByteCntPtr != NULL )
  281. *recvByteCntPtr = 0;
  282. if((retVal = recvfrom(p->sockH, data, dataByteCnt, 0, (struct sockaddr*)fromAddr, &sizeOfRemoteAddr )) == cmUdp_SYS_ERR )
  283. return cmErrSysMsg(&p->err,kSockRecvFailUdpRC,errno,"recvFrom() failed.");
  284. if( recvByteCntPtr != NULL )
  285. *recvByteCntPtr = retVal;
  286. return rc;
  287. }
  288. bool _cmUdpThreadCb(void* param)
  289. {
  290. cmUdp_t* p = (cmUdp_t*)param;
  291. fd_set rdSet;
  292. struct timeval timeOut;
  293. // setup the select() call
  294. FD_ZERO(&rdSet);
  295. FD_SET(p->sockH, &rdSet );
  296. timeOut.tv_sec = p->timeOutMs/1000;
  297. timeOut.tv_usec = (p->timeOutMs - (timeOut.tv_sec * 1000)) * 1000;
  298. // NOTE; select() takes the highest socket value plus one of all the sockets in all the sets.
  299. switch( select(p->sockH+1,&rdSet,NULL,NULL,&timeOut) )
  300. {
  301. case -1: // error
  302. if( errno != EINTR )
  303. cmErrSysMsg(&p->err,kSockSelectFailUdpRC,errno,"Select failed.");
  304. ++p->errCnt;
  305. break;
  306. case 0: // select() timed out
  307. ++p->timeOutCnt;
  308. break;
  309. case 1: // (> 0) count of ready descripters
  310. if( FD_ISSET(p->sockH,&rdSet) )
  311. {
  312. struct sockaddr_in remoteAddr;
  313. socklen_t addrByteCnt = sizeof(remoteAddr);
  314. ssize_t retByteCnt;
  315. _cmUdpClear_errno();
  316. ++p->recvCnt;
  317. // recv the incoming msg into p->tempBuf[]
  318. if(( retByteCnt = recvfrom( p->sockH, p->tempBuf, p->recvBufByteCnt, 0, (struct sockaddr*)&remoteAddr, &addrByteCnt )) == cmUdp_SYS_ERR )
  319. cmErrSysMsg(&p->err,kSockRecvFailUdpRC,errno,"recvfrom() failed.");
  320. else
  321. {
  322. // check for overflow
  323. if( retByteCnt == p->recvBufByteCnt )
  324. cmErrMsg(&p->err,kRecvBufOverflowUdpRC,"The receive buffer requires more than %i bytes.",p->recvBufByteCnt);
  325. else
  326. {
  327. // if queueing is enabled
  328. if( cmIsFlag(p->flags,kQueueingUdpFl ) )
  329. {
  330. // enqueue the msg - with the source address appended after the data
  331. const void* msgPtrArray[] = { p->tempBuf, &remoteAddr, p->tempBuf };
  332. unsigned msgByteCntArray[] = { retByteCnt, sizeof(remoteAddr) };
  333. if( cmTs1p1cEnqueueSegMsg( p->qH, msgPtrArray, msgByteCntArray, 2 ) != kOkThRC )
  334. cmErrMsg(&p->err,kQueueFailUdpRC,"A received msg containing %i bytes was not queued.",retByteCnt);
  335. }
  336. else // if queueing is not enabled - transmit the data directly via the callback
  337. if( p->cbFunc != NULL )
  338. {
  339. p->cbFunc(p->cbArg,p->tempBuf,retByteCnt,&remoteAddr);
  340. }
  341. }
  342. }
  343. }
  344. break;
  345. default:
  346. { assert(0); }
  347. } // switch
  348. return true;
  349. }
  350. cmRC_t _cmUdpQueueCb(void* userCbPtr, unsigned msgByteCnt, const void* msgDataPtr )
  351. {
  352. cmUdp_t* p = (cmUdp_t*)userCbPtr;
  353. if( p->cbFunc != NULL )
  354. {
  355. struct sockaddr_in addr;
  356. assert( msgByteCnt >= sizeof(addr));
  357. const char* dataPtr = (const char*)msgDataPtr;
  358. // the address of the data source is apppended to the data bytes.
  359. const char* addrPtr = dataPtr + msgByteCnt - sizeof(addr);
  360. memcpy(&addr,addrPtr,sizeof(addr));
  361. // make the receive callback
  362. p->cbFunc(p->cbArg,dataPtr,msgByteCnt-sizeof(addr),&addr);
  363. ++p->queCbCnt;
  364. }
  365. return cmOkRC;
  366. }
  367. cmUdpRC_t cmUdpEnableListen( cmUdpH_t h, bool enableFl )
  368. {
  369. cmUdp_t* p = _cmUdpHandleToPtr(h);
  370. if( cmThreadIsValid(p->thH) == false && enableFl == true)
  371. {
  372. if(cmThreadCreate(&p->thH,_cmUdpThreadCb,p,p->err.rpt) != kOkThRC )
  373. return cmErrMsg(&p->err,kThreadFailUdpRC,"Listener thread create failed.");
  374. if(cmTs1p1cCreate(&p->qH,p->recvBufByteCnt,_cmUdpQueueCb,p,p->err.rpt) != kOkThRC )
  375. return cmErrMsg(&p->err,kQueueFailUdpRC,"Listener data queue create failed.");
  376. }
  377. if( cmThreadIsValid(p->thH) )
  378. if( cmThreadPause( p->thH, enableFl ? 0 : kPauseThFl ) != kOkThRC )
  379. return cmErrMsg(&p->err,kThreadFailUdpRC,"The listener thread failed to %s.", enableFl ? "pause" : "un-pause" );
  380. return kOkUdpRC;
  381. }
  382. bool cmUdpIsQueueEnabled( cmUdpH_t h )
  383. {
  384. cmUdp_t* p = _cmUdpHandleToPtr(h);
  385. return cmIsFlag(p->flags,kQueueingUdpFl);
  386. }
  387. void cmUdpQueueEnable( cmUdpH_t h, bool enableFl )
  388. {
  389. cmUdp_t* p = _cmUdpHandleToPtr(h);
  390. p->flags = cmSetFlag(p->flags,kQueueingUdpFl);
  391. }
  392. unsigned cmUdpAvailDataByteCount( cmUdpH_t h )
  393. {
  394. cmUdp_t* p = _cmUdpHandleToPtr(h);
  395. return cmTs1p1cIsValid(p->qH) ? cmTs1p1cDequeueMsgByteCount( p->qH ) : 0;
  396. }
  397. cmUdpRC_t cmUdpGetAvailData( cmUdpH_t h, char* data, unsigned* dataByteCntPtr, struct sockaddr_in* fromAddr )
  398. {
  399. cmUdp_t* p = _cmUdpHandleToPtr(h);
  400. unsigned availByteCnt;
  401. // if a received msg is queued
  402. if( (availByteCnt = cmTs1p1cAvailByteCount(p->qH)) > 0 )
  403. {
  404. // all msg's must have at least a source address
  405. assert( availByteCnt >= sizeof(*fromAddr) );
  406. // get the size of the return buffer (or 0 if there is no return buffer)
  407. unsigned dataByteCnt = (data != NULL && dataByteCntPtr != NULL) ? *dataByteCntPtr : 0;
  408. if( dataByteCnt == 0 )
  409. data = NULL;
  410. // dequeue the msg - if data==NULL then the data will be returned by
  411. // a call to the callback function provided in cmUdpAlloc().
  412. if( cmTs1p1cDequeueMsg(p->qH, data, dataByteCnt ) != kOkThRC )
  413. return cmErrMsg(&p->err,kQueueFailUdpRC,"Data dequeue failed.");
  414. // if a return buffer was given
  415. if( data != NULL )
  416. {
  417. assert( dataByteCntPtr != NULL );
  418. // the source address is appended to the end of the data
  419. const char* addrPtr = data + availByteCnt - sizeof(*fromAddr);
  420. // copy out the source address
  421. if( fromAddr != NULL )
  422. memcpy(fromAddr,addrPtr,sizeof(*fromAddr));
  423. // subtract the address size from the total msg size
  424. *dataByteCntPtr = availByteCnt - sizeof(*fromAddr);
  425. }
  426. }
  427. return kOkUdpRC;
  428. }
  429. void cmUdpReport( cmUdpH_t h, cmRpt_t* rpt )
  430. {
  431. cmUdp_t* p = _cmUdpHandleToPtr(h);
  432. cmRptPrintf(rpt,"time-out:%i recv:%i queue cb:%i\n",p->timeOutCnt,p->recvCnt,p->queCbCnt);
  433. }
  434. cmUdpRC_t cmUdpInitAddr( cmUdpH_t h, const char* addrStr, cmUdpPort_t portNumber, struct sockaddr_in* retAddrPtr )
  435. {
  436. cmUdp_t* p = _cmUdpHandleToPtr(h);
  437. return _cmUdpInitAddr(p,addrStr,portNumber,retAddrPtr);
  438. }
  439. const cmChar_t* cmUdpAddrToString( cmUdpH_t h, const struct sockaddr_in* addr )
  440. {
  441. cmUdp_t* p = _cmUdpHandleToPtr(h);
  442. _cmUdpClear_errno();
  443. if( inet_ntop(AF_INET, &(addr->sin_addr), p->ntopBuf, INET_ADDRSTRLEN) == NULL)
  444. {
  445. cmErrSysMsg(&p->err,kNtoPFailUdpRC,errno, "Network address to string conversion failed." );
  446. return NULL;
  447. }
  448. p->ntopBuf[INET_ADDRSTRLEN]=0;
  449. return p->ntopBuf;
  450. }
  451. bool cmUdpAddrIsEqual( const struct sockaddr_in* a0, const struct sockaddr_in* a1 )
  452. {
  453. return a0->sin_family == a1->sin_family
  454. && a0->sin_port == a1->sin_port
  455. && memcmp(&a0->sin_addr,&a1->sin_addr,sizeof(a0->sin_addr))==0;
  456. }
  457. const cmChar_t* cmUdpHostName( cmUdpH_t h )
  458. {
  459. cmUdp_t* p = _cmUdpHandleToPtr(h);
  460. _cmUdpClear_errno();
  461. if( gethostname(p->hnameBuf,HOST_NAME_MAX) != 0 )
  462. {
  463. cmErrSysMsg(&p->err,kHostNameFailUdpRC,errno, "gethostname() failed." );
  464. return NULL;
  465. }
  466. p->hnameBuf[HOST_NAME_MAX] = 0;
  467. return p->hnameBuf;
  468. }
  469. //=======================================================================================
  470. void cmUdpCallbackTest( void* cbArg, const char* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
  471. {
  472. printf("%s\n",data);
  473. }
  474. cmUdpRC_t cmUdpTest( cmCtx_t* ctx, const char* remoteIpAddr, cmUdpPort_t port )
  475. {
  476. cmUdpRC_t rc = kOkUdpRC;
  477. cmUdpH_t h = cmUdpNullHandle;
  478. cmErr_t* err = &ctx->err;
  479. unsigned recvBufByteCnt = 4096;
  480. unsigned recvTimeOutMs = 50;
  481. unsigned udpFlags = kNonBlockingUdpFl | kBroadcastUdpFl | kNoQueueUdpFl;
  482. unsigned strN = 1024;
  483. char str[ strN ];
  484. struct sockaddr_in addr;
  485. // allocate the UDP port
  486. if((rc = cmUdpAlloc(ctx, &h )) != kOkUdpRC )
  487. return cmErrMsg(err,rc,"UDP port allocate failed.");
  488. // initialize the UDP port
  489. if((rc = cmUdpInit( h, port, udpFlags, cmUdpCallbackTest, NULL, NULL, 0, recvBufByteCnt, recvTimeOutMs )) != kOkUdpRC )
  490. {
  491. rc = cmErrMsg(err,rc,"UPD port initialzation failed.");
  492. goto errLabel;
  493. }
  494. // convert the IP address to a sockaddr_in
  495. if((rc = cmUdpInitAddr(h, remoteIpAddr, port, &addr )) != kOkUdpRC )
  496. {
  497. rc = cmErrMsg(err,rc,"IP address conversion failed.");
  498. goto errLabel;
  499. }
  500. // start up the UDP port listening thread
  501. if((rc = cmUdpEnableListen(h, true )) != kOkUdpRC )
  502. {
  503. rc = cmErrMsg(err,rc,"UDP port switch to listen mode failed.");
  504. goto errLabel;
  505. }
  506. printf("q=quit");
  507. while(1)
  508. {
  509. printf("$");
  510. fflush(stdout);
  511. scanf("%s",str);
  512. if( strlen(str) > 0 )
  513. {
  514. if( strcmp(str,"q")==0 )
  515. break;
  516. if((rc = cmUdpSendTo(h,str,strlen(str)+1,&addr)) != kOkUdpRC )
  517. {
  518. rc = cmErrMsg(err,rc,"UDP send failed.");
  519. goto errLabel;
  520. }
  521. }
  522. }
  523. errLabel:
  524. if((rc = cmUdpFree(&h)) != kOkUdpRC )
  525. rc = cmErrMsg(err,rc,"UDP port free failed.");
  526. return rc;
  527. }
  528. cmUdpRC_t cmUdpTestV( cmCtx_t* ctx, unsigned argc, const char* argv[])
  529. {
  530. if( argc != 2 )
  531. return cmErrMsg(&ctx->err,kTestFailUdpRC,"cmUdpTestV usage: argv[0]=<IP Addr> argv[1]=<Socket Port Number>");
  532. const char* ipAddr = argv[0];
  533. cmUdpPort_t port = atoi(argv[1]);
  534. cmRptPrintf(&ctx->rpt,"UDP Test Sending To: %s port:%i\n",ipAddr,port);
  535. return cmUdpTest(ctx,ipAddr,port);
  536. }