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

265 lignes
8.9 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. log_debug ("Debug: Pinging devices...\n");
  45. for (int i=0 ; i<AEMLIST_SIZE ; ++i) {
  46. if (devList[i].dev == settings.me) {// Don't ping me, I'm not here, go away...
  47. devList[i].onRange = false;
  48. continue;
  49. }
  50. if (ping (devList[i].dev)) { // Noc noc....
  51. devList[i].onRange = true; // Who's there?
  52. ++cnt; // No one, bye bye!
  53. // Mark the time for first and last time we saw this device
  54. if (!devList[i].begin)
  55. devList[i].begin = time(NULL); // first time only
  56. devList[i].end = time(NULL); // every time
  57. log_debug ("Debug: Device %u found\n", devList[i].dev);
  58. }
  59. else
  60. devList[i].onRange = false; // Where is everybody?
  61. }
  62. log_debug ("Debug: %d devices found\n", cnt);
  63. return cnt;
  64. }
  65. /*!
  66. * @brief
  67. * Send message functionality
  68. * TCP socket communication for sending a message to device. We use non-blocking
  69. * connect/select in order to be able to timeout from select. We also use send with
  70. * timeout.
  71. *
  72. * @param dev The device to send the message
  73. * @param msg Pointer to message to send
  74. * @return The status of the operation
  75. * @note
  76. * We treat all error of this function as "non-fatal".
  77. */
  78. static bool_t sendMsg (devAEM_t dev, cMsg_t* msg) {
  79. int sock;
  80. sockaddr_in_t srvAddr;
  81. timeval_t timeout = settings.sendTimeout;
  82. long arg;
  83. bool_t ret = true; // have faith
  84. // Make address
  85. memset(&srvAddr, 0, sizeof (srvAddr));
  86. srvAddr.sin_family= AF_INET;
  87. srvAddr.sin_port= htons (settings.port);
  88. srvAddr.sin_addr.s_addr = htonl (devAEM2addr (dev));
  89. devIP_t ip = AEM2ip (dev);
  90. // Create socket for sending
  91. if ((sock = socket (PF_INET, SOCK_STREAM, 0)) == -1)
  92. return false;
  93. log_debug ("Debug: Socket for sending to %u.%u.%u.%u created\n", ip.A, ip.B, ip.C, ip.D);
  94. do {
  95. // Set non-blocking connect
  96. if((arg = fcntl(sock, F_GETFL, NULL)) < 0) {
  97. ret = false;
  98. log_debug ("Debug: Reading socket's file status failed\n");
  99. break;
  100. }
  101. if(fcntl(sock, F_SETFL, arg | O_NONBLOCK) < 0) {
  102. ret = false;
  103. log_debug ("Debug: Set socket to non-blocking mode failed\n");
  104. break;
  105. }
  106. log_debug ("Debug: Socket switched to non-blocking mode\n");
  107. // Trying to connect with timeout
  108. log_debug ("Debug: Try to connect with timeout\n");
  109. if (connect (sock, (sockaddr_t*)&srvAddr, sizeof(srvAddr)) == -1) {
  110. if (errno == EINPROGRESS) {
  111. fd_set myset;
  112. FD_ZERO (&myset);
  113. FD_SET (sock, &myset);
  114. if (select (sock+1, NULL, &myset, NULL, &timeout) <= 0) {
  115. ret = false;
  116. log_debug ("Debug: Connection failed in select()\n");
  117. break;
  118. }
  119. // Socket selected for write
  120. int valopt;
  121. socklen_t lon = sizeof(int);
  122. if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon) < 0) {
  123. ret = false;
  124. log_debug ("Debug: Reading socket's options failed\n");
  125. break;
  126. }
  127. // Check the value returned...
  128. if (valopt) {
  129. ret = false;
  130. log_debug ("Debug: Delayed connection failed\n");
  131. break;
  132. }
  133. }
  134. else {
  135. ret = false;
  136. log_debug ("Debug: Connection failed in select()\n");
  137. break;
  138. }
  139. }
  140. log_debug ("Debug: Connection to %u.%u.%u.%u succeed\n", ip.A, ip.B, ip.C, ip.D);
  141. // Set to blocking mode again...
  142. if( (arg = fcntl(sock, F_GETFL, NULL)) < 0) {
  143. ret = false;
  144. log_debug ("Debug: Reading socket's file status failed\n");
  145. break;
  146. }
  147. arg &= (~O_NONBLOCK);
  148. if( fcntl(sock, F_SETFL, arg) < 0) {
  149. ret = false;
  150. log_debug ("Debug: Set socket back to blocking mode failed\n");
  151. break;
  152. }
  153. log_debug ("Debug: Socket switched to blocking mode\n");
  154. // send the data with timeout
  155. if (setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (void*)&timeout, sizeof(timeout)) == -1) {
  156. ret = false;
  157. log_debug ("Debug: Setting send timeout failed\n");
  158. break;
  159. }
  160. log_debug ("Debug: Setting send timeout succeed\n");
  161. size_t len = cMsg_serialize (msg, tx_buffer);
  162. if (send (sock, tx_buffer, len, MSG_CONFIRM) == -1) {
  163. ret = false;
  164. log_debug ("Debug: Sending failed\n");
  165. break;
  166. }
  167. log_debug ("Debug: Sending succeed\n");
  168. } while (0);
  169. close (sock);
  170. log_debug ("Debug: Closing socket\n");
  171. return ret;
  172. }
  173. /*!
  174. * @brief
  175. * Client functionality
  176. *
  177. * Creates a message in a random interval, seeks for devices on range
  178. * and loop in entire msgList in order to send all messages
  179. * @note
  180. * This function never returns
  181. */
  182. static void client (void) {
  183. msg_t msg; // new message buffer
  184. while (true) {
  185. // Idle until the time comes
  186. sleep (settings.msgInterval + (rand()%settings.msgRand));
  187. memset ((void*)&msg, 0, sizeof (msg)); // create a new message
  188. cMsg_make (&msg.cMsg);
  189. msg.sender = settings.me;
  190. log_msg_new (&msg);
  191. statsUpdateCreate (&msg);
  192. msgList_acquire (); // try to lock resources
  193. mIter_t at = msgList_add (&msgList, &msg); // Add message to msgList
  194. msgList_release ();
  195. log_debug ("Debug: Message added to msgList at %d\n", at);
  196. if (!seeker ()) // If we are alone skip the rest
  197. continue;
  198. log_debug ("Debug: Devices found on range\n");
  199. msgList_acquire ();
  200. mIter_t it = msgList_begin (&msgList); // get a message iterator
  201. // for each message -> for each recipient
  202. for (size_t i=0 ; i<msgList_size(&msgList) ; ++i, msgList_preInc (&it)) {
  203. for (size_t j=0 ; j<AEMLIST_SIZE ; ++j) {
  204. // check when to send
  205. if (devList[j].onRange // is on range
  206. && !msgList.m[it].recipients[j] // we haven't send the message to that device
  207. && msgList.m[it].cMsg.to != settings.me // the message it's not for me
  208. ) {
  209. if (sendMsg (devList[j].dev, &msgList.m[it].cMsg)) {
  210. msgList.m[it].recipients[j] = true;
  211. msgList_release ();
  212. statsUpdateOut (&msg, devList[j].dev);
  213. log_msg_out (&msg, devList[j].dev);
  214. log_debug ("Debug: Send message to device %u succeed\n", devList[j].dev);
  215. msgList_acquire ();
  216. }
  217. else {
  218. msgList_release ();
  219. log_debug ("Debug: Send message to device %u failed\n", devList[j].dev);
  220. msgList_acquire ();
  221. }
  222. //^ we try to send the message and mark the transmission on success
  223. // if send fail, don't worry it may succeed the next time.
  224. }
  225. }
  226. }
  227. msgList_release (); // Unlock resources
  228. if (statsPrint (&stats) == MSG_ERROR)
  229. log_error ("Error: Writing to statistics file failed\n");
  230. }
  231. return;
  232. }
  233. /*!
  234. * pthread wrapper for \sa client()
  235. * @param ptr
  236. */
  237. void* pthClient (void* ptr) {
  238. (void)&ptr; // use parameter
  239. client ();
  240. exit(1); // we should not be here, client never returns
  241. return NULL;
  242. }