1#if !defined( EMSCRIPTEN )
6#include "../Logging/Logger.h"
8#define __STD_FORMAT_MACROS
11 #include <system_error>
19 #include <netinet/ip.h>
20 #include <netinet/tcp.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
28#define FUNC_STR __FUNCTION__
30#define FUNC_STR __PRETTY_FUNCTION__
32#define FUNC_STR __func__
35#define check_net_error(_x) check_net_error_impl(__FILE__, __LINE__, FUNC_STR, (_x))
42 constexpr int sendFlags = 0;
44 constexpr int sendFlags = MSG_NOSIGNAL;
47 void check_net_error_impl(
const char *file,
unsigned int line,
const char *func,
int err)
50 LOG_ERROR(logger,
"%s(%d): %s: WSAGetLastError %d (%s)", file, line, func, err, std::system_category().message(err).c_str());
52 LOG_ERROR(logger,
"%s(%d): %s: errno %d: %s", file, line, func, err, strerror(err));
63 u_long nonblocking = val;
64 ioctlsocket(socket, FIONBIO, &nonblocking);
66 int nonblocking = val ? 1 : 0;
67 fcntl(socket, F_SETFL, O_NONBLOCK, &nonblocking);
75bool Cogs::Network::ConnectionBase::bind(
AddrIn ip, uint16_t port)
78 if (socket != InvalidSocket) {
81 int result = ::bind(socket, sockAddr.getPtr(), sockAddr.size());
83 if (!(flags & cDoNotProcess)) {
88 LOG_ERROR(logger,
"Failed to bind to %s:%d.", ip.string().c_str(), port);
90 check_net_error(WSAGetLastError());
92 check_net_error(errno);
107 const char* ptr =
reinterpret_cast<const char*
>(data);
110 while (bytesSent < byteCount) {
111 int64_t justSent = ::send(socket, ptr,
static_cast<int>(byteCount - bytesSent), sendFlags);
113 if (justSent == -1) {
115 int err = WSAGetLastError();
116 if (err == WSAEWOULDBLOCK) {
121 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
125 check_net_error(err);
129 bytesSent += justSent;
143 char* ptr =
reinterpret_cast<char*
>(buffer);
146 for (int64_t justReceived; bytesReceived < byteCount; bytesReceived += justReceived, ptr += justReceived) {
147 justReceived = ::recv(socket, ptr,
static_cast<int>(byteCount - bytesReceived), 0);
149 switch (justReceived) {
156 int err = WSAGetLastError();
157 if (err == WSAEWOULDBLOCK) {
162 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
166 check_net_error(err);
183 const char* ptr =
reinterpret_cast<const char*
>(data);
186 for (; bytesSent < byteCount; ) {
187 int64_t justSent = ::sendto(socket, ptr,
static_cast<int>(byteCount - bytesSent), sendFlags, addr.getPtr(), addr.size());
189 if (justSent == -1) {
191 int err = WSAGetLastError();
192 if (err == WSAEWOULDBLOCK) {
197 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
201 check_net_error(err);
205 bytesSent += justSent;
219 char* ptr =
reinterpret_cast<char*
>(buffer);
223 socklen_t fromlen = addr.size();
224 int64_t justReceived = ::recvfrom(socket, ptr,
static_cast<int>(byteCount - bytesReceived), 0, addr.getPtr(), &fromlen);
226 switch (justReceived) {
233 int err = WSAGetLastError();
234 if (err == WSAEWOULDBLOCK) {
239 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
243 check_net_error(err);
248 bytesReceived += justReceived;
259 if (socket != InvalidSocket) {
262 socket = ::socket(family(), type(), proto());
264 if (socket == InvalidSocket) {
265 LOG_ERROR(logger,
"Failed to create socket.");
274 if (socket != InvalidSocket) {
275 LOG_INFO(logger,
"Closing socket %" PRIu64
".",
static_cast<uint64_t
>(socket));
278 ::closesocket(socket);
282 socket = InvalidSocket;
289Cogs::Network::ConnectionUDP::~ConnectionUDP() {
290 removeConnection(
this);
296 if (socket != InvalidSocket) {
297 int enableAddrReuse = 1;
299 setNonblocking(
true);
300 setsockopt(socket, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<char*
>(&enableAddrReuse),
sizeof(enableAddrReuse));
302 if (multicastAdapter.string() !=
"0.0.0.0") {
305 data.imr_multiaddr = ip.addr;
306 data.imr_interface = multicastAdapter.addr;
308 if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
reinterpret_cast<char*
>(&data),
sizeof(data))) {
309 LOG_ERROR(logger,
"Socket %" PRIdPTR
" failed to join multicast group %s using adapter %s.",
static_cast< uintptr_t
>( socket ), ip.string().c_str(), multicastAdapter.string().c_str());
312 LOG_INFO(logger,
"Socket %" PRIdPTR
" joined multicast group %s using adapter %s.",
static_cast< uintptr_t
>( socket ), ip.string().c_str(), multicastAdapter.string().c_str());
320 if (!::bind(socket, sockAddr.getPtr(), sockAddr.size())) {
321 if (!(flags & cDoNotProcess)) {
326 LOG_ERROR(logger,
"Failed to bind to %s:%d.", ip.string().c_str(), port);
328 check_net_error(WSAGetLastError());
330 check_net_error(errno);
337bool Cogs::Network::ConnectionUDP::disconnect()
352 ::shutdown(socket, SD_BOTH);
354 ::shutdown(socket, SHUT_RDWR);
365 if (socket != InvalidSocket) {
367 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char*
>(&nodelay),
sizeof(nodelay));
376Cogs::Network::ConnectionTCP::ConnectionTCP(Socket s,
const SockaddrIn& addr)
378 flags = cNoDelay | cConnected;
390 removeConnection(
this);
411 if (socket != InvalidSocket) {
412 LOG_INFO(logger,
"Reconnecting socket %" PRIu64
" to %s:%d.",
static_cast<uint64_t
>(socket), sockAddr.ip().string().c_str(), sockAddr.port());
416 if (connectTimeout) {
420 int result = ::connect(socket, sockAddr.getPtr(), sockAddr.size());
423 if ((result < 0) && (WSAGetLastError() == WSAEWOULDBLOCK )) {
425 if ((result < 0) && (errno == EINPROGRESS)) {
428 struct timeval timeout;
431 FD_SET(socket, &waitSet);
433 timeout.tv_sec =
static_cast<long>(connectTimeout / 1000);
434 timeout.tv_usec =
static_cast<long>((connectTimeout - (timeout.tv_sec * 1000)) * 1000);
436 result = select(
static_cast<int>(socket + 1),
nullptr, &waitSet,
nullptr, &timeout);
439 socklen_t len =
sizeof(socketError);
441 getsockopt(socket, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char*
>(&socketError), &len);
443 if (socketError == 0) {
448 else if (result == 0) {
455 setNoDelay(flags & cNoDelay);
459 if (!(flags & cDoNotProcess)) {
464 else if (isAutoConnectEnabled()) {
465 if (!(flags & cDoNotProcess)) {
472 check_net_error(WSAGetLastError());
474 check_net_error(errno);
490 if (socket != InvalidSocket) {
491 LOG_INFO(logger,
"Disconnecting socket %" PRIu64
".",
static_cast<uint64_t
>(socket));
496 flags &= ~(cListening | cConnected);
498 if (flags & cAutoReconnect) {
508bool Cogs::Network::ConnectionTCP::listen(
AddrIn ip, uint16_t port)
511 if (socket != InvalidSocket) {
514 if (!::bind(socket, sockAddr.getPtr(), sockAddr.size())) {
515 if (!::listen(socket, SOMAXCONN)) {
516 LOG_INFO(logger,
"Socket %" PRIu64
" listening for connections on %s:%d.",
static_cast<uint64_t
>(socket), sockAddr.ip().string().c_str(), port);
519 if (!(flags & cDoNotProcess)) {
526 check_net_error(WSAGetLastError());
528 check_net_error(errno);
540 if (flags & cListening) {
543 socklen_t size =
sizeof(addr);
545 newsocket = ::accept(socket,
reinterpret_cast<sockaddr*
>(&addr), &size);
546 if (newsocket != InvalidSocket) {
555 ::shutdown(newsocket, SD_BOTH);
556 ::closesocket(newsocket);
558 ::shutdown(newsocket, SHUT_RDWR);
Log implementation class.
void create()
Internal wrapper for the socket() call.
void close()
Internal wrapper for closing the socket.
bool receive(void *buffer, uint64_t byteCount, uint64_t &bytesReceived)
Attempts to read the specified number of bytes from the network socket this connection represents.
bool send(const void *data, uint64_t byteCount, uint64_t &bytesSent)
Attempts to send as much of the specified data as possible.
bool recvFrom(void *buffer, uint64_t byteCount, uint64_t &bytesReceived, SockaddrIn &addr)
Attempts to read the specified number of bytes from the network socket this connection represents.
void setNonblocking(bool val=true)
Sets socket non-blocking mode to val.
bool sendTo(const void *data, uint64_t byteCount, uint64_t &bytesSent, const SockaddrIn &addr)
Attempts to send as much of the specified data as possible.
virtual bool processIncoming() override
Manages accepting incoming connections when this connection is a listener.
virtual bool connect(const SockaddrIn &addr)
Sets up this connection ready to connect to the specified remote address and port.
void shutdown()
Shutdown this connection.
virtual bool reconnect() override
Reconnects this connection to the remote host previously specified through a call to connect.
void setNoDelay(bool val=true)
Set the TCP_NODELAY flag (Enable / Disables Nagles algorithm).
virtual bool disconnect() override
Disconnects this connection.
virtual ~ConnectionTCP()
Destroys this connection.
virtual bool bind(AddrIn ip, uint16_t port) override
Sets this connection up as a listener that will listen for and respond to incoming connection request...
constexpr Log getLogger(const char(&name)[LEN]) noexcept