A messenger application for Raspberry Pi Zerofor A.U.TH (Real time Embedded systems).
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.
 
 
 
 
 

289 lines
9.4 KiB

  1. /*!
  2. * \file client.c
  3. * This file contains all the client and device search functionalities.
  4. *
  5. * \author Christos Choutouridis AEM:8997 <cchoutou@ece.auth.gr>
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <unistd.h>
  11. #include <time.h>
  12. #include <sys/select.h>
  13. #include <fcntl.h>
  14. #include <errno.h>
  15. #include "client.h"
  16. /*!
  17. * Transmit buffer
  18. */
  19. static char_t tx_buffer [MSG_TEXT_SIZE];
  20. /*!
  21. * Ping functionality. We use system and pass a test command in order to
  22. * find out if a particular device is on range.
  23. * @param dev The device to ping
  24. * @return The status of the operation
  25. * @arg true The device was on range and replyed to our ping
  26. * @arg false The device wasn't on range
  27. */
  28. static bool_t ping (devAEM_t dev) {
  29. char_t cmd[72];
  30. devIP_t ip = AEM2ip (dev);
  31. // ask host to ping and make a little pre-process before take the answer
  32. sprintf (cmd,
  33. "test $(ping -c1 -w%d %u.%u.%u.%u| grep received |cut -d' ' -f4) = 1",
  34. (int)settings.pingTimeout, ip.A, ip.B, ip.C, ip.D
  35. );
  36. return (system(cmd) == 0) ? true:false;
  37. }
  38. /*!
  39. * Search for devices on range and mark the time of first and last sight
  40. * @return The number of devices on range
  41. */
  42. static size_t seeker (void) {
  43. size_t cnt =0; // count devices on range
  44. sleep (settings.seekerInterval);
  45. log_debug ("Debug: Pinging devices...\n");
  46. devList_acquire ();
  47. for (int i=0 ; i<AEMLIST_SIZE ; ++i) {
  48. if (devList[i].dev == settings.me) {// Don't ping me, I'm not here, go away...
  49. devList[i].onRange = false;
  50. continue;
  51. }
  52. if (ping (devList[i].dev)) { // Noc noc....
  53. devList[i].onRange = true; // Who's there?
  54. ++cnt; // No one, bye bye!
  55. // Mark the time for first and last time we saw this device
  56. if (!devList[i].begin)
  57. devList[i].begin = time(NULL); // first time only
  58. devList[i].end = time(NULL); // every time
  59. devList_t cDev = devList[i];
  60. devList_release ();
  61. log_debug ("Debug: Device %u found\n", cDev.dev);
  62. devList_acquire ();
  63. }
  64. else
  65. devList[i].onRange = false; // Where is everybody?
  66. }
  67. statsTimesPrint (devList);
  68. devList_release ();
  69. log_debug ("Debug: %d devices found\n", cnt);
  70. return cnt;
  71. }
  72. /*!
  73. * @brief
  74. * Send message functionality
  75. * TCP socket communication for sending a message to device. We use non-blocking
  76. * connect/select in order to be able to timeout from select. We also use send with
  77. * timeout.
  78. *
  79. * @param dev The device to send the message
  80. * @param msg Pointer to message to send
  81. * @return The status of the operation
  82. * @note
  83. * We treat all error of this function as "non-fatal".
  84. */
  85. static bool_t sendMsg (devAEM_t dev, cMsg_t* msg) {
  86. int sock;
  87. sockaddr_in_t srvAddr;
  88. timeval_t timeout = settings.sendTimeout;
  89. long arg;
  90. bool_t ret = true; // have faith
  91. // Make address
  92. memset(&srvAddr, 0, sizeof (srvAddr));
  93. srvAddr.sin_family= AF_INET;
  94. srvAddr.sin_port= htons (settings.port);
  95. srvAddr.sin_addr.s_addr = htonl (devAEM2addr (dev));
  96. devIP_t ip = AEM2ip (dev);
  97. // Create socket for sending
  98. if ((sock = socket (PF_INET, SOCK_STREAM, 0)) == -1)
  99. return false;
  100. log_debug ("Debug: Socket for sending to %u.%u.%u.%u created\n", ip.A, ip.B, ip.C, ip.D);
  101. do {
  102. // Set non-blocking connect
  103. if((arg = fcntl(sock, F_GETFL, NULL)) < 0) {
  104. ret = false;
  105. log_debug ("Debug: Reading socket's file status failed\n");
  106. break;
  107. }
  108. if(fcntl(sock, F_SETFL, arg | O_NONBLOCK) < 0) {
  109. ret = false;
  110. log_debug ("Debug: Set socket to non-blocking mode failed\n");
  111. break;
  112. }
  113. log_debug ("Debug: Socket switched to non-blocking mode\n");
  114. // Trying to connect with timeout
  115. log_debug ("Debug: Try to connect with timeout\n");
  116. if (connect (sock, (sockaddr_t*)&srvAddr, sizeof(srvAddr)) == -1) {
  117. if (errno == EINPROGRESS) {
  118. fd_set myset;
  119. FD_ZERO (&myset);
  120. FD_SET (sock, &myset);
  121. if (select (sock+1, NULL, &myset, NULL, &timeout) <= 0) {
  122. ret = false;
  123. log_debug ("Debug: Connection failed in select()\n");
  124. break;
  125. }
  126. // Socket selected for write
  127. int valopt;
  128. socklen_t lon = sizeof(int);
  129. if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) {
  130. ret = false;
  131. log_debug ("Debug: Reading socket's options failed\n");
  132. break;
  133. }
  134. // Check the value returned...
  135. if (valopt) {
  136. ret = false;
  137. log_debug ("Debug: Delayed connection failed\n");
  138. break;
  139. }
  140. }
  141. else {
  142. ret = false;
  143. log_debug ("Debug: Connection failed in select()\n");
  144. break;
  145. }
  146. }
  147. log_debug ("Debug: Connection to %u.%u.%u.%u succeed\n", ip.A, ip.B, ip.C, ip.D);
  148. // Set to blocking mode again...
  149. if( (arg = fcntl(sock, F_GETFL, NULL)) < 0) {
  150. ret = false;
  151. log_debug ("Debug: Reading socket's file status failed\n");
  152. break;
  153. }
  154. arg &= (~O_NONBLOCK);
  155. if( fcntl(sock, F_SETFL, arg) < 0) {
  156. ret = false;
  157. log_debug ("Debug: Set socket back to blocking mode failed\n");
  158. break;
  159. }
  160. log_debug ("Debug: Socket switched to blocking mode\n");
  161. // send the data with timeout
  162. if (setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (void*)&timeout, sizeof(timeout)) == -1) {
  163. ret = false;
  164. log_debug ("Debug: Setting send timeout failed\n");
  165. break;
  166. }
  167. log_debug ("Debug: Setting send timeout succeed\n");
  168. size_t len = cMsg_serialize (msg, tx_buffer);
  169. if (send (sock, tx_buffer, len, MSG_CONFIRM) == -1) {
  170. ret = false;
  171. log_debug ("Debug: Sending failed\n");
  172. break;
  173. }
  174. log_debug ("Debug: Sending succeed\n");
  175. } while (0);
  176. close (sock);
  177. log_debug ("Debug: Closing socket\n");
  178. return ret;
  179. }
  180. /*!
  181. * @brief
  182. * Client functionality
  183. *
  184. * Creates a message in a random interval, seeks for devices on range
  185. * and loop in entire msgList in order to send all messages
  186. * @note
  187. * This function never returns
  188. */
  189. static void client (void) {
  190. msg_t msg; // new message buffer
  191. while (true) {
  192. // Idle until the time comes
  193. sleep (
  194. settings.msgIntervalMin +
  195. (rand() % (settings.msgIntervalMax - settings.msgIntervalMin))
  196. );
  197. memset ((void*)&msg, 0, sizeof (msg)); // create a new message
  198. cMsg_make (&msg.cMsg);
  199. msg.sender = settings.me;
  200. log_msg_new (&msg);
  201. statsUpdateCreate (&msg);
  202. msgList_acquire (); // try to lock resources
  203. mIter_t at = msgList_add (&msgList, &msg); // Add message to msgList
  204. msgList_release ();
  205. log_debug ("Debug: Message added to msgList at %d\n", at);
  206. msgList_acquire ();
  207. mIter_t it = msgList_begin (&msgList); // get a message iterator
  208. // for each message -> for each recipient
  209. for (size_t i=0 ; i<msgList_size(&msgList) ; ++i, msgList_preInc (&it)) {
  210. for (size_t j=0 ; j<AEMLIST_SIZE ; ++j) {
  211. // get current dev instance
  212. devList_acquire ();
  213. devList_t currentDev = devList[j];
  214. devList_release ();
  215. // check when to send
  216. if (currentDev.onRange // is on range
  217. && !msgList.m[it].recipients[j] // we haven't send the message to that device
  218. && msgList.m[it].cMsg.to != settings.me // the message it's not for me
  219. ) {
  220. if (sendMsg (currentDev.dev, &msgList.m[it].cMsg)) {
  221. msgList.m[it].recipients[j] = true;
  222. msgList_release ();
  223. statsUpdateOut (&msg, currentDev.dev);
  224. log_msg_out (&msg, currentDev.dev);
  225. log_debug ("Debug: Send message to device %u succeed\n", currentDev.dev);
  226. msgList_acquire ();
  227. }
  228. else {
  229. msgList_release ();
  230. log_debug ("Debug: Send message to device %u failed\n", currentDev.dev);
  231. msgList_acquire ();
  232. }
  233. //^ we try to send the message and mark the transmission on success
  234. // if send fail, don't worry it may succeed the next time.
  235. }
  236. }
  237. }
  238. msgList_release (); // Unlock resources
  239. if (statsPrint (&stats) == MSG_ERROR)
  240. log_error ("Error: Writing to statistics file failed\n");
  241. }
  242. return;
  243. }
  244. /*!
  245. * pthread wrapper for \sa seeker()
  246. * @param ptr
  247. */
  248. void* pthSeeker (void* ptr) {
  249. (void)&ptr; // use parameter
  250. while (true)
  251. seeker ();
  252. exit(1); // we should not be here
  253. return NULL;
  254. }
  255. /*!
  256. * pthread wrapper for \sa client()
  257. * @param ptr
  258. */
  259. void* pthClient (void* ptr) {
  260. (void)&ptr; // use parameter
  261. client ();
  262. exit(1); // we should not be here, client never returns
  263. return NULL;
  264. }