A messenger application for Raspberry Pi Zerofor A.U.TH (Real time Embedded systems).
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 

289 linhas
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. }