socket.h
15.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
#pragma once
#include "socket-cpp/sock_address.h"
#include <chrono>
#include <string>
#include <tuple>
namespace osdev {
namespace components {
namespace socket-cpp {
#if !defined(SOCKPP_SOCKET_T_DEFINED)
typedef int socket_t; ///< The OS socket handle
const socket_t INVALID_SOCKET = -1; ///< Invalid socket descriptor
#define SOCKPP_SOCKET_T_DEFINED
#endif
/**
* Converts a number of microseconds to a relative timeval.
* @param dur A chrono duration of microseconds.
* @return A timeval
*/
timeval to_timeval(const std::chrono::microseconds& dur);
/**
* Converts a chrono duration to a relative timeval.
* @param dur A chrono duration.
* @return A timeval.
*/
template<class Rep, class Period>
timeval to_timeval(const std::chrono::duration<Rep,Period>& dur) {
return to_timeval(std::chrono::duration_cast<std::chrono::microseconds>(dur));
}
/**
* Converts a relative timeval to a chrono duration.
* @param tv A timeval.
* @return A chrono duration.
*/
inline std::chrono::microseconds to_duration(const timeval& tv)
{
auto dur = std::chrono::seconds{tv.tv_sec}
+ std::chrono::microseconds{tv.tv_usec};
return std::chrono::duration_cast<std::chrono::microseconds>(dur);
}
/**
* Converts an absolute timeval to a chrono time_point.
* @param tv A timeval.
* @return A chrono time_point.
*/
inline std::chrono::system_clock::time_point to_timepoint(const timeval& tv)
{
return std::chrono::system_clock::time_point {
std::chrono::duration_cast<std::chrono::system_clock::duration>(to_duration(tv))
};
}
/////////////////////////////////////////////////////////////////////////////
/**
* Base class for socket objects.
*
* This class wraps an OS socket handle and maintains strict ownership
* semantics. If a socket object has a valid, open socket, then it owns the
* handle and will close it when the object is destroyed.
*
* Objects of this class are not copyable, but they are moveable.
*/
class socket
{
/** The OS integer socket handle */
socket_t handle_;
/** Cache of the last error (errno) */
mutable int lastErr_;
/**
* The OS-specific function to close a socket handle/
* @param h The OS socket handle.
* @return @em true if the sock is closed, @em false on error.
*/
bool close(socket_t h);
// Non-copyable.
socket(const socket&) =delete;
socket& operator=(const socket&) =delete;
protected:
/**
* Closes the socket without checking for errors or updating the last
* error.
* This is used in internal open/connect type functions that fail after
* creating the socket, but want to preserve the previous failure
* condition.
* Assumes that the socket handle is valid.
* @return Always returns @em false.
*/
bool close_on_err() {
close(release());
return false;
}
/**
* OS-specific means to retrieve the last error from an operation.
* This should be called after a failed system call to set the
* lastErr_ member variable. Normally this would be called from
* @ref check_ret.
*/
static int get_last_error();
/**
* Cache the last system error code into this object.
* This should be called after a failed system call to store the error
* value.
*/
void set_last_error() {
lastErr_ = get_last_error();
}
/**
* Checks the value and if less than zero, sets last error.
* @tparam T A signed integer type of any size
* @param ret The return value from a library or system call.
* @return Returns the value sent to it, `ret`.
*/
template <typename T>
T check_ret(T ret) const{
lastErr_ = (ret < 0) ? get_last_error() : 0;
return ret;
}
/**
* Checks the value and if less than zero, sets last error.
* @tparam T A signed integer type of any size
* @param ret The return value from a library or system call.
* @return @em true if the value is a typical system success value (>=0)
* or @em false is is an error (<0)
*/
template <typename T>
bool check_ret_bool(T ret) const{
lastErr_ = (ret < 0) ? get_last_error() : 0;
return ret >= 0;
}
/**
* Checks the value and if it is not a valid socket, sets last error.
* This is specifically required for Windows which uses an unsigned type
* for its SOCKET.
* @param ret The return value from a library or system call that returns
* a socket, such as socket() or accept().
* @return Returns the value sent to it, `ret`.
*/
socket_t check_socket(socket_t ret) const {
lastErr_ = (ret == INVALID_SOCKET) ? get_last_error() : 0;
return ret;
}
/**
* Checks the value and if it is INVALID_SOCKET, sets last error.
* This is specifically required for Windows which uses an unsigned type
* for its SOCKET.
* @param ret The return value from a library or system call that returns
* a socket such as socket() or accept().
* @return @em true if the value is a valid socket (not INVALID_SOCKET)
* or @em false is is an error (INVALID_SOCKET)
*/
bool check_socket_bool(socket_t ret) const{
lastErr_ = (ret == INVALID_SOCKET) ? get_last_error() : 0;
return ret != INVALID_SOCKET;
}
public:
/**
* Creates an unconnected (invalid) socket
*/
socket() : handle_(INVALID_SOCKET), lastErr_(0) {}
/**
* Creates a socket from an existing OS socket handle.
* The object takes ownership of the handle and will close it when
* destroyed.
* @param h An OS socket handle.
*/
explicit socket(socket_t h) : handle_(h), lastErr_(0) {}
/**
* Move constructor.
* This takes ownership of the underlying handle in sock.
* @param sock An rvalue reference to a socket object.
*/
socket(socket&& sock) noexcept
: handle_(sock.handle_), lastErr_(sock.lastErr_) {
sock.handle_ = INVALID_SOCKET;
}
/**
* Destructor closes the socket.
*/
virtual ~socket() { close(); }
/**
* Initializes the socket (sockpp) library.
* This is only required for Win32. On platforms that use a standard
* socket implementation this is an empty call.
*/
static void initialize();
/**
* Shuts down the socket library.
* This is only required for Win32. On platforms that use a standard
* socket implementation this is an empty call.
*/
static void destroy();
/**
* Creates a socket with the specified communications characterics.
* Not that this is not normally how a socket is creates in the sockpp
* library. Applications would typically create a connector (client) or
* acceptor (server) socket which would take care of the details.
*
* This is included for completeness or for creating different types of
* sockets than are supported by the library.
*
* @param domain The communications domain for the sockets (i.e. the
* address family)
* @param type The communication semantics for the sockets (SOCK_STREAM,
* SOCK_DGRAM, etc).
* @param protocol The particular protocol to be used with the sockets
*
* @return A socket with the requested communications characteristics.
*/
static socket create(int domain, int type, int protocol=0);
/**
* Determines if the socket is open (valid).
* @return @em true if the socket is open, @em false otherwise.
*/
bool is_open() const { return handle_ != INVALID_SOCKET; }
/**
* Determines if the socket is closed or in an error state.
* @return @em true if the socket is closed or in an error state, @em
* false otherwise.
*/
bool operator!() const {
return handle_ == INVALID_SOCKET || lastErr_ != 0;
}
/**
* Determines if the socket is open and in an error-free state.
* @return @em true if the socket is open and in an error-free state,
* @em false otherwise.
*/
explicit operator bool() const {
return handle_ != INVALID_SOCKET && lastErr_ == 0;
}
/**
* Get the underlying OS socket handle.
* @return The underlying OS socket handle.
*/
socket_t handle() const { return handle_; }
/**
* Gets the network family of the address to which the socket is bound.
* @return The network family of the address (AF_INET, etc) to which the
* socket is bound. If the socket is not bound, or the address
* is not known, returns AF_UNSPEC.
*/
virtual sa_family_t family() const {
return address().family();
}
/**
* Creates a new socket that refers to this one.
* This creates a new object with an independent lifetime, but refers
* back to this same socket. On most systems, this duplicates the file
* handle using the dup() call.
* A typical use of this is to have separate threads for reading and
* writing the socket. One thread would get the original socket and the
* other would get the cloned one.
* @return A new socket object that refers to the same socket as this
* one.
*/
socket clone() const;
/**
* Creates a pair of connected sockets.
*
* Whether this will work at all is highly system and domain dependent.
* Currently it is only known to work for Unix-domain sockets on *nix
* systems.
*
* Note that applications would normally call this from a derived socket
* class which would return the properly type-cast sockets to match the
* `domain` and `type`.
*
* @param domain The communications domain for the sockets (i.e. the
* address family)
* @param type The communication semantics for the sockets (SOCK_STREAM,
* SOCK_DGRAM, etc).
* @param protocol The particular protocol to be used with the sockets
*
* @return A std::tuple of sockets. On error both sockets will be in an
* error state with the
*/
static std::tuple<socket, socket> pair(int domain, int type, int protocol=0);
/**
* Clears the error flag for the object.
* @param val The value to set the flag, normally zero.
*/
void clear(int val=0) { lastErr_ = val; }
/**
* Releases ownership of the underlying socket object.
* @return The OS socket handle.
*/
socket_t release() {
socket_t h = handle_;
handle_ = INVALID_SOCKET;
return h;
}
/**
* Replaces the underlying managed socket object.
* @param h The new socket handle to manage.
*/
void reset(socket_t h=INVALID_SOCKET);
/**
* Move assignment.
* This assigns ownership of the socket from the other object to this
* one.
* @return A reference to this object.
*/
socket& operator=(socket&& sock) noexcept {
// Give our handle to the other to close.
std::swap(handle_, sock.handle_);
lastErr_ = sock.lastErr_;
return *this;
}
/**
* Binds the socket to the specified address.
* @param addr The address to which we get bound.
* @return @em true on success, @em false on error
*/
bool bind(const sock_address& addr);
/**
* Gets the local address to which the socket is bound.
* @return The local address to which the socket is bound.
*/
sock_address_any address() const;
/**
* Gets the address of the remote peer, if this socket is connected.
* @return The address of the remote peer, if this socket is connected.
*/
sock_address_any peer_address() const;
/**
* Gets the value of a socket option.
*
* This is a thin wrapper for the system `getsockopt`.
*
* @param level The protocol level at which the option resides, such as
* SOL_SOCKET.
* @param optname The option passed directly to the protocol module.
* @param optval The buffer for the value to retrieve
* @param optlen Initially contains the lenth of the buffer, and on return
* contains the length of the value retrieved.
*
* @return bool @em true if the value was retrieved, @em false if an error
* occurred.
*/
bool get_option(int level, int optname, void* optval, socklen_t* optlen) const;
/**
* Gets the value of a socket option.
*
* @param level The protocol level at which the option resides, such as
* SOL_SOCKET.
* @param optname The option passed directly to the protocol module.
* @param val The value to retrieve
* @return bool @em true if the value was retrieved, @em false if an error
* occurred.
*/
template <typename T>
bool get_option(int level, int optname, T* val) const {
socklen_t len = sizeof(T);
return get_option(level, optname, (void*) val, &len);
}
/**
* Sets the value of a socket option.
*
* This is a thin wrapper for the system `setsockopt`.
*
* @param level The protocol level at which the option resides, such as
* SOL_SOCKET.
* @param optname The option passed directly to the protocol module.
* @param optval The buffer with the value to set.
* @param optlen Contains the lenth of the value buffer.
*
* @return bool @em true if the value was set, @em false if an error
* occurred.
*/
bool set_option(int level, int optname, const void* optval, socklen_t optlen);
/**
* Sets the value of a socket option.
*
* @param level The protocol level at which the option resides, such as
* SOL_SOCKET.
* @param optname The option passed directly to the protocol module.
* @param val The value to set.
*
* @return bool @em true if the value was set, @em false if an error
* occurred.
*/
template <typename T>
bool set_option(int level, int optname, const T& val) {
return set_option(level, optname, (void*) &val, sizeof(T));
}
/**
* Places the socket into or out of non-blocking mode.
* When in non-blocking mode, a call that is not immediately ready to
* complete (read, write, accept, etc) will return immediately with the
* error EWOULDBLOCK.
* @param on Whether to turn non-blocking mode on or off.
* @return @em true on success, @em false on failure.
*/
bool set_non_blocking(bool on=true);
/**
* Gets a string describing the specified error.
* This is typically the returned message from the system strerror().
* @param errNum The error number.
* @return A string describing the specified error.
*/
static std::string error_str(int errNum);
/**
* Gets the code for the last errror.
* This is typically the code from the underlying OS operation.
* @return The code for the last errror.
*/
int last_error() const { return lastErr_; }
/**
* Gets a string describing the last errror.
* This is typically the returned message from the system strerror().
* @return A string describing the last errror.
*/
std::string last_error_str() const {
return error_str(lastErr_);
}
/**
* Shuts down all or part of the full-duplex connection.
* @param how Which part of the connection should be shut:
* @li SHUT_RD (0) Further reads disallowed.
* @li SHUT_WR (1) Further writes disallowed
* @li SHUT_RDWR (2) Further reads and writes disallowed.
* @return @em true on success, @em false on error.
*/
bool shutdown(int how=SHUT_RDWR);
/**
* Closes the socket.
* After closing the socket, the handle is @em invalid, and can not be
* used again until reassigned.
* @return @em true if the sock is closed, @em false on error.
*/
bool close();
};
/**
* RAII class to initialize and then shut down the library.
* A single object of this class can be declared before any other classes in
* the library are used. The lifetime of the object should span the use of
* the other classes in the library, so declaring an object at the top of
* main() is usually the best choice.
* This is only required on some platforms, particularly Windows, but is
* harmless on other platforms. On some, such as POSIX, the initializer sets
* optional parameters for the library, and the destructor does nothing.
*/
class socket_initializer
{
public:
socket_initializer() { socket::initialize(); }
~socket_initializer() { socket::destroy(); }
};
} // End namespace socket-cpp
} // End namespace components
} // End namespace osdev