MySensors Library & Examples  2.3.2-62-ge298769
TCPHelper_POSIX.h
1 #pragma once
2 
3 #include <stdio.h>
4 #include <errno.h>
5 #include <sys/types.h>
6 #include <fcntl.h>
7 
8 #ifdef _WIN32
9 #include <winsock2.h>
10 #include <WS2tcpip.h>
11 #include <stdlib.h>
12 
13 #define close(fd) closesocket(fd)
14 #define ssize_t int
15 #else
16 #include <sys/time.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <netdb.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <netinet/tcp.h>
23 #include <arpa/inet.h>
24 #include <poll.h>
25 #endif
26 
27 #ifndef constrain
28 #define constrain(x, l, u) (x<l?l:(x>u?u:x))
29 #endif
30 
32 {
33 #ifdef _WIN32
34  SOCKET _fd = -1;
35 #else
36  int _fd = -1;
37 #endif
38  sockaddr_in _remote_addr;
39 
40 public:
41  TCPHelperClient() {}
42  TCPHelperClient(int fd)
43  {
44  _fd = fd;
45  }
46  TCPHelperClient(const TCPHelperClient &another)
47  {
48  *this = another;
49  }
50 
51  /*
52  ~TCPHelperClient() {
53  #ifdef _WIN32
54  if (_fd != INVALID_SOCKET) closesocket(_fd);
55  WSACleanup();
56  #else
57  if (_fd != INVALID_SOCKET) close(_fd);
58  #endif
59  }
60  */
61  int available()
62  {
63 #ifdef _WIN32
64  WSAPOLLFD pfs;
65  pfs.fd = _fd;
66  pfs.events = POLLIN;
67  pfs.revents = 0;
68  int rv = WSAPoll(&pfs, 1, 0);
69  if ((rv == -1 && WSAGetLastError() == WSAENETDOWN) || (rv > 0 && (pfs.revents & POLLNVAL) != 0)) {
70 #ifdef ETCP_ERROR_PRINT
71  printf("poll triggered stop, rv=%d: %s\n", rv, strerror(errno));
72 #endif
73  stop(); // Socket problem, close it
74  } else if (rv > 0) {
75  return (pfs.revents & POLLRDNORM) != 0 || (pfs.revents & POLLHUP) != 0;
76  }
77 #else
78  struct pollfd pfs;
79  pfs.fd = _fd;
80  pfs.events = POLLIN;
81  pfs.revents = 0;
82  int rv = ::poll(&pfs, 1, 0);
83  if (rv == -1 || (rv > 0 && (pfs.revents & POLLNVAL) != 0)) {
84 #ifdef ETCP_ERROR_PRINT
85  printf("poll triggered stop, rv=%d: %s\n", rv, strerror(errno));
86 #endif
87  stop(); // Socket problem, close it
88  } else if (rv > 0) {
89  return (pfs.revents & POLLIN) != 0 || (pfs.revents & POLLHUP) != 0;
90  }
91 #endif
92  return 0;
93  }
94 
95  bool connect(const uint8_t *address, uint16_t port)
96  {
97  if (_fd != -1) {
98 #ifdef ETCP_ERROR_PRINT
99  printf("connect triggered stop, _fd=%d\n", _fd);
100 #endif
101  stop();
102  }
103  _fd = ::socket(AF_INET,SOCK_STREAM,0);
104  if (_fd == -1) {
105  return false;
106  }
107  memset(&_remote_addr, 0, sizeof(_remote_addr));
108  _remote_addr.sin_family = AF_INET;
109  _remote_addr.sin_port = htons(port);
110  memcpy(&_remote_addr.sin_addr.s_addr, address, 4);
111 
112  // Shorten timeout for connecting
113  struct timeval read_timeout;
114  read_timeout.tv_sec = 0;
115  read_timeout.tv_usec = 2000000;
116 #ifndef __ZEPHYR__
117  setsockopt(_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&read_timeout, sizeof read_timeout);
118  setsockopt(_fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&read_timeout, sizeof read_timeout);
119 #endif
120 
121  bool connected = ::connect(_fd, (struct sockaddr *) &_remote_addr,sizeof(_remote_addr)) == 0;
122  if (connected) {
123 #ifndef __ZEPHYR__
124  // Shorten timeouts for reading and writing
125  struct timeval read_timeout;
126  read_timeout.tv_sec = 0;
127  read_timeout.tv_usec = 1000;
128  setsockopt(_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&read_timeout, sizeof read_timeout);
129  read_timeout.tv_usec = 2000000;
130  setsockopt(_fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&read_timeout, sizeof read_timeout);
131 #endif
132  // Disable Nagles algorith because we are sending small packets and waiting for reply
133  set_nodelay(true);
134  }
135  return connected;
136  }
137 
138  // Prepare a socket for non-blocking connect
139  bool prepare_connect(const uint8_t *address, uint16_t port)
140  {
141  if (_fd != -1) {
142 #ifdef ETCP_ERROR_PRINT
143  printf("prepare_connect triggered stop, _fd=%d\n", _fd);
144  stop();
145 #endif
146  }
147  _fd = ::socket(AF_INET,SOCK_STREAM,0);
148  if (_fd == -1) {
149  return false;
150  }
151  memset(&_remote_addr, 0, sizeof(_remote_addr));
152  _remote_addr.sin_family = AF_INET;
153  _remote_addr.sin_port = htons(port);
154  memcpy(&_remote_addr.sin_addr.s_addr, address, 4);
155 #ifdef _WIN32
156  unsigned long ul = 1;
157  int flags = ioctlsocket(_fd, FIONBIO, (unsigned long *)&ul); //Set into non blocking mode.
158  if (flags != SOCKET_ERROR) ; // Failed to set?
159 #else
160  int flags = fcntl(_fd, F_GETFL, 0);
161  if (flags!=-1) {
162  fcntl(_fd, F_SETFL, flags | O_NONBLOCK);
163  }
164 #endif
165  else {
166 #ifdef ETCP_ERROR_PRINT
167  printf("prepare_connect triggered stop, _fd=%d: %s\n", _fd, strerror(errno));
168 #endif
169  }
170  return flags != -1;
171  }
172 
173  // Try a non-blocking connect, it may succeed at once or after some calls
174  // Returns -1 if failure, 0 if waiting to connect, 1 when connected
175  int8_t try_connect()
176  {
177  int8_t connected = 0;
178  int status = ::connect(_fd, (struct sockaddr *) &_remote_addr,sizeof(_remote_addr));
179  if (status == -1) {
180 #ifdef _WIN32
181  int errno2 = WSAGetLastError();
182  if (errno2 == WSAEISCONN); // Already connected, proceed with setting timeouts
183  else if (errno2 == WSAEWOULDBLOCK || errno2 == WSAEINPROGRESS || errno2 == WSAEALREADY) {
184  return 0; // Normal, we are waiting for connect to succeed
185  }
186 #else
187  if (errno == EISCONN); // Already connected, proceed with setting timeouts
188  else if (errno == EINPROGRESS || errno == EALREADY) {
189  return 0; // Normal, we are waiting for connect to succeed
190  }
191 #endif
192  else {
193 #ifdef ETCP_ERROR_PRINT
194 #ifdef _WIN32
195  printf("try_connect triggered stop, _fd=%d: %d\n", _fd, errno2);
196 #else
197  printf("try_connect triggered stop, _fd=%d: %d %s\n", _fd, errno, strerror(errno));
198 #endif
199 #endif
200  stop();
201  return -1; // Some other error
202  }
203  }
204 
205  // Enable blocking operations
206 #ifdef _WIN32
207  unsigned long ul = 0;
208  int flags = ioctlsocket(_fd, FIONBIO, (unsigned long *)&ul); // Set into non blocking mode.
209  //if (flags != SOCKET_ERROR) ; // Failed to set.
210 #else
211  int flags = fcntl(_fd, F_GETFL, 0);
212  if (flags != -1) {
213  fcntl(_fd, F_SETFL, flags & ~O_NONBLOCK);
214  }
215 #endif
216 
217  // Set timeouts for reading and writing
218 #ifndef __ZEPHYR__
219  struct timeval read_timeout;
220  read_timeout.tv_sec = 0;
221  read_timeout.tv_usec = 1000;
222  setsockopt(_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&read_timeout, sizeof read_timeout);
223  read_timeout.tv_usec = 2000000;
224  setsockopt(_fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&read_timeout, sizeof read_timeout);
225 #endif
226  // Disable Nagles algorith because we are sending small packets and waiting for reply
227  set_nodelay(true);
228 
229  return 1;
230  }
231 
232  // Enable or disable Nagles algorithm to send unfilled packets immediately or not
233  void set_nodelay(bool nodelay)
234  {
235  int flag = nodelay;
236  if (_fd != -1) {
237  setsockopt(_fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof flag);
238  }
239  }
240 
241  bool connected()
242  {
243  return _fd != -1;
244  }
245 
246  int read(uint8_t *buffer, int buffer_size)
247  {
248  if (_fd == -1) {
249  return -1;
250  }
251  int r = ::recv(_fd, (char*)buffer, buffer_size, 0); //MSG_DONTWAIT);
252  if (r == -1) {
253 #ifdef _WIN32
254  int errno2 = WSAGetLastError();
255  if (errno2 == WSAEWOULDBLOCK) {
256  return 0; // Waiting for more
257  }
258 #ifdef ETCP_ERROR_PRINT
259  printf("read triggered stop, r=%d: %d\n", r, errno2);
260 #endif
261 #else
262  if (errno == EAGAIN) {
263  return 0; // Waiting for more
264  }
265 #ifdef ETCP_ERROR_PRINT
266  printf("read triggered stop, r=%d: %s\n", r, strerror(errno));
267 #endif
268 #endif
269  stop();
270  }
271  return r;
272  }
273 
274  int write(const uint8_t *buffer, int size)
275  {
276  if (_fd == -1) {
277  return -1;
278  }
279 #if defined(_WIN32) || defined(__ZEPHYR__)
280  int w = ::send(_fd, (char*)buffer, size, 0);
281 #else
282  int w = ::send(_fd, (char*)buffer, size, MSG_NOSIGNAL);
283 #endif
284  if (w == -1) {
285 #ifdef ETCP_ERROR_PRINT
286 #ifdef _WIN32
287  int errno2 = WSAGetLastError();
288  printf("write triggered stop, w=%d: %d\n", w, errno2);
289 #else
290  if (errno != EPIPE) {
291  printf("write triggered stop, w=%d: %s\n", w, strerror(errno));
292  }
293 #endif
294 #endif
295  stop();
296  }
297  return w;
298  }
299 
300  void flush() { }
301 
302  void stop()
303  {
304  if (_fd != -1) {
305 #ifndef _WIN32
306  ::shutdown(_fd, SHUT_RDWR);
307 #endif
308  ::close(_fd);
309  _fd = -1;
310  }
311  }
312 
313  void operator=(const TCPHelperClient &another)
314  {
315  _fd = another._fd;
316  }
317 
318  operator bool()
319  {
320  return connected();
321  }
322  bool operator==(const bool value)
323  {
324  return bool() == value;
325  }
326  bool operator!=(const bool value)
327  {
328  return bool() != value;
329  }
330  bool operator==(const TCPHelperClient& rhs)
331  {
332  return _fd == rhs._fd && _fd != -1 && rhs._fd != -1;
333  }
334  bool operator!=(const TCPHelperClient& rhs)
335  {
336  return !this->operator==(rhs);
337  }
338  uint8_t getSocketNumber()
339  {
340  return _fd;
341  }
342 
343  int print(const char *msg)
344  {
345  return write((const uint8_t*) msg, strlen(msg));
346  }
347 };
348 
349 
351 {
352  uint16_t _port = 0;
353  int _fd = -1;
354  sockaddr_in _localaddr, _remote_sender_addr;
355 
356 public:
357  TCPHelperServer(uint16_t listening_port)
358  {
359  _port = listening_port;
360  }
361  ~TCPHelperServer()
362  {
363 #ifdef ETCP_DEBUG_PRINT
364  printf("destructor triggered stop, _fd=%d\n", _fd);
365 #endif
366  stop();
367  }
368 
369  TCPHelperClient available()
370  {
371  socklen_t len = sizeof(_remote_sender_addr);
372  memset(&_remote_sender_addr, 0, len);
373  int connected_fd = _fd == -1 ? -1 : ::accept(_fd, (struct sockaddr *) &_remote_sender_addr, &len);
374  if (connected_fd != -1) {
375 #ifndef __ZEPHYR__
376  // Shorten timeout for reading and writing
377  struct timeval read_timeout;
378  read_timeout.tv_sec = 0;
379  read_timeout.tv_usec = 1000;
380  setsockopt(connected_fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&read_timeout, sizeof read_timeout);
381  read_timeout.tv_usec = 2000000;
382  setsockopt(connected_fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&read_timeout, sizeof read_timeout);
383 #endif
384  // Disable Nagles algorith because we are sending small packets and waiting for reply
385  int flag = 1;
386  setsockopt(connected_fd, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof flag);
387  }
388  return TCPHelperClient(connected_fd);
389  }
390 
391  bool begin()
392  {
393  // Close if open after previous init attempt
394  if (_fd != -1) {
395  close(_fd);
396  _fd = -1;
397  }
398 
399  // Prepare socket
400  _fd=socket(AF_INET,SOCK_STREAM,0);
401  if (_fd==-1) {
402 #ifdef ETCP_ERROR_PRINT
403  printf("failure creating socket: %s\n", strerror(errno));
404 #endif
405  return false;
406  }
407 
408  int yes = 1;
409  setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
410 
411  // Bind to specific local port
412  memset(&_localaddr, 0, sizeof(_localaddr));
413  _localaddr.sin_family = AF_INET;
414  _localaddr.sin_port = htons(_port);
415  _localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
416  if (bind(_fd,(struct sockaddr *) &_localaddr,sizeof(_localaddr))==-1) {
417 #ifdef ETCP_ERROR_PRINT
418  printf("bind failed: %s\n", strerror(errno));
419 #endif
420  return false;
421  }
422 
423  // Start listening
424  if (listen(_fd, 5) == -1) {
425 #ifdef ETCP_ERROR_PRINT
426  printf("listen failed: %s\n", strerror(errno));
427 #endif
428  return false;
429  }
430 
431  // Set the socket to nonblocking so that accept will not block
432 #ifdef _WIN32
433  unsigned long ul = 1;
434  int flags = ioctlsocket(_fd, FIONBIO, (unsigned long *)&ul); //Set into non blocking mode.
435  if (flags != NO_ERROR) { // Failed to set.
436 #ifdef ETCP_ERROR_PRINT
437  printf("ioctlsocket FIONBIO failed.");
438 #endif
439  }
440 #else
441  int flags = fcntl(_fd, F_GETFL, 0);
442  if (flags != -1) {
443  fcntl(_fd, F_SETFL, flags | O_NONBLOCK);
444  }
445 #endif
446  memset(&_remote_sender_addr, 0, sizeof(_remote_sender_addr));
447  return true;
448  }
449 
450  void stop()
451  {
452  if (_fd != -1) {
453  ::close(_fd);
454  _fd = -1;
455  }
456  }
457 };
458 
459 #undef close
TCPHelperClient
Definition: TCPHelper_POSIX.h:31
send
bool send(MyMessage &msg, const bool requestEcho=false)
TCPHelperServer
Definition: TCPHelper_POSIX.h:350