libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmUdpPort.c 15KB

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