lib45d
45Drives C++ Library API Documentation
SocketBase.hpp
1 // -*- C++ -*-
2 /*
3  * Copyright (C) 2021 Joshua Boudreau <jboudreau@45drives.com>
4  *
5  * This file is part of lib45d.
6  *
7  * lib45d is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * lib45d is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with lib45d. If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #pragma once
22 
23 #include <45d/socket/Exceptions.hpp>
24 #include <sstream>
25 #include <vector>
26 
27 extern "C" {
28 #include <string.h> // for strerror
29 #include <sys/socket.h>
30 #include <unistd.h> // for close
31 }
32 
33 namespace ffd {
38  namespace Socket {
39  const int _backlog_default = 50;
40 
45  const int _buff_sz =
46 #ifndef FFD_SOCKET_BUFF_SZ
47  1024;
48 #else
49  FFD_SOCKET_BUFF_SZ;
50 #endif
51  const char _rec_delim = 0x1E;
52  } // namespace Socket
57  class SocketBase {
58  public:
69  SocketBase(int domain, int type, int protocol = 0) : ACK('\6') {
70  int res = socket(domain, type, protocol);
71  if (res == -1) {
72  int error = errno;
73  throw SocketCreateException(strerror(error), error);
74  }
75  fd_ = res;
76  }
83  close(fd_);
84  }
90  void close_connection(int fd = 0) {
91  if (fd == 0) {
92  fd = io_fd_;
93  }
94  int res = close(fd);
95  if (res == -1) {
96  int error = errno;
97  throw SocketCloseException(strerror(error), error);
98  }
99  }
107  void send_data_async(const std::string &str, int flags = 0, int fd = 0) {
108  if (fd == 0)
109  fd = io_fd_;
110  int res = send(fd, (void *)str.c_str(), str.length(), flags);
111  if (res == -1) {
112  int error = errno;
113  throw SocketWriteException(strerror(error), error);
114  }
115  }
123  void send_data_sync(const std::string &str, int flags = 0, int fd = 0) {
124  if (fd == 0)
125  fd = io_fd_;
126  send_data_async(str, flags, fd);
127  get_ack(fd);
128  }
137  void send_data(const std::string &str, int flags = 0, int fd = 0) {
138  send_data_sync(str, flags, fd);
139  }
147  void send_data_async(const std::vector<std::string> &vec, int flags = 0, int fd = 0) {
148  std::string payload;
149  std::vector<std::string>::const_iterator itr = vec.begin();
150  payload = *itr;
151  ++itr;
152  for (; itr != vec.end(); ++itr) {
153  payload += Socket::_rec_delim + *itr;
154  }
155  send_data_async(payload, flags, fd);
156  }
164  void send_data_sync(const std::vector<std::string> &vec, int flags = 0, int fd = 0) {
165  send_data_async(vec, flags, fd);
166  get_ack(fd);
167  }
176  void send_data(const std::vector<std::string> &vec, int flags = 0, int fd = 0) {
177  send_data_sync(vec, fd, flags);
178  }
186  void receive_data_async(std::string &payload, int flags = 0, int fd = 0) {
187  payload = "";
188  if (fd == 0)
189  fd = io_fd_;
190  char buff[Socket::_buff_sz];
191  memset(&buff, 0, sizeof(buff));
192  int bytes_read = 0;
193  do {
194  bytes_read = recv(fd, &buff, sizeof(buff) - 1, flags);
195  if (bytes_read == -1) {
196  int error = errno;
197  throw SocketReadException(strerror(error), error);
198  }
199  buff[bytes_read] = '\0';
200  payload += buff;
201  } while (bytes_read > 0
202  && recv(fd, &buff, sizeof(buff) - 1, MSG_PEEK | MSG_DONTWAIT) > 0);
203  }
211  void receive_data_sync(std::string &payload, int flags = 0, int fd = 0) {
212  receive_data_async(payload, flags, fd);
213  send_ack(fd);
214  }
223  void receive_data(std::string &payload, int flags = 0, int fd = 0) {
224  receive_data_sync(payload, flags, fd);
225  }
233  void receive_data_async(std::vector<std::string> &vec, int flags = 0, int fd = 0) {
234  vec.clear();
235  std::string payload, record;
236  receive_data_async(payload, flags, fd);
237  std::stringstream ss(payload);
238  while (std::getline(ss, record, Socket::_rec_delim)) {
239  vec.push_back(record);
240  }
241  }
249  void receive_data_sync(std::vector<std::string> &vec, int flags = 0, int fd = 0) {
250  receive_data_async(vec, flags, fd);
251  send_ack(fd);
252  }
261  void receive_data(std::vector<std::string> &vec, int flags = 0, int fd = 0) {
262  receive_data_sync(vec, flags, fd);
263  }
269  void shutdown(int how = SHUT_RDWR) {
270  int res = ::shutdown(fd_, how);
271  if (res == -1) {
272  int error = errno;
273  throw SocketShutdownException(strerror(error), error);
274  }
275  }
276  protected:
277  int fd_;
278  int io_fd_;
279  char ACK;
280  private:
281  void get_ack(int fd) {
282  if (fd == 0)
283  fd = io_fd_;
284  char ack_check;
285  recv(fd, &ack_check, 1, 0);
286  if (ack_check != ACK) {
287  throw SocketWriteException("ACK failed");
288  }
289  }
290  void send_ack(int fd) {
291  if (fd == 0)
292  fd = io_fd_;
293  send(fd, &ACK, 1, 0);
294  }
295  };
296 } // namespace ffd
ffd::SocketBase::io_fd_
int io_fd_
Connection fd.
Definition: SocketBase.hpp:278
ffd::SocketShutdownException
Thrown when shutdown() fails.
Definition: Exceptions.hpp:116
ffd::SocketBase::close_connection
void close_connection(int fd=0)
Close a connection.
Definition: SocketBase.hpp:90
ffd::SocketBase::shutdown
void shutdown(int how=SHUT_RDWR)
Call shutdown() on the socket fd, waking any blocked threads.
Definition: SocketBase.hpp:269
ffd::SocketBase::receive_data
void receive_data(std::string &payload, int flags=0, int fd=0)
Receive a string and reply with ACK (alias for ffd::SocketBase::receive_data_sync(std::string&,...
Definition: SocketBase.hpp:223
ffd::SocketReadException
Thrown when read() fails.
Definition: Exceptions.hpp:107
ffd::Socket::_buff_sz
const int _buff_sz
Size of recv buffer. Can be overridden by defining FFD_SOCKET_BUFF_SZ before including header.
Definition: SocketBase.hpp:45
ffd::SocketCloseException
Thrown when close(fd_) returns -1 with strerror(errno) as what.
Definition: Exceptions.hpp:48
ffd::SocketBase::receive_data
void receive_data(std::vector< std::string > &vec, int flags=0, int fd=0)
Receive a vector as a record separator (0x1E) delimited string and reply with ACK (alias for ffd::Soc...
Definition: SocketBase.hpp:261
ffd::Socket::_backlog_default
const int _backlog_default
Number of connections to queue.
Definition: SocketBase.hpp:39
ffd
45Drives namespace
Definition: Bytes.hpp:27
ffd::SocketBase::receive_data_async
void receive_data_async(std::string &payload, int flags=0, int fd=0)
Receive a string.
Definition: SocketBase.hpp:186
ffd::SocketBase::send_data
void send_data(const std::vector< std::string > &vec, int flags=0, int fd=0)
Send a vector as a record separator (0x1E) delimited string and wait for ACK (alias for ffd::SocketBa...
Definition: SocketBase.hpp:176
ffd::SocketBase::send_data_sync
void send_data_sync(const std::vector< std::string > &vec, int flags=0, int fd=0)
Send a vector as a record separator (0x1E) delimited string and wait for ACK.
Definition: SocketBase.hpp:164
ffd::SocketBase::ACK
char ACK
char to send for acknowledging reception
Definition: SocketBase.hpp:279
ffd::SocketBase::send_data
void send_data(const std::string &str, int flags=0, int fd=0)
Send a string and wait for ACK (alias for ffd::SocketBase::send_data_sync(const std::string&,...
Definition: SocketBase.hpp:137
ffd::SocketWriteException
Thrown when write() fails.
Definition: Exceptions.hpp:98
ffd::SocketBase::send_data_async
void send_data_async(const std::string &str, int flags=0, int fd=0)
Send a string.
Definition: SocketBase.hpp:107
ffd::SocketCreateException
Thrown when socket() returns -1 with strerror(errno) as what.
Definition: Exceptions.hpp:39
ffd::SocketBase::send_data_sync
void send_data_sync(const std::string &str, int flags=0, int fd=0)
Send a string and wait for ACK.
Definition: SocketBase.hpp:123
ffd::SocketBase
Base Unix Socket Class for opening and closing the socket.
Definition: SocketBase.hpp:57
ffd::SocketBase::~SocketBase
~SocketBase()
Destroy the Socket Base object. Calls close() on the socket fd.
Definition: SocketBase.hpp:82
ffd::Socket::_rec_delim
const char _rec_delim
Record separator character.
Definition: SocketBase.hpp:51
ffd::SocketBase::receive_data_async
void receive_data_async(std::vector< std::string > &vec, int flags=0, int fd=0)
Receive a vector as a record separator (0x1E) delimited string.
Definition: SocketBase.hpp:233
ffd::SocketBase::fd_
int fd_
File descriptor of socket.
Definition: SocketBase.hpp:277
ffd::SocketBase::SocketBase
SocketBase(int domain, int type, int protocol=0)
Construct a new Socket Base object. Opens a socket fd.
Definition: SocketBase.hpp:69
ffd::SocketBase::receive_data_sync
void receive_data_sync(std::vector< std::string > &vec, int flags=0, int fd=0)
Receive a vector as a record separator (0x1E) delimited string and reply with ACK.
Definition: SocketBase.hpp:249
ffd::SocketBase::send_data_async
void send_data_async(const std::vector< std::string > &vec, int flags=0, int fd=0)
Send a vector as a record separator (0x1E) delimited string.
Definition: SocketBase.hpp:147
ffd::SocketBase::receive_data_sync
void receive_data_sync(std::string &payload, int flags=0, int fd=0)
Receive a string and reply with ACK.
Definition: SocketBase.hpp:211