mirror of
https://github.com/fpagliughi/sockpp.git
synced 2026-01-12 00:04:45 +08:00
Added initial support for CAN FD on Linux
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
build/
|
||||
obj/
|
||||
lib/
|
||||
book/
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
@@ -41,10 +42,12 @@ lib/
|
||||
.hgignore
|
||||
.hgtags
|
||||
|
||||
# SlickEdit files
|
||||
# Various editor and IDE files
|
||||
sockpp.v*
|
||||
*.vpj
|
||||
.vscode/
|
||||
|
||||
# Misc
|
||||
fmt.sh
|
||||
|
||||
.vscode/
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@
|
||||
#include <linux/can.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
// #include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include "sockpp/platform.h"
|
||||
#include "sockpp/types.h"
|
||||
@@ -78,6 +79,11 @@ public:
|
||||
* The frame is initialized to all zeroes.
|
||||
*/
|
||||
can_frame() : base{} {}
|
||||
/**
|
||||
* Constructs a frame with the specified ID and no data.
|
||||
* @param canID The CAN identifier for the frame
|
||||
*/
|
||||
can_frame(canid_t canID) : can_frame{canID, nullptr, 0} {}
|
||||
/**
|
||||
* Constructs a frame with the specified ID and data.
|
||||
* @param canID The CAN identifier for the frame
|
||||
@@ -93,14 +99,159 @@ public:
|
||||
*/
|
||||
can_frame(canid_t canID, const void* data, size_t n) : base{} {
|
||||
this->can_id = canID;
|
||||
this->can_dlc = n;
|
||||
::memcpy(&this->data, data, n);
|
||||
if (data && n != 0) {
|
||||
n = std::min(n, size_t(CAN_MAX_DLEN));
|
||||
this->can_dlc = n;
|
||||
::memcpy(&this->data, data, n);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Construct a frame from a C library CAN frame.
|
||||
* @param frame A C lib CAN frame.
|
||||
*/
|
||||
can_frame(const base& frame) : base{frame} {}
|
||||
/**
|
||||
* Determines if this frame has an extended (29-bit) CAN ID.
|
||||
* @return @em true if this frame has an extended ID, @em false if not
|
||||
*/
|
||||
bool has_extended_id() const { return (can_id & CAN_EFF_FLAG) != 0; }
|
||||
/**
|
||||
* Determines if this is a remote transmission request (RTR) frame.
|
||||
* @return @em true if this is an RTR frame, @em false if not
|
||||
*/
|
||||
bool is_remote() const { return (can_id & CAN_RTR_FLAG) != 0; }
|
||||
/**
|
||||
* Determines if this is an error frame.
|
||||
* @return @em true if this is an error frame, @em false if not
|
||||
*/
|
||||
bool is_error() const { return (can_id & CAN_ERR_FLAG) != 0; }
|
||||
/**
|
||||
* Gets the numeric CAN ID.
|
||||
* @return The numeric CAN ID.
|
||||
*/
|
||||
canid_t id_value() const {
|
||||
if (has_extended_id())
|
||||
return can_id & CAN_EFF_MASK;
|
||||
return can_id & CAN_SFF_MASK;
|
||||
}
|
||||
/**
|
||||
* Sets the ID as a standard 11-bit value.
|
||||
* @param canID The new CAN ID, in the range 0 - 0x7FF.
|
||||
*/
|
||||
void set_standard_id(canid_t canID) {
|
||||
can_id &= ~(CAN_EFF_FLAG | CAN_SFF_MASK);
|
||||
can_id |= canID & CAN_SFF_MASK;
|
||||
}
|
||||
/**
|
||||
* Sets the ID as an extended 29-bit value.
|
||||
* @param canID The new CAN ID, in the range 0 - 0x1FFFFFFF.
|
||||
*/
|
||||
void set_extended_id(canid_t canID) {
|
||||
can_id &= ~CAN_EFF_MASK;
|
||||
can_id |= CAN_EFF_FLAG | (canID & CAN_EFF_MASK);
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* A remote transfer request (RTR) frame.
|
||||
* This is a classic frame with no data and the RTR flag set.
|
||||
*/
|
||||
class can_remote_frame : public can_frame
|
||||
{
|
||||
/** The base class is the CAN frame */
|
||||
using base = can_frame;
|
||||
|
||||
/** The size of the underlying address struct, in bytes */
|
||||
static constexpr size_t SZ = sizeof(can_frame);
|
||||
|
||||
public:
|
||||
/** Create a default remote frame */
|
||||
can_remote_frame() : base{CAN_RTR_FLAG} {}
|
||||
/**
|
||||
* Create a remote frame for the specified ID.
|
||||
* @param canID The CAN identifier for the frame
|
||||
*/
|
||||
can_remote_frame(canid_t canID) : base{CAN_RTR_FLAG | canID} {}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Class that represents a Linux SocketCAN FD frame.
|
||||
* This inherits from the Linux CAN fdframe struct, just providing easier
|
||||
* construction.
|
||||
*/
|
||||
class canfd_frame : public ::canfd_frame
|
||||
{
|
||||
/** The base class is the C library CAN frame struct. */
|
||||
using base = ::canfd_frame;
|
||||
|
||||
/** The size of the underlying address struct, in bytes */
|
||||
static constexpr size_t SZ = sizeof(::canfd_frame);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs an empty frame.
|
||||
* The frame is initialized to all zeroes.
|
||||
*/
|
||||
canfd_frame() : base{} {}
|
||||
/**
|
||||
* Constructs a frame with the specified ID and data.
|
||||
* @param canID The CAN identifier for the frame
|
||||
* @param data The data field for the frame
|
||||
*/
|
||||
canfd_frame(canid_t canID, const string& data)
|
||||
: canfd_frame{canID, data.data(), data.length()} {}
|
||||
/**
|
||||
* Constructs a frame with the specified ID and data.
|
||||
* @param canID The CAN identifier for the frame
|
||||
* @param data The data field for the frame
|
||||
* @param n The number of bytes in the data field
|
||||
*/
|
||||
canfd_frame(canid_t canID, const void* data, size_t n) : base{} {
|
||||
this->can_id = canID;
|
||||
if (data && n != 0) {
|
||||
this->len = n;
|
||||
::memcpy(&this->data, data, n);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Construct a frame from a C library CAN frame.
|
||||
* @param frame A C lib CAN frame.
|
||||
*/
|
||||
canfd_frame(const base& frame) : base{frame} {}
|
||||
/**
|
||||
* Determines if this frame has an extended (29-bit) CAN ID.
|
||||
* @return @em true if this frame has an extended ID, @em false if not
|
||||
*/
|
||||
bool has_extended_id() const { return (can_id & CAN_EFF_FLAG) != 0; }
|
||||
/**
|
||||
* Gets the numeric CAN ID.
|
||||
* @return The numeric CAN ID.
|
||||
*/
|
||||
canid_t id_value() const {
|
||||
if (has_extended_id())
|
||||
return can_id & CAN_EFF_MASK;
|
||||
return can_id & CAN_SFF_MASK;
|
||||
}
|
||||
/**
|
||||
* Sets the ID as a standard 11-bit value.
|
||||
* @param canID The new CAN ID, in the range 0 - 0x7FF.
|
||||
*/
|
||||
void set_standard_id(canid_t canID) {
|
||||
can_id &= ~(CAN_EFF_FLAG | CAN_SFF_MASK);
|
||||
can_id |= canID & CAN_SFF_MASK;
|
||||
}
|
||||
/**
|
||||
* Sets the ID as an extended 29-bit value.
|
||||
* @param canID The new CAN ID, in the range 0 - 0x1FFFFFFF.
|
||||
*/
|
||||
void set_extended_id(canid_t canID) {
|
||||
can_id &= ~CAN_EFF_MASK;
|
||||
can_id |= CAN_EFF_FLAG | (canID & CAN_EFF_MASK);
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -168,6 +168,16 @@ public:
|
||||
* @return A floating-point timestamp with microsecond precision.
|
||||
*/
|
||||
double last_frame_timestamp();
|
||||
/**
|
||||
* Turn FD mode for the socket on or off.
|
||||
*
|
||||
* When enabled, it allows the socket to read or write the larger CAN FD
|
||||
* frames.
|
||||
*/
|
||||
result<> set_fd_mode(bool on = true) {
|
||||
int val = (on) ? 1 : 0;
|
||||
return set_option(SOL_CAN_RAW, CAN_RAW_FD_FRAMES, val);
|
||||
}
|
||||
|
||||
// ----- Filters -----
|
||||
|
||||
@@ -202,14 +212,15 @@ public:
|
||||
return set_filters(filters.data(), filters.size());
|
||||
}
|
||||
|
||||
// ----- I/O -----
|
||||
// ----- I/O (classic) -----
|
||||
|
||||
/**
|
||||
* Sends a frame to the CAN interface at the specified address.
|
||||
* @param frame The CAN frame to send.
|
||||
* @param flags The flags. See send(2).
|
||||
* @param addr The remote destination of the data.
|
||||
* @return the number of bytes sent on success or, @em -1 on failure.
|
||||
* @return The number of bytes sent on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send_to(const can_frame& frame, int flags, const can_address& addr) {
|
||||
return base::send_to(&frame, sizeof(can_frame), flags, addr);
|
||||
@@ -218,7 +229,8 @@ public:
|
||||
* Sends a frame to the CAN interface at the specified address.
|
||||
* @param frame The CAN frame to send.
|
||||
* @param addr The remote destination of the data.
|
||||
* @return the number of bytes sent on success or, @em -1 on failure.
|
||||
* @return The number of bytes sent on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send_to(const can_frame& frame, const can_address& addr) {
|
||||
return send_to(frame, 0, addr);
|
||||
@@ -228,7 +240,8 @@ public:
|
||||
* The socket should be bound before calling this.
|
||||
* @param frame The CAN frame to send.
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @return @em zero on success, @em -1 on failure.
|
||||
* @return The number of bytes sent on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send(const can_frame& frame, int flags = 0) {
|
||||
return base::send(&frame, sizeof(can_frame), flags);
|
||||
@@ -239,7 +252,8 @@ public:
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @param srcAddr Receives the address of the peer that sent the
|
||||
* message
|
||||
* @return The number of bytes read or @em -1 on error.
|
||||
* @return The number of bytes read on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> recv_from(can_frame* frame, int flags, can_address* srcAddr = nullptr) {
|
||||
return base::recv_from(frame, sizeof(frame), flags, srcAddr);
|
||||
@@ -248,11 +262,70 @@ public:
|
||||
* Receives a message on the socket.
|
||||
* @param frame CAN frame to get the incoming data.
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @return The number of bytes read or @em -1 on error.
|
||||
* @return The number of bytes read on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> recv(can_frame* frame, int flags = 0) {
|
||||
return base::recv(frame, sizeof(can_frame), flags);
|
||||
}
|
||||
|
||||
// ----- I/O (FD) -----
|
||||
|
||||
/**
|
||||
* Sends an FD frame to the CAN interface at the specified address.
|
||||
* @param frame The CAN FD frame to send.
|
||||
* @param flags The flags. See send(2).
|
||||
* @param addr The remote destination of the data.
|
||||
* @return The number of bytes sent on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send_to(const canfd_frame& frame, int flags, const can_address& addr) {
|
||||
return base::send_to(&frame, sizeof(canfd_frame), flags, addr);
|
||||
}
|
||||
/**
|
||||
* Sends an FD frame to the CAN interface at the specified address.
|
||||
* @param frame The CAN FD frame to send.
|
||||
* @param addr The remote destination of the data.
|
||||
* @return The number of bytes sent on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send_to(const canfd_frame& frame, const can_address& addr) {
|
||||
return send_to(frame, 0, addr);
|
||||
}
|
||||
/**
|
||||
* Sends an FD frame to the CAN bus.
|
||||
* The socket should be bound before calling this.
|
||||
* @param frame The CAN FD frame to send.
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @return The number of bytes sent on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send(const canfd_frame& frame, int flags = 0) {
|
||||
return base::send(&frame, sizeof(canfd_frame), flags);
|
||||
}
|
||||
/**
|
||||
* Receives an FD frame from the CAN interface with the specified
|
||||
* address.
|
||||
* @param frame CAN frame to get the incoming data.
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @param srcAddr Receives the address of the peer that sent the
|
||||
* message, if non-null.
|
||||
* @return The number of bytes read on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> recv_from(canfd_frame* frame, int flags, can_address* srcAddr = nullptr) {
|
||||
return base::recv_from(frame, sizeof(canfd_frame), flags, srcAddr);
|
||||
}
|
||||
/**
|
||||
* Receives an FD frame on the socket.
|
||||
* @param frame CAN FD frame to get the incoming data.
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @return The number of bytes read on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> recv(canfd_frame* frame, int flags = 0) {
|
||||
return base::recv(frame, sizeof(canfd_frame), flags);
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
* @param addr The other address
|
||||
* @throw invalid_argument if the address is not IPv6
|
||||
*/
|
||||
inet6_address(const sock_address& addr);
|
||||
explicit inet6_address(const sock_address& addr);
|
||||
/**
|
||||
* Constructs the address by copying the specified structure.
|
||||
* @param addr The other address
|
||||
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
* @param addr The other address
|
||||
* @throw invalid_argument if the address is not IPv4
|
||||
*/
|
||||
inet_address(const sockaddr& addr);
|
||||
explicit inet_address(const sockaddr& addr);
|
||||
/**
|
||||
* Constructs the address by copying the specified structure.
|
||||
* @param addr The other address
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
* @param addr The other address
|
||||
* @throw invalid_argument if the address is not IPv4
|
||||
*/
|
||||
inet_address(const sock_address& addr);
|
||||
explicit inet_address(const sock_address& addr);
|
||||
/**
|
||||
* Constructs the address by copying the specified structure.
|
||||
* @param addr The other address
|
||||
|
||||
@@ -620,24 +620,14 @@ public:
|
||||
* @return The number of bytes sent on success or, the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send_to(const void* buf, size_t n, int flags, const sock_address& addr) {
|
||||
#if defined(_WIN32)
|
||||
auto cbuf = reinterpret_cast<const char*>(buf);
|
||||
return check_res<size_t>(
|
||||
::sendto(handle(), cbuf, int(n), flags, addr.sockaddr_ptr(), addr.size())
|
||||
);
|
||||
#else
|
||||
return check_res<ssize_t, size_t>(
|
||||
::sendto(handle(), buf, n, flags, addr.sockaddr_ptr(), addr.size())
|
||||
);
|
||||
#endif
|
||||
}
|
||||
result<size_t> send_to(const void* buf, size_t n, int flags, const sock_address& addr);
|
||||
/**
|
||||
* Sends a message to another socket.
|
||||
* @param buf The data to send.
|
||||
* @param n The number of bytes in the data buffer.
|
||||
* @param addr The remote destination of the data.
|
||||
* @return the number of bytes sent on success or, @em -1 on failure.
|
||||
* @return the number of bytes sent on success or, the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send_to(const void* buf, size_t n, const sock_address& addr) {
|
||||
return send_to(buf, n, 0, addr);
|
||||
@@ -646,7 +636,8 @@ public:
|
||||
* Sends a string to another socket.
|
||||
* @param s The string to send.
|
||||
* @param addr The remote destination of the data.
|
||||
* @return the number of bytes sent on success or, @em -1 on failure.
|
||||
* @return The number of bytes sent on success or, the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send_to(const string& s, const sock_address& addr) {
|
||||
return send_to(s.data(), s.length(), 0, addr);
|
||||
@@ -657,23 +648,17 @@ public:
|
||||
* @param buf The date to send.
|
||||
* @param n The number of bytes in the data buffer.
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @return @em zero on success, @em -1 on failure.
|
||||
* @return The number of bytes sent on success or, the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send(const void* buf, size_t n, int flags = 0) {
|
||||
#if defined(_WIN32)
|
||||
return check_res<ssize_t, size_t>(
|
||||
::send(handle(), reinterpret_cast<const char*>(buf), int(n), flags)
|
||||
);
|
||||
#else
|
||||
return check_res<ssize_t, size_t>(::send(handle(), buf, n, flags));
|
||||
#endif
|
||||
}
|
||||
result<size_t> send(const void* buf, size_t n, int flags = 0);
|
||||
/**
|
||||
* Sends a string to the socket at the default address.
|
||||
* The socket should be connected before calling this
|
||||
* @param s The string to send.
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @return @em zero on success, @em -1 on failure.
|
||||
* @return The number of bytes sent on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> send(const string& s, int flags = 0) {
|
||||
return send(s.data(), s.length(), flags);
|
||||
@@ -685,7 +670,8 @@ public:
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @param srcAddr Receives the address of the peer that sent the
|
||||
* message
|
||||
* @return The number of bytes read or @em -1 on error.
|
||||
* @return The number of bytes read on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> recv_from(void* buf, size_t n, int flags, sock_address* srcAddr = nullptr);
|
||||
/**
|
||||
@@ -694,7 +680,8 @@ public:
|
||||
* @param n The number of bytes to read.
|
||||
* @param srcAddr Receives the address of the peer that sent the
|
||||
* message
|
||||
* @return The number of bytes read or @em -1 on error.
|
||||
* @return The number of bytes read on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> recv_from(void* buf, size_t n, sock_address* srcAddr = nullptr) {
|
||||
return recv_from(buf, n, 0, srcAddr);
|
||||
@@ -704,17 +691,10 @@ public:
|
||||
* @param buf Buffer to get the incoming data.
|
||||
* @param n The number of bytes to read.
|
||||
* @param flags The option bit flags. See send(2).
|
||||
* @return The number of bytes read or @em -1 on error.
|
||||
* @return The number of bytes read on success, or the error code on
|
||||
* failure.
|
||||
*/
|
||||
result<size_t> recv(void* buf, size_t n, int flags = 0) {
|
||||
#if defined(_WIN32)
|
||||
return check_res<ssize_t, size_t>(
|
||||
::recv(handle(), reinterpret_cast<char*>(buf), int(n), flags)
|
||||
);
|
||||
#else
|
||||
return check_res<ssize_t, size_t>(::recv(handle(), buf, n, flags));
|
||||
#endif
|
||||
}
|
||||
result<size_t> recv(void* buf, size_t n, int flags = 0);
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -94,7 +94,7 @@ public:
|
||||
* @param path The path to the socket file.
|
||||
* @throw system_error if the path is invalid (too long, etc)
|
||||
*/
|
||||
unix_address(const string& path);
|
||||
explicit unix_address(const string& path);
|
||||
/**
|
||||
* Constructs an address given the specified path.
|
||||
* Sets the error code on failure.
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
* @param addr The other address
|
||||
* @throw invalid_argument if the address is not IPv4
|
||||
*/
|
||||
unix_address(const sockaddr& addr);
|
||||
explicit unix_address(const sockaddr& addr);
|
||||
/**
|
||||
* Constructs the address by copying the specified structure.
|
||||
* @param addr The other address
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
* @param addr The other address
|
||||
* @throw invalid_argument if the address is not IPv4
|
||||
*/
|
||||
unix_address(const sock_address& addr);
|
||||
explicit unix_address(const sock_address& addr);
|
||||
/**
|
||||
* Constructs the address by copying the specified structure.
|
||||
* @param addr The other address
|
||||
|
||||
@@ -327,6 +327,32 @@ result<> socket::close() {
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// I/O
|
||||
|
||||
result<size_t> socket::send_to(
|
||||
const void* buf, size_t n, int flags, const sock_address& addr
|
||||
) {
|
||||
#if defined(_WIN32)
|
||||
auto cbuf = reinterpret_cast<const char*>(buf);
|
||||
return check_res<size_t>(
|
||||
::sendto(handle(), cbuf, int(n), flags, addr.sockaddr_ptr(), addr.size())
|
||||
);
|
||||
#else
|
||||
return check_res<ssize_t, size_t>(
|
||||
::sendto(handle(), buf, n, flags, addr.sockaddr_ptr(), addr.size())
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
result<size_t> socket::send(const void* buf, size_t n, int flags /*=0*/) {
|
||||
#if defined(_WIN32)
|
||||
return check_res<ssize_t, size_t>(
|
||||
::send(handle(), reinterpret_cast<const char*>(buf), int(n), flags)
|
||||
);
|
||||
#else
|
||||
return check_res<ssize_t, size_t>(::send(handle(), buf, n, flags));
|
||||
#endif
|
||||
}
|
||||
|
||||
result<size_t>
|
||||
socket::recv_from(void* buf, size_t n, int flags, sock_address* srcAddr /*=nullptr*/) {
|
||||
@@ -344,5 +370,15 @@ socket::recv_from(void* buf, size_t n, int flags, sock_address* srcAddr /*=nullp
|
||||
#endif
|
||||
}
|
||||
|
||||
result<size_t> socket::recv(void* buf, size_t n, int flags /*=0*/) {
|
||||
#if defined(_WIN32)
|
||||
return check_res<ssize_t, size_t>(
|
||||
::recv(handle(), reinterpret_cast<char*>(buf), int(n), flags)
|
||||
);
|
||||
#else
|
||||
return check_res<ssize_t, size_t>(::recv(handle(), buf, n, flags));
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
} // namespace sockpp
|
||||
|
||||
Reference in New Issue
Block a user