rcolyer.net
RC Lib  Version 202403231100
Net.h
Go to the documentation of this file.
1 /////////////////////////////////////////////////////////////////////
2 //
3 // RC Library, (c) 2011-2015, Ryan A. Colyer
4 // Distributed under the Boost Software License, v1.0. (LICENSE.txt)
5 //
6 /// \file Net.h
7 /// Provides basic cross-platform socket networking, with support for
8 /// both blocking and non-blocking sending and receiving.
9 /////////////////////////////////////////////////////////////////////
10 
11 #ifndef RC_NET_H
12 #define RC_NET_H
13 
14 #include "Types.h"
15 #include "Errors.h"
16 #include "File.h"
17 #include "RStr.h"
18 #include "Data1D.h"
19 #include "APtr.h"
20 #include <string.h>
21 #ifdef WIN32
22 #ifndef _WIN32_WINNT
23 /// @cond UNDOC
24 #define _WIN32_WINNT 0x0501 // XP and later only
25 /// @endcond
26 #endif
27 #include <winsock2.h>
28 #include <ws2tcpip.h>
29 #else
30 #include <sys/socket.h>
31 #include <netdb.h>
32 #endif
33 #include <sys/types.h>
34 
35 /// \def RC_SockType
36 /// The standard socket type used in socket calls for a given system.
37 /// \def RC_InvSock
38 /// The value returned for an invalid socket.
39 /// \def RC_SockErr
40 /// The value returned for a socket error.
41 /// \def RC_CloseSock
42 /// The function for manually closing sockets.
43 /// \def RC_WouldBlock
44 /// The return value for a socket call which would block.
45 /// \def RC_SockErrorAt
46 /// The variable or function call for obtaining the last error.
47 /// \def RC_SockReturn
48 /// The return type for send and recv calls.
49 #ifdef WIN32
50 #define RC_SockType SOCKET
51 #define RC_InvSock INVALID_SOCKET
52 #define RC_SockErr SOCKET_ERROR
53 #define RC_CloseSock closesocket
54 #define RC_WouldBlock WSAEWOULDBLOCK
55 #define RC_SockErrorAt WSAGetLastError()
56 #define RC_SockReturn int
57 #else
58 #define RC_SockType int
59 #define RC_InvSock -1
60 #define RC_SockErr -1
61 #define RC_CloseSock close
62 #define RC_WouldBlock EWOULDBLOCK
63 #define RC_SockErrorAt errno
64 #define RC_SockReturn ssize_t
65 #endif
66 
67 
68 namespace RC {
69  /// A portable socket interface for reading and writing to an open socket.
70  /** This class shares its data in a reference-counted manner upon
71  * assignment or copy construction, and automatically closes the socket
72  * as the last instance goes out of scope.
73  */
74  class Sock {
75  protected:
76  /// @cond PROTECTED
77 
78  class SockHelper {
79  public:
80 
81  inline SockHelper(RC_SockType new_sock=RC_InvSock)
82  : socket(new_sock),
83  buf (4096),
84  index(0),
85  full_to(0) {
86  }
87 
88  inline ~SockHelper() {
89  Close();
90  }
91 
92  inline void Close() {
93  RC_SockType loc_soc;
94 #ifdef CPP11
95  loc_soc = socket.exchange(RC_InvSock);
96 #else
97  loc_soc = socket;
98  socket = RC_InvSock;
99 #endif
100  if (loc_soc != RC_InvSock) {
101  RC_CloseSock(loc_soc);
102  }
103  }
104 
105 
106  inline bool DataReady(bool do_block = true) const {
107  fd_set readset;
108  FD_ZERO(&readset);
109  FD_SET(socket, &readset);
110  struct timeval *timeptr = NULL;
111  struct timeval timeout;
112  if (!do_block) {
113  timeout.tv_sec = 0;
114  timeout.tv_usec = 0;
115  timeptr = &timeout;
116  }
117  int result = select(socket+1, &readset, NULL, NULL, timeptr);
118  return (result > 0);
119  }
120 
121 
122  inline bool CanSend(bool do_block = true) const {
123  fd_set writeset;
124  FD_ZERO(&writeset);
125  FD_SET(socket, &writeset);
126  struct timeval *timeptr = NULL;
127  struct timeval timeout;
128  if (!do_block) {
129  timeout.tv_sec = 0;
130  timeout.tv_usec = 0;
131  timeptr = &timeout;
132  }
133  int result = select(socket+1, NULL, &writeset, NULL, timeptr);
134  return (result > 0);
135  }
136 
137 
138  inline void ReadMore(size_t amnt_wanted = -1, bool do_block = true) {
139  size_t amnt_left = full_to - index;
140  if (amnt_wanted < amnt_left) {
141  return;
142  }
143 
144  bool got_data = false;
145  do {
146  if (do_block) {
147  DataReady();
148  }
149 
150  RC_SockReturn bytes_recv;
151 
152  if (amnt_wanted == size_t(-1)) {
153  amnt_wanted = buf.size();
154  }
155 
156  if (amnt_wanted > buf.size()) {
157  buf.Resize(amnt_wanted);
158  }
159 
160  if (index + amnt_wanted > buf.size()) {
161  buf.CopyData(0, index, amnt_left);
162  index = 0;
163  full_to = amnt_left;
164  amnt_wanted = amnt_wanted - amnt_left;
165  }
166 
167  bytes_recv = recv(socket, reinterpret_cast<char*>(buf.Raw()) +
168  full_to, amnt_wanted, 0);
169 
170  if (bytes_recv == RC_SockErr) {
171  if (RC_SockErrorAt == RC_WouldBlock) {
172  continue;
173  }
174  Throw_RC_Type(Net, "Socket receive error");
175  }
176 
177  got_data = true;
178  full_to += bytes_recv;
179 
180  } while (do_block && !got_data);
181  }
182 
183 
184  size_t SendData(const char* arr, size_t len, bool do_block = true) {
185  const char *arr_current = arr;
186  size_t len_left = len;
187 
188  do {
189  if (do_block) {
190  CanSend();
191  }
192 
193  RC_SockReturn bytes_sent;
194  bytes_sent = send(socket, arr, len, 0);
195 
196  if (bytes_sent == RC_SockErr) {
197  if (RC_SockErrorAt == RC_WouldBlock) {
198  continue;
199  }
200  Throw_RC_Type(Net, "Socket send error");
201  }
202 
203  arr_current += bytes_sent;
204  len_left -= bytes_sent;
205  } while (do_block && len_left > 0);
206 
207  return len;
208  }
209 
210 #ifdef CPP11
211  std::atomic<RC_SockType> socket;
212 #else
213  RC_SockType socket;
214 #endif
215 
216  Data1D<u8> buf;
217  size_t index;
218  size_t full_to;
219  RStr remote_addr;
220  RStr remote_port;
221  };
222 
223  APtr<SockHelper> helper;
224 
225  /// @endcond
226 
227  public:
228 
229  /// Encapsulate a new socket, receiving the base socket type of the
230  /// system (int or SOCKET).
231  /** Use Connect or Listener::Accept to initialize this.
232  */
233  inline Sock(RC_SockType new_sock=RC_InvSock) {
234  helper = new SockHelper(new_sock);
235 
236  if (new_sock != RC_InvSock) {
237  bool is_non_blocking = false;
238  // Set non-blocking.
239 #ifdef WIN32
240  unsigned long mode = 1;
241  if (ioctlsocket(new_sock, FIONBIO, &mode) == NO_ERROR) {
242  is_non_blocking = true;
243  }
244 #else
245  int flags = fcntl(new_sock, F_GETFL, 0);
246  if (flags != -1) {
247  if (fcntl(new_sock, F_SETFL, flags | O_NONBLOCK) != -1) {
248  is_non_blocking = true;
249  }
250  }
251 #endif
252  if (!is_non_blocking) {
253  Throw_RC_Type(Net, "Could not set socket non-blocking!");
254  }
255  }
256 
257  }
258 
259  /// Manually close the socket.
260  /** Note this is handled automatically when all copies of the Sock go
261  * out of scope.)
262  */
263  inline void Close() {
264  helper->Close();
265  }
266 
267  /// For record-keeping, set the remote address and port.
268  inline void SetRemote(RStr remote_addr, RStr remote_port) {
269  helper->remote_addr = remote_addr;
270  helper->remote_port = remote_port;
271  }
272 
273  /// Get the remote address this socket is connected to.
274  inline RStr GetRemoteAddr() const {
275  return helper->remote_addr;
276  }
277 
278  /// Get the remote port this socket is connected to.
279  inline RStr GetRemotePort() const {
280  return helper->remote_port;
281  }
282 
283  /// Returns true if data is ready for reading.
284  /** If block_until_ready is true this will wait until data is available
285  * or an error occurs, such as the socket closing or a signal being
286  * received. (Note that rare kernel level events can cause data to no
287  * longer be available after it was reported as available.)
288  */
289  inline bool DataReady(bool block_until_ready = false) const {
290  return helper->DataReady(block_until_ready);
291  }
292 
293  /// Returns true if the socket is ready to send data.
294  /** If block_until_ready is true this will wait until data can be sent
295  * or an error occurs, such as the socket closing or a signal being
296  * received.
297  */
298  inline bool CanSend(bool block_until_ready = false) const {
299  return helper->CanSend(block_until_ready);
300  }
301 
302 #ifdef unix
303  /// On unix-based systems, return a FileRW coresponding to this socket.
304  /** Throws ErrorMsgNet if it cannot convert the socket.
305  */
307  FileRW frw;
308 
309  FILE* sfp = fdopen(helper->socket, "a+");
310  if (sfp == NULL) {
311  Throw_RC_Type(Net, "fdopen failed");
312  }
313 
314  frw = FileRW(sfp);
315  frw.SetFilename(helper->remote_addr + " " + helper->remote_port);
316  return frw;
317  }
318 #endif
319 
320  /// Return the raw encapsulated socket.
321  inline RC_SockType Raw() const {
322  return helper->socket;
323  }
324 
325 
326  /// Receive data from the socket into buf, up to the size of buf.
327  /** @param buf The buffer into which data is received.
328  * @param do_block If true, wait until some data is available.
329  * @return The number of elements of buf received.
330  */
331  template<class T>
332  inline size_t Recv(Data1D<T> buf, bool do_block = true) {
333 
334  size_t amnt_to_read = buf.size()*buf.TypeSize();
335 
336  helper->ReadMore(amnt_to_read, do_block);
337 
338  size_t elem_in_helper = (helper->full_to - helper->index)
339  / buf.TypeSize();
340  size_t elem_to_copy =
341  buf.size() < elem_in_helper ? buf.size() : elem_in_helper;
342 
343  size_t bytes_to_copy = elem_to_copy * buf.TypeSize();
344 
345  buf.CopyFrom(helper->buf, helper->index, bytes_to_copy);
346  helper->index += bytes_to_copy;
347 
348  return elem_to_copy;
349  }
350 
351  /// Receive characters into str until a newline or the end of data.
352  /** @param str The string into which a line is received.
353  * @param crop_newline If true, removes "\r\n" and "\n" from the end.
354  * @param do_block If true, keeps reading until newline or the socket
355  * closes. If false, reads until newline or no data is available.
356  * @return The number of bytes received, including null characters and
357  * newlines not added to str.
358  */
359  inline size_t Recv(RStr& str, bool crop_newline = true,
360  bool do_block = true) {
361  Data1D<char> arr;
362  arr.Reserve(4096);
363 
364  size_t count = 0;
365 
366  while (1) {
367  if (helper->full_to == helper->index) {
368  helper->ReadMore(-1, do_block);
369  if (helper->full_to == 0) {
370  break;
371  }
372  }
373 
374  char c = helper->buf[helper->index];
375  ++(helper->index);
376  count++;
377 
378  if (c == '\0') {
379  break;
380  }
381  if (c == '\n') {
382  if (!crop_newline) {
383  arr += c;
384  }
385  break;
386  }
387 
388  arr += c;
389  }
390 
391  if (crop_newline) {
392  while (arr.size() > 0 && arr[arr.size()-1] == '\r') {
393  arr.Resize(arr.size()-1);
394  }
395  }
396 
397  arr += '\0';
398  str = arr.Raw();
399 
400  return count;
401  }
402 
403 
404  /// Sends the contents of buf through the socket.
405  /** @param buf The buffer containing the data to send.
406  * @param do_block If true, sends all of buf. If false, sends as much of
407  * buf as can be sent.
408  * @return The amount of data sent.
409  */
410  template<class T>
411  inline size_t Send(Data1D<T> buf, bool do_block = true) {
412  return helper->SendData(buf.Raw(), buf.Size()*buf.TypeSize(), do_block);
413  }
414 
415 
416  /// Sends the contents of str through the socket.
417  /** @param str The string containing the data to send.
418  * @param do_block If true, sends all of str. If false, sends as much of
419  * str as can be sent.
420  * @return The amount of data sent.
421  */
422  inline size_t Send(RStr str, bool do_block = true) {
423  return helper->SendData(str.c_str(), str.size(), do_block);
424  }
425  };
426 
427 
428  /// Provides both client and server sides of blocking TCP connections.
429  class Net {
430  public:
431 
432  /// Automatically called by Connect and Listener.
433  /** The first time this is called, it sets SIGPIPE to ignore so the
434  * program doesn't terminate while writing to a closed socket. Call
435  * this once manually beforehand if you want to set your own custom
436  * handler for SIGPIPE.
437  */
438  static inline void InitializeSockets() {
439  static RC_THREAD_LOCAL bool already_run = false;
440  if (! already_run) {
441 #ifdef WIN32
442  WSADATA wsa_data;
443  int err;
444  err = WSAStartup(0x0202, &wsa_data);
445  if (err != 0) {
446  Throw_RC_Type(Net, "WSAStartup error");
447  }
448 #else
449  struct sigaction sa;
450  memset (&sa, 0, sizeof(sa));
451  sa.sa_handler = SIG_IGN;
452  sigaction(SIGPIPE, &sa, NULL);
453 #endif
454 
455  already_run = true;
456  }
457  }
458 
459 
460  /// Opens a TCP connection to host:port.
461  /** Will throw ErrorMsgNet upon failure to connect if quiet_fail is
462  * false.
463  * @return True if the connect succeeded.
464  */
465  static inline bool Connect(Sock &connection, const RStr& host,
466  const RStr& port, bool quiet_fail=true) {
467  FileRW retval;
468  struct addrinfo hints;
469  struct addrinfo *ai_list, *addr;
470  RC_SockType sfd = RC_InvSock;
471 
473  connection.Close();
474 
475  memset(&hints, 0, sizeof(hints));
476  hints.ai_family = AF_UNSPEC; // Support IPv4 && IPv6
477  hints.ai_socktype = SOCK_STREAM; // For TCP
478 
479  int aierr = getaddrinfo(host.c_str(), port.c_str(), &hints, &ai_list);
480  if (aierr != 0) {
481  if (! quiet_fail) {
482  Throw_RC_Type(Net, RStr(gai_strerror(aierr)).c_str());
483  }
484  return false;
485  }
486 
487  for (addr = ai_list; addr != NULL; addr = addr->ai_next) {
488  sfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
489 
490  if (sfd == RC_InvSock) {
491  continue;
492  }
493 
494  if (connect(sfd, addr->ai_addr, addr->ai_addrlen) != RC_SockErr) {
495  break; // Connected.
496  }
497 
498  RC_CloseSock(sfd);
499  }
500 
501  freeaddrinfo(ai_list);
502 
503  // If could not find one
504  if (addr == NULL) {
505  if (! quiet_fail) {
506  Throw_RC_Error("Connection failed");
507  }
508  return false;
509  }
510 
511  connection = Sock(sfd);
512  connection.SetRemote(host, port);
513 
514  return true;
515  }
516 
517 
518  /// Listens to the specified port for incoming TCP connections.
519  class Listener {
520  protected:
521  /// @cond PROTECTED
522  RC_SockType sockfd;
523 
524  /// @endcond
525  public:
526 
527  /// Begin listening to the specified port for incoming TCP connections.
528  /** Use Accept to accept a connection.
529  */
530  inline Listener(const RStr& port) {
531  sockfd = RC_InvSock;
532 
533  struct addrinfo hints;
534  struct addrinfo *ai_list, *addr;
535  RC_SockType sfd;
536 
538 
539  memset(&hints, 0, sizeof(hints));
540  hints.ai_family = AF_UNSPEC; // Support IPv4 && IPv6
541  hints.ai_socktype = SOCK_STREAM; // For TCP
542  hints.ai_flags = AI_PASSIVE; // Listen on all interfaces
543 
544  int aierr = getaddrinfo(NULL, port.c_str(), &hints, &ai_list);
545  if (aierr != 0) {
546  Throw_RC_Type(Net, RStr(gai_strerror(aierr)).c_str());
547  }
548 
549  for (addr = ai_list; addr != NULL; addr = addr->ai_next) {
550  sfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
551  if (sfd == RC_InvSock) {
552  continue;
553  }
554 
555  if (::bind(sfd, addr->ai_addr, addr->ai_addrlen) == 0) {
556  break; // Bound.
557  }
558 
559  close(sfd);
560  }
561 
562  // If could not find one
563  if (addr == NULL) {
564  Throw_RC_Type(Net, "Could not bind");
565  }
566 
567  freeaddrinfo(ai_list);
568 
569  if (listen(sfd, 128) == RC_SockErr) {
570  Throw_RC_Type(Net, "Port in use");
571  }
572 
573  sockfd = sfd;
574  }
575 
576 
577  /// If open, this closes the port being listened to.
579  if (sockfd != RC_InvSock) {
580  close(sockfd);
581  }
582  }
583 
584 
585  /// Provides the raw socket descriptor being listened to.
586  RC_SockType Raw() const {
587  return sockfd;
588  }
589 
590  /// Accept an incoming connection, with read/write access through the
591  /// FileRW connection.
592  /** This throws an ErrorMsgNet on error if quiet_fail is false.
593  * @return False if an error occurred.
594  */
595  inline bool Accept(Sock &connection, bool quiet_fail=true) {
596  RC_SockType acc_sock;
597  struct sockaddr_in6 addr;
598  socklen_t len = sizeof(addr);
599 
600  connection.Close();
601 
602  acc_sock = accept(sockfd, reinterpret_cast<sockaddr*>(&addr), &len);
603 
604  if (acc_sock == RC_InvSock) {
605  if (! quiet_fail) {
606  Throw_RC_Type(Net, "Could not accept connection");
607  }
608  return false;
609  }
610 
611  connection = Sock(acc_sock);
612 
613  char host[2048];
614  char port[2048];
615  if (getnameinfo(reinterpret_cast<sockaddr*>(&addr), sizeof(addr), host,
616  2048, port, 2048, NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
617  connection.SetRemote(host, port);
618  }
619 
620  return true;
621  }
622  };
623 
624  };
625 }
626 
627 
628 #endif // RC_NET_H
629 
A reference counting ptr that is auto-deleted as the last copy leaves scope.
Provides a one-dimensional vector-like structure.
Provides informative exception handling.
#define Throw_RC_Type(Type, err)
Use this to throw an RC:ErrorMsg subtype exception.
Definition: Errors.h:282
#define Throw_RC_Error(err)
Use this to throw an RC:ErrorMsg exception.
Definition: Errors.h:274
Provides file input and output, and file / directory tools.
#define RC_SockReturn
The return type for send and recv calls.
Definition: Net.h:56
#define RC_SockType
The standard socket type used in socket calls for a given system.
Definition: Net.h:50
#define RC_SockErr
The value returned for a socket error.
Definition: Net.h:52
#define RC_WouldBlock
The return value for a socket call which would block.
Definition: Net.h:54
#define RC_CloseSock
The function for manually closing sockets.
Definition: Net.h:53
#define RC_SockErrorAt
The variable or function call for obtaining the last error.
Definition: Net.h:55
#define RC_InvSock
The value returned for an invalid socket.
Definition: Net.h:51
Provides a robust value-added wrapper for std::string.
Provides typedefs and routines for working with primitives.
#define RC_THREAD_LOCAL
Provides thread_local if available.
Definition: Types.h:61
A reference counting ptr that auto-deletes what it points to when the last copy leaves scope or is ot...
Definition: APtr.h:39
A bounds-safe one-dimensional vector-like structure.
Definition: Data1D.h:49
size_t TypeSize() const
Returns the size of this Data1D's type.
Definition: Data1D.h:361
T * Raw() const
Access a raw unprotected C-pointer to the enclosed data.
Definition: Data1D.h:356
void Reserve(const size_t reserve_size)
Reserve storage without resizing the array.
Definition: Data1D.h:271
size_t size() const
Returns the current number of elements.
Definition: Data1D.h:359
void Resize(const size_t resize_size)
Resize the array, reallocating if necessary.
Definition: Data1D.h:302
Data1D & CopyFrom(const Data1D< T2 > &other)
Copy data from any type with a compatible assignment operator.
Definition: Data1D.h:389
void SetFilename(const RStr &newfilename) const
Manually changes the associated filename.
Definition: File.h:356
A file class for both reading and writing that provides buffered and unbuffered output to files.
Definition: File.h:1124
Listens to the specified port for incoming TCP connections.
Definition: Net.h:519
~Listener()
If open, this closes the port being listened to.
Definition: Net.h:578
SOCKET Raw() const
Provides the raw socket descriptor being listened to.
Definition: Net.h:586
Listener(const RStr &port)
Begin listening to the specified port for incoming TCP connections.
Definition: Net.h:530
bool Accept(Sock &connection, bool quiet_fail=true)
Accept an incoming connection, with read/write access through the FileRW connection.
Definition: Net.h:595
Provides both client and server sides of blocking TCP connections.
Definition: Net.h:429
static void InitializeSockets()
Automatically called by Connect and Listener.
Definition: Net.h:438
static bool Connect(Sock &connection, const RStr &host, const RStr &port, bool quiet_fail=true)
Opens a TCP connection to host:port.
Definition: Net.h:465
A bounds-safe string class which provides an identical interface to std::string plus many convenience...
Definition: RStr.h:110
size_t size() const
Returns the length of the string.
Definition: RStr.h:263
const char * c_str() const
Provides a null-terminated C style string corresponding to RStr.
Definition: RStr.h:622
A portable socket interface for reading and writing to an open socket.
Definition: Net.h:74
size_t Send(Data1D< T > buf, bool do_block=true)
Sends the contents of buf through the socket.
Definition: Net.h:411
SOCKET Raw() const
Return the raw encapsulated socket.
Definition: Net.h:321
size_t Recv(RStr &str, bool crop_newline=true, bool do_block=true)
Receive characters into str until a newline or the end of data.
Definition: Net.h:359
RStr GetRemoteAddr() const
Get the remote address this socket is connected to.
Definition: Net.h:274
void Close()
Manually close the socket.
Definition: Net.h:263
void SetRemote(RStr remote_addr, RStr remote_port)
For record-keeping, set the remote address and port.
Definition: Net.h:268
bool DataReady(bool block_until_ready=false) const
Returns true if data is ready for reading.
Definition: Net.h:289
bool CanSend(bool block_until_ready=false) const
Returns true if the socket is ready to send data.
Definition: Net.h:298
FileRW ToFileRW()
On unix-based systems, return a FileRW coresponding to this socket.
Definition: Net.h:306
Sock(SOCKET new_sock=INVALID_SOCKET)
Encapsulate a new socket, receiving the base socket type of the system (int or SOCKET).
Definition: Net.h:233
RStr GetRemotePort() const
Get the remote port this socket is connected to.
Definition: Net.h:279
size_t Recv(Data1D< T > buf, bool do_block=true)
Receive data from the socket into buf, up to the size of buf.
Definition: Net.h:332
size_t Send(RStr str, bool do_block=true)
Sends the contents of str through the socket.
Definition: Net.h:422
Definition: APtr.h:25
email address
— (c) 2015