libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
socket_wrapper_glibc.hpp
Go to the documentation of this file.
1 #ifndef LIBJMMCG_CORE_SOCKET_WRAPPER_GLIBC_HPP
2 #define LIBJMMCG_CORE_SOCKET_WRAPPER_GLIBC_HPP
3 
4 /******************************************************************************
5 ** Copyright © 2016 by J.M.McGuiness, coder@hussar.me.uk
6 **
7 ** This library is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU Lesser General Public
9 ** License as published by the Free Software Foundation; either
10 ** version 2.1 of the License, or (at your option) any later version.
11 **
12 ** This library 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 GNU
15 ** Lesser General Public License for more details.
16 **
17 ** You should have received a copy of the GNU Lesser General Public
18 ** License along with this library; if not, write to the Free Software
19 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 
22 #include "socket_wrapper.hpp"
23 #include "syscall_wrapper.hpp"
24 #include "thread_wrapper.hpp"
25 
26 #include <iostream>
27 
28 #include <arpa/inet.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 
32 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace socket { namespace glibc {
33 
34 namespace basic {
35 
36 /// A simple TCP/IP socket wrapper.
37 template<class LkT>
38 class wrapper {
39 public:
40  using exception_t=crt_exception<ppd::platform_api, ppd::heavyweight_threading>;
42  /// The lock-type to use to ensure that the underlying ::write()s occur atomically.
43  /**
44  The calls to the underlying socket ::write() may occur multiple times when under extreme load. This lock is used to ensure that the calls to write() occur atomically with respect to multiple threads.
45 
46  \see ::write()
47  */
48  using write_lock_t=LkT;
49  using atomic_t=typename write_lock_t::atomic_t;
50 
51  enum domain_t : int {
52  ip_v4=AF_INET
53  };
54  enum type_t : int {
55  stream=SOCK_STREAM
56  };
57  using socket_priority=socket::socket_priority;
58 
59  using socket_type=int;
60 
61  /// Wrap a TCP socket using the TCP/IP protocol.
62  explicit wrapper(socket_type opened_skt) noexcept(false);
63 
64  /// Wrap a TCP socket using the TCP/IP protocol.
65  explicit wrapper(type_t type=type_t::stream, domain_t domain=domain_t::ip_v4) noexcept(false);
66 
67  ~wrapper() noexcept(true);
68 
69  /// Write the whole message to the socket in one go.
70  /**
71  \param message The message to write, that must be as-if a POD.
72  */
73  template<
74  class MsgT ///< The type of the message to write.
75  >
76  void write(MsgT const &message) noexcept(true);
77 
78  /// Write the whole message to the socket in one go.
79  /**
80  \param message The message to write, that must be as-if a POD.
81  */
82  template<class V, std::size_t N>
83  void write(std::array<V, N> const &message) noexcept(true);
84 
85  /// Read the whole message from the socket in one go.
86  /**
87  \param dest The message will be placed into this buffer, which may be grown to accommodate the message.
88  \return False if all of the message was successfully read into the buffer, true otherwise.
89  */
90  template<
91  class MsgT ///< The type of the message to read, that must be as-if a POD.
92  >
93  bool read(MsgT &dest) noexcept(true);
94  /// Read the whole message from the socket in one go.
95  /**
96  \param dest The message will be placed into this stack-based buffer, which must be sufficiently large to accommodate the message read, otherwise UB will result.
97  \return False if all of the message was successfully read into the buffer, true otherwise.
98  */
99  template<class V, std::size_t SrcSz> bool
100  read(V (& dest)[SrcSz]) noexcept(true);
101 
102  /**
103  \return False if all of the message was successfully read into the buffer, true otherwise.
104  */
105  template<class MsgDetails, class V, std::size_t N> bool
106  read(std::array<V, N> &buff) noexcept(false);
107 
108  void close() noexcept(true);
109 
110  bool is_open() const noexcept(true);
111 
112  /// Set various options on the created socket to ensure more optimal behaviour.
113  /**
114  * \todo Consider setting MSG_ZEROCOPY.
115  * \param timeout In seconds.
116  */
117  void set_options(std::size_t min_message_size, std::size_t max_message_size, unsigned short timeout, socket_priority priority, std::size_t incoming_cpu) noexcept(false);
118 
119  std::string to_string() const noexcept(false);
120 
121 protected:
122  mutable atomic_t mutex_;
123  socket_type socket_;
124 
125  /**
126  \return False if all of the message was successfully read into the buffer, true otherwise.
127  */
128  bool read_msg(std::uint8_t *dest, std::size_t msg_size) noexcept(true);
129 
130 private:
131  /**
132  * <a href="https://stackoverflow.com/questions/8829238/how-can-i-trap-a-signal-sigpipe-for-a-socket-that-closes#8829311"/>
133  */
134  void ignore_sigpipe_for_a_socket_that_closes() noexcept(false);
135 };
136 
137 template<class LkT>
138 inline std::ostream &
139 operator<<(std::ostream &os, wrapper<LkT> const &ec) noexcept(false);
140 
141 }
142 
143 namespace client {
144 
145 /// A simple client wrapper.
146 template<class LkT>
147 class wrapper : public basic::wrapper<LkT> {
148 public:
149  using base_t=basic::wrapper<LkT>;
150  using domain_t=typename base_t::domain_t;
151  using exception_t=typename base_t::exception_t;
152  using type_t=typename base_t::type_t;
153  using socket_type=typename base_t::socket_type;
154 
155  /// Wrap a TCP socket using the TCP/IP protocol.
156  explicit wrapper(socket_type opened_skt) noexcept(false);
157 
158  /// Wrap a TCP socket using the TCP/IP protocol.
159  explicit wrapper(type_t type=type_t::stream, domain_t domain=domain_t::ip_v4) noexcept(false);
160 
161  void connect(char const *addr, uint16_t port) noexcept(false);
162 };
163 
164 }
165 
166 namespace server {
167 
168 /// A simple server wrapper.
169 template<class LkT>
170 class wrapper : public basic::wrapper<LkT> {
171 public:
172  using base_t=basic::wrapper<LkT>;
173  using domain_t=typename base_t::domain_t;
174  using exception_t=typename base_t::exception_t;
175  using socket_t=base_t;
176  using type_t=typename base_t::type_t;
177  using socket_type=typename base_t::socket_type;
178  using thread_traits=typename base_t::thread_traits;
179 
180  enum : int {
182  };
183 
184  /// Wrap a TCP socket using the TCP/IP protocol.
185  wrapper(char const *addr, uint16_t port, std::size_t min_message_size, std::size_t max_message_size, unsigned short timeout, socket_priority priority, std::size_t incoming_cpu, type_t type=type_t::stream, domain_t domain=domain_t::ip_v4) noexcept(false);
186  ~wrapper() noexcept(false);
187 
188  void run() noexcept(false);
189  void stop() noexcept(false);
190  bool stopped() const noexcept(true);
191 
192  template<class RecvProcMsgs> void
193  async_accept(RecvProcMsgs &&f) noexcept(false);
194 
195  void set_options(base_t &skt) const noexcept(false);
196 
197 private:
198  class recv_msg_ops_t;
199 
200  std::atomic<bool> exit_{false};
201  std::atomic<bool> exited_{false};
202  std::exception_ptr ex=nullptr;
203 
204  std::shared_ptr<recv_msg_ops_t> recv_msg_ops{};
205 };
206 
207 }
208 
209 } } } }
210 
212 
213 #endif