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);
186 assert (type() == SOCK_DGRAM);
188 int64_t justSent = ::sendto(socket,
static_cast<const char*
>(data),
static_cast<int>(byteCount), sendFlags, addr.getPtr(), addr.size());
190 if (justSent == -1) {
192 int err = WSAGetLastError();
193 if (err == WSAEWOULDBLOCK) {
198 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
202 check_net_error(err);
206 bytesSent = justSent;
224 assert (type() == SOCK_DGRAM);
226 socklen_t fromlen = addr.size();
227 int64_t justReceived = ::recvfrom(socket,
static_cast<char*
>(buffer),
static_cast<int>(byteCount), 0, addr.getPtr(), &fromlen);
229 if (justReceived == -1) {
231 int err = WSAGetLastError();
232 if (err == WSAEWOULDBLOCK) {
237 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
241 check_net_error(err);
245 bytesReceived = justReceived;
254 if (socket != InvalidSocket) {
257 socket = ::socket(family(), type(), proto());
259 if (socket == InvalidSocket) {
260 LOG_ERROR(logger,
"Failed to create socket.");
269 if (socket != InvalidSocket) {
270 LOG_INFO(logger,
"Closing socket %" PRIu64
".",
static_cast<uint64_t
>(socket));
273 ::closesocket(socket);
277 socket = InvalidSocket;
284Cogs::Network::ConnectionUDP::~ConnectionUDP() {
285 removeConnection(
this);
291 if (socket != InvalidSocket) {
292 int enableAddrReuse = 1;
294 setNonblocking(
true);
295 setsockopt(socket, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<char*
>(&enableAddrReuse),
sizeof(enableAddrReuse));
297 if (multicastAdapter.string() !=
"0.0.0.0") {
300 data.imr_multiaddr = ip.addr;
301 data.imr_interface = multicastAdapter.addr;
303 if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
reinterpret_cast<char*
>(&data),
sizeof(data))) {
304 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());
307 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());
315 if (!::bind(socket, sockAddr.getPtr(), sockAddr.size())) {
316 if (!(flags & cDoNotProcess)) {
321 LOG_ERROR(logger,
"Failed to bind to %s:%d.", ip.string().c_str(), port);
323 check_net_error(WSAGetLastError());
325 check_net_error(errno);
332bool Cogs::Network::ConnectionUDP::disconnect()
347 ::shutdown(socket, SD_BOTH);
349 ::shutdown(socket, SHUT_RDWR);
360 if (socket != InvalidSocket) {
362 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
reinterpret_cast<char*
>(&nodelay),
sizeof(nodelay));
371Cogs::Network::ConnectionTCP::ConnectionTCP(Socket s,
const SockaddrIn& addr)
373 flags = cNoDelay | cConnected;
385 removeConnection(
this);
406 if (socket != InvalidSocket) {
407 LOG_INFO(logger,
"Reconnecting socket %" PRIu64
" to %s:%d.",
static_cast<uint64_t
>(socket), sockAddr.ip().string().c_str(), sockAddr.port());
411 if (connectTimeout) {
415 int result = ::connect(socket, sockAddr.getPtr(), sockAddr.size());
418 if ((result < 0) && (WSAGetLastError() == WSAEWOULDBLOCK )) {
420 if ((result < 0) && (errno == EINPROGRESS)) {
423 struct timeval timeout;
426 FD_SET(socket, &waitSet);
428 timeout.tv_sec =
static_cast<long>(connectTimeout / 1000);
429 timeout.tv_usec =
static_cast<long>((connectTimeout - (timeout.tv_sec * 1000)) * 1000);
431 result = select(
static_cast<int>(socket + 1),
nullptr, &waitSet,
nullptr, &timeout);
434 socklen_t len =
sizeof(socketError);
436 getsockopt(socket, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char*
>(&socketError), &len);
438 if (socketError == 0) {
443 else if (result == 0) {
450 setNoDelay(flags & cNoDelay);
454 if (!(flags & cDoNotProcess)) {
459 else if (isAutoConnectEnabled()) {
460 if (!(flags & cDoNotProcess)) {
467 check_net_error(WSAGetLastError());
469 check_net_error(errno);
485 if (socket != InvalidSocket) {
486 LOG_INFO(logger,
"Disconnecting socket %" PRIu64
".",
static_cast<uint64_t
>(socket));
491 flags &= ~(cListening | cConnected);
493 if (flags & cAutoReconnect) {
503bool Cogs::Network::ConnectionTCP::listen(
AddrIn ip, uint16_t port)
506 if (socket != InvalidSocket) {
509 if (!::bind(socket, sockAddr.getPtr(), sockAddr.size())) {
510 if (!::listen(socket, SOMAXCONN)) {
511 LOG_INFO(logger,
"Socket %" PRIu64
" listening for connections on %s:%d.",
static_cast<uint64_t
>(socket), sockAddr.ip().string().c_str(), port);
514 if (!(flags & cDoNotProcess)) {
521 check_net_error(WSAGetLastError());
523 check_net_error(errno);
535 if (flags & cListening) {
538 socklen_t size =
sizeof(addr);
540 newsocket = ::accept(socket,
reinterpret_cast<sockaddr*
>(&addr), &size);
541 if (newsocket != InvalidSocket) {
550 ::shutdown(newsocket, SD_BOTH);
551 ::closesocket(newsocket);
553 ::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 the specified data as a single datagram.
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