Smart-Home am Beispiel der Präsenzerkennung im Raum Projektarbeit Lennart Heimbs, Johannes Krug, Sebastian Dohle und Kevin Holzschuh bei Prof. Oliver Hofmann SS2019
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.

EthernetClient.cpp 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * The MySensors Arduino library handles the wireless radio link and protocol
  3. * between your home built sensors/actuators and HA controller of choice.
  4. * The sensors forms a self healing radio network with optional repeaters. Each
  5. * repeater and gateway builds a routing tables in EEPROM which keeps track of the
  6. * network topology allowing messages to be routed to nodes.
  7. *
  8. * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
  9. * Copyright (C) 2013-2018 Sensnology AB
  10. * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
  11. *
  12. * Documentation: http://www.mysensors.org
  13. * Support Forum: http://forum.mysensors.org
  14. *
  15. * This program is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU General Public License
  17. * version 2 as published by the Free Software Foundation.
  18. *
  19. * Based on Arduino ethernet library, Copyright (c) 2010 Arduino LLC. All right reserved.
  20. */
  21. #include "EthernetClient.h"
  22. #include <cstdio>
  23. #include <sys/socket.h>
  24. #include <sys/ioctl.h>
  25. #include <linux/sockios.h>
  26. #include <netdb.h>
  27. #include <arpa/inet.h>
  28. #include <cstring>
  29. #include <unistd.h>
  30. #include <sys/time.h>
  31. #include <netinet/tcp.h>
  32. #include <errno.h>
  33. #include "log.h"
  34. EthernetClient::EthernetClient() : _sock(-1)
  35. {
  36. }
  37. EthernetClient::EthernetClient(int sock) : _sock(sock)
  38. {
  39. }
  40. int EthernetClient::connect(const char* host, uint16_t port)
  41. {
  42. struct addrinfo hints, *servinfo, *localinfo, *p;
  43. int rv;
  44. char s[INET6_ADDRSTRLEN];
  45. char port_str[6];
  46. bool use_bind = (_srcip != 0);
  47. close();
  48. memset(&hints, 0, sizeof hints);
  49. hints.ai_family = AF_UNSPEC;
  50. hints.ai_socktype = SOCK_STREAM;
  51. sprintf(port_str, "%hu", port);
  52. if ((rv = getaddrinfo(host, port_str, &hints, &servinfo)) != 0) {
  53. logError("getaddrinfo: %s\n", gai_strerror(rv));
  54. return -1;
  55. }
  56. if (use_bind) {
  57. if ((rv = getaddrinfo(_srcip.toString().c_str(), port_str, &hints, &localinfo)) != 0) {
  58. logError("getaddrinfo: %s\n", gai_strerror(rv));
  59. return -1;
  60. }
  61. }
  62. // loop through all the results and connect to the first we can
  63. for (p = servinfo; p != NULL; p = p->ai_next) {
  64. if ((_sock = socket(p->ai_family, p->ai_socktype,
  65. p->ai_protocol)) == -1) {
  66. logError("socket: %s\n", strerror(errno));
  67. continue;
  68. }
  69. if (use_bind) {
  70. if (::bind(_sock, localinfo->ai_addr, localinfo->ai_addrlen) == -1) {
  71. close();
  72. logError("bind: %s\n", strerror(errno));
  73. return -1;
  74. }
  75. }
  76. if (::connect(_sock, p->ai_addr, p->ai_addrlen) == -1) {
  77. close();
  78. logError("connect: %s\n", strerror(errno));
  79. continue;
  80. }
  81. break;
  82. }
  83. if (p == NULL) {
  84. logError("failed to connect\n");
  85. return -1;
  86. }
  87. void *addr = &(((struct sockaddr_in*)p->ai_addr)->sin_addr);
  88. inet_ntop(p->ai_family, addr, s, sizeof s);
  89. logDebug("connected to %s\n", s);
  90. freeaddrinfo(servinfo); // all done with this structure
  91. if (use_bind) {
  92. freeaddrinfo(localinfo); // all done with this structure
  93. }
  94. return 1;
  95. }
  96. int EthernetClient::connect(IPAddress ip, uint16_t port)
  97. {
  98. return connect(ip.toString().c_str(), port);
  99. }
  100. size_t EthernetClient::write(uint8_t b)
  101. {
  102. return write(&b, 1);
  103. }
  104. size_t EthernetClient::write(const uint8_t *buf, size_t size)
  105. {
  106. int bytes = 0;
  107. if (_sock == -1) {
  108. return 0;
  109. }
  110. while (size > 0) {
  111. int rc = send(_sock, buf + bytes, size, MSG_NOSIGNAL | MSG_DONTWAIT);
  112. if (rc == -1) {
  113. logError("send: %s\n", strerror(errno));
  114. close();
  115. break;
  116. }
  117. bytes += rc;
  118. size -= rc;
  119. }
  120. return bytes;
  121. }
  122. size_t EthernetClient::write(const char *str)
  123. {
  124. if (str == NULL) {
  125. return 0;
  126. }
  127. return write((const uint8_t *)str, strlen(str));
  128. }
  129. size_t EthernetClient::write(const char *buffer, size_t size)
  130. {
  131. return write((const uint8_t *)buffer, size);
  132. }
  133. int EthernetClient::available()
  134. {
  135. int count = 0;
  136. if (_sock != -1) {
  137. ioctl(_sock, SIOCINQ, &count);
  138. }
  139. return count;
  140. }
  141. int EthernetClient::read()
  142. {
  143. uint8_t b;
  144. if ( recv(_sock, &b, 1, MSG_DONTWAIT) > 0 ) {
  145. // recv worked
  146. return b;
  147. } else {
  148. // No data available
  149. return -1;
  150. }
  151. }
  152. int EthernetClient::read(uint8_t *buf, size_t bytes)
  153. {
  154. return recv(_sock, buf, bytes, MSG_DONTWAIT);
  155. }
  156. int EthernetClient::peek()
  157. {
  158. uint8_t b;
  159. if (recv(_sock, &b, 1, MSG_PEEK | MSG_DONTWAIT) > 0) {
  160. return b;
  161. } else {
  162. return -1;
  163. }
  164. }
  165. void EthernetClient::flush()
  166. {
  167. int count = 0;
  168. if (_sock != -1) {
  169. while (true) {
  170. ioctl(_sock, SIOCOUTQ, &count);
  171. if (count == 0) {
  172. return;
  173. }
  174. usleep(1000);
  175. }
  176. }
  177. }
  178. void EthernetClient::stop()
  179. {
  180. if (_sock == -1) {
  181. return;
  182. }
  183. // attempt to close the connection gracefully (send a FIN to other side)
  184. shutdown(_sock, SHUT_RDWR);
  185. timeval startTime, curTime;
  186. gettimeofday(&startTime, NULL);
  187. // wait up to a second for the connection to close
  188. do {
  189. uint8_t s = status();
  190. if (s == ETHERNETCLIENT_W5100_CLOSED) {
  191. break; // exit the loop
  192. }
  193. usleep(1000);
  194. gettimeofday(&curTime, NULL);
  195. } while (((curTime.tv_sec - startTime.tv_sec) * 1000000) + (curTime.tv_usec - startTime.tv_usec) <
  196. 1000000);
  197. // free up the socket descriptor
  198. ::close(_sock);
  199. _sock = -1;
  200. }
  201. uint8_t EthernetClient::status()
  202. {
  203. if (_sock == -1) {
  204. return ETHERNETCLIENT_W5100_CLOSED;
  205. }
  206. struct tcp_info tcp_info;
  207. int tcp_info_length = sizeof(tcp_info);
  208. if ( getsockopt( _sock, SOL_TCP, TCP_INFO, (void *)&tcp_info,
  209. (socklen_t *)&tcp_info_length ) == 0 ) {
  210. switch (tcp_info.tcpi_state) {
  211. case TCP_ESTABLISHED:
  212. return ETHERNETCLIENT_W5100_ESTABLISHED;
  213. case TCP_SYN_SENT:
  214. return ETHERNETCLIENT_W5100_SYNSENT;
  215. case TCP_SYN_RECV:
  216. return ETHERNETCLIENT_W5100_SYNRECV;
  217. case TCP_FIN_WAIT1:
  218. case TCP_FIN_WAIT2:
  219. return ETHERNETCLIENT_W5100_FIN_WAIT;
  220. case TCP_TIME_WAIT:
  221. return TCP_TIME_WAIT;
  222. case TCP_CLOSE:
  223. return ETHERNETCLIENT_W5100_CLOSED;
  224. case TCP_CLOSE_WAIT:
  225. return ETHERNETCLIENT_W5100_CLOSING;
  226. case TCP_LAST_ACK:
  227. return ETHERNETCLIENT_W5100_LAST_ACK;
  228. case TCP_LISTEN:
  229. return ETHERNETCLIENT_W5100_LISTEN;
  230. case TCP_CLOSING:
  231. return ETHERNETCLIENT_W5100_CLOSING;
  232. }
  233. }
  234. return ETHERNETCLIENT_W5100_CLOSED;
  235. }
  236. uint8_t EthernetClient::connected()
  237. {
  238. return status() == ETHERNETCLIENT_W5100_ESTABLISHED || available();
  239. }
  240. void EthernetClient::close()
  241. {
  242. if (_sock != -1) {
  243. ::close(_sock);
  244. _sock = -1;
  245. }
  246. }
  247. void EthernetClient::bind(IPAddress ip)
  248. {
  249. _srcip = ip;
  250. }
  251. int EthernetClient::getSocketNumber()
  252. {
  253. return _sock;
  254. }
  255. // the next function allows us to use the client returned by
  256. // EthernetServer::available() as the condition in an if-statement.
  257. EthernetClient::operator bool()
  258. {
  259. return _sock != -1;
  260. }
  261. bool EthernetClient::operator==(const EthernetClient& rhs)
  262. {
  263. return _sock == rhs._sock && _sock != -1 && rhs._sock != -1;
  264. }