Cogs.Core
Connection.cpp
1#if !defined( EMSCRIPTEN )
2
3#include "Connection.h"
4#include "Network.h"
5
6#include "../Logging/Logger.h"
7
8#define __STD_FORMAT_MACROS
9
10#ifdef _WIN32
11 #include <system_error>
12 #include <Ws2tcpip.h>
13#else
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <netdb.h>
17 #include <unistd.h>
18
19 #include <netinet/ip.h>
20 #include <netinet/tcp.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
23#endif
24
25#include <inttypes.h>
26
27#ifdef _WIN32
28#define FUNC_STR __FUNCTION__
29#elif __GNUC__
30#define FUNC_STR __PRETTY_FUNCTION__
31#else
32#define FUNC_STR __func__
33#endif
34
35#define check_net_error(_x) check_net_error_impl(__FILE__, __LINE__, FUNC_STR, (_x))
36
37namespace
38{
39 Cogs::Logging::Log logger = Cogs::Logging::getLogger("Connection");
40
41#if defined( _WIN32 )
42 constexpr int sendFlags = 0;
43#else
44 constexpr int sendFlags = MSG_NOSIGNAL;
45#endif
46
47 void check_net_error_impl(const char *file, unsigned int line, const char *func, int err)
48 {
49#ifdef _WIN32
50 LOG_ERROR(logger, "%s(%d): %s: WSAGetLastError %d (%s)", file, line, func, err, std::system_category().message(err).c_str());
51#else
52 LOG_ERROR(logger, "%s(%d): %s: errno %d: %s", file, line, func, err, strerror(err));
53#endif
54 }
55}
56
61{
62#if defined( _WIN32 )
63 u_long nonblocking = val;
64 ioctlsocket(socket, FIONBIO, &nonblocking);
65#else
66 int nonblocking = val ? 1 : 0;
67 fcntl(socket, F_SETFL, O_NONBLOCK, &nonblocking);
68#endif
69}
70
75bool Cogs::Network::ConnectionBase::bind(AddrIn ip, uint16_t port)
76{
77 create();
78 if (socket != InvalidSocket) {
79 sockAddr = SockaddrIn(ip, port);
80
81 int result = ::bind(socket, sockAddr.getPtr(), sockAddr.size());
82 if (!result) {
83 if (!(flags & cDoNotProcess)) {
84 addConnection(this);
85 }
86 return true;
87 }
88 LOG_ERROR(logger, "Failed to bind to %s:%d.", ip.string().c_str(), port);
89#if defined( _WIN32 )
90 check_net_error(WSAGetLastError());
91#else
92 check_net_error(errno);
93#endif
94 disconnect();
95 }
96 return false;
97}
98
105bool Cogs::Network::ConnectionBase::send(const void* data, uint64_t byteCount, uint64_t& bytesSent)
106{
107 const char* ptr = reinterpret_cast<const char*>(data);
108 bytesSent = 0;
109
110 while (bytesSent < byteCount) {
111 int64_t justSent = ::send(socket, ptr, static_cast<int>(byteCount - bytesSent), sendFlags);
112
113 if (justSent == -1) {
114#if defined( _WIN32 )
115 int err = WSAGetLastError();
116 if (err == WSAEWOULDBLOCK) {
117 return true;
118 }
119#else
120 int err = errno;
121 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
122 return true;
123 }
124#endif
125 check_net_error(err);
126 disconnect();
127 return false;
128 }
129 bytesSent += justSent;
130 ptr += justSent;
131 }
132 return true;
133}
134
141bool Cogs::Network::ConnectionBase::receive(void* buffer, uint64_t byteCount, uint64_t& bytesReceived)
142{
143 char* ptr = reinterpret_cast<char*>(buffer);
144 bytesReceived = 0;
145
146 for (int64_t justReceived; bytesReceived < byteCount; bytesReceived += justReceived, ptr += justReceived) {
147 justReceived = ::recv(socket, ptr, static_cast<int>(byteCount - bytesReceived), 0);
148
149 switch (justReceived) {
150 case 0: {
151 disconnect();
152 return false;
153 }
154 case -1: {
155#if defined( _WIN32 )
156 int err = WSAGetLastError();
157 if (err == WSAEWOULDBLOCK) {
158 return true;
159 }
160#else
161 int err = errno;
162 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
163 return true;
164 }
165#endif
166 check_net_error(err);
167 disconnect();
168 return false;
169 }
170 }
171 }
172 return true;
173}
174
181bool Cogs::Network::ConnectionBase::sendTo(const void *data, uint64_t byteCount, uint64_t &bytesSent, const SockaddrIn &addr)
182{
183 const char* ptr = reinterpret_cast<const char*>(data);
184 bytesSent = 0;
185
186 for (; bytesSent < byteCount; ) {
187 int64_t justSent = ::sendto(socket, ptr, static_cast<int>(byteCount - bytesSent), sendFlags, addr.getPtr(), addr.size());
188
189 if (justSent == -1) {
190#if defined( _WIN32 )
191 int err = WSAGetLastError();
192 if (err == WSAEWOULDBLOCK) {
193 return true;
194 }
195#else
196 int err = errno;
197 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
198 return true;
199 }
200#endif
201 check_net_error(err);
202 disconnect();
203 return false;
204 }
205 bytesSent += justSent;
206 ptr += justSent;
207 }
208 return true;
209}
210
217bool Cogs::Network::ConnectionBase::recvFrom(void* buffer, uint64_t byteCount, uint64_t& bytesReceived, SockaddrIn &addr)
218{
219 char* ptr = reinterpret_cast<char*>(buffer);
220 bytesReceived = 0;
221
222 {
223 socklen_t fromlen = addr.size();
224 int64_t justReceived = ::recvfrom(socket, ptr, static_cast<int>(byteCount - bytesReceived), 0, addr.getPtr(), &fromlen);
225
226 switch (justReceived) {
227 case 0: {
228 disconnect();
229 return false;
230 }
231 case -1: {
232#if defined( _WIN32 )
233 int err = WSAGetLastError();
234 if (err == WSAEWOULDBLOCK) {
235 return true;
236 }
237#else
238 int err = errno;
239 if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
240 return true;
241 }
242#endif
243 check_net_error(err);
244 disconnect();
245 return false;
246 }
247 }
248 bytesReceived += justReceived;
249 ptr += justReceived;
250 }
251 return true;
252}
253
258{
259 if (socket != InvalidSocket) {
260 disconnect();
261 }
262 socket = ::socket(family(), type(), proto());
263
264 if (socket == InvalidSocket) {
265 LOG_ERROR(logger, "Failed to create socket.");
266 }
267}
268
273{
274 if (socket != InvalidSocket) {
275 LOG_INFO(logger, "Closing socket %" PRIu64 ".", static_cast<uint64_t>(socket));
276
277#if defined( _WIN32 )
278 ::closesocket(socket);
279#else
280 ::close(socket);
281#endif
282 socket = InvalidSocket;
283 }
284}
285
286/*
287 * UDP
288 */
289Cogs::Network::ConnectionUDP::~ConnectionUDP() {
290 removeConnection(this);
291 disconnect();
292}
293
295 create();
296 if (socket != InvalidSocket) {
297 int enableAddrReuse = 1;
298
299 setNonblocking(true);
300 setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&enableAddrReuse), sizeof(enableAddrReuse));
301
302 if (multicastAdapter.string() != "0.0.0.0") {
303 ip_mreq data = {};
304
305 data.imr_multiaddr = ip.addr;
306 data.imr_interface = multicastAdapter.addr;
307
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());
310 }
311 else {
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());
313 }
314 sockAddr = SockaddrIn(INADDR_ANY, port);
315 }
316 else {
317 sockAddr = SockaddrIn(ip, port);
318 }
319
320 if (!::bind(socket, sockAddr.getPtr(), sockAddr.size())) {
321 if (!(flags & cDoNotProcess)) {
322 addConnection(this);
323 }
324 return true;
325 }
326 LOG_ERROR(logger, "Failed to bind to %s:%d.", ip.string().c_str(), port);
327#if defined( _WIN32 )
328 check_net_error(WSAGetLastError());
329#else
330 check_net_error(errno);
331#endif
332 disconnect();
333 }
334 return false;
335}
336
337bool Cogs::Network::ConnectionUDP::disconnect()
338{
339 close();
340 return true;
341}
342/*
343 * TCP
344 */
345
350{
351#if defined( _WIN32 )
352 ::shutdown(socket, SD_BOTH);
353#else
354 ::shutdown(socket, SHUT_RDWR);
355#endif
356}
357
362{
363 flags &= ~cNoDelay;
364
365 if (socket != InvalidSocket) {
366 int nodelay = val;
367 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&nodelay), sizeof(nodelay));
368 }
369}
370
376Cogs::Network::ConnectionTCP::ConnectionTCP(Socket s, const SockaddrIn& addr)
377{
378 flags = cNoDelay | cConnected;
379 socket = s;
380 sockAddr = addr;
381 setNonblocking();
382 addConnection(this);
383}
384
389{
390 removeConnection(this);
391 disconnect();
392}
393
400{
401 sockAddr = addr;
402 return reconnect();
403}
404
409{
410 create();
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());
413
414 // If we have a connection timeout value defined, we'll set the socket to
415 // non-blocking, and call select with the timeout value.
416 if (connectTimeout) {
417 setNonblocking();
418 }
419
420 int result = ::connect(socket, sockAddr.getPtr(), sockAddr.size());
421
422#if defined( _WIN32 )
423 if ((result < 0) && (WSAGetLastError() == WSAEWOULDBLOCK )) {
424#else
425 if ((result < 0) && (errno == EINPROGRESS)) {
426#endif
427 fd_set waitSet;
428 struct timeval timeout;
429
430 FD_ZERO(&waitSet);
431 FD_SET(socket, &waitSet);
432
433 timeout.tv_sec = static_cast<long>(connectTimeout / 1000);
434 timeout.tv_usec = static_cast<long>((connectTimeout - (timeout.tv_sec * 1000)) * 1000);
435
436 result = select(static_cast<int>(socket + 1), nullptr, &waitSet, nullptr, &timeout);
437 if (result > 0) {
438 int socketError;
439 socklen_t len = sizeof(socketError);
440
441 getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&socketError), &len);
442
443 if (socketError == 0) {
444 // Connection succeeded.
445 result = 0;
446 }
447 }
448 else if (result == 0) {
449 // Connect timed out before completion.
450 result = -1;
451 }
452 }
453
454 if (result == 0) {
455 setNoDelay(flags & cNoDelay);
456 setNonblocking();
457 flags |= cConnected;
458 flags &= ~cRemove;
459 if (!(flags & cDoNotProcess)) {
460 addConnection(this);
461 }
462 return true;
463 }
464 else if (isAutoConnectEnabled()) {
465 if (!(flags & cDoNotProcess)) {
466 addConnection(this);
467 }
468 close();
469 }
470 else {
471#if defined( _WIN32 )
472 check_net_error(WSAGetLastError());
473#else
474 check_net_error(errno);
475#endif
476 close();
477 }
478 }
479 return false;
480}
481
489{
490 if (socket != InvalidSocket) {
491 LOG_INFO(logger, "Disconnecting socket %" PRIu64 ".", static_cast<uint64_t>(socket));
492
493 shutdown();
494 close();
495 }
496 flags &= ~(cListening | cConnected);
497
498 if (flags & cAutoReconnect) {
499 return !reconnect();
500 }
501 return true;
502}
503
508bool Cogs::Network::ConnectionTCP::listen(AddrIn ip, uint16_t port)
509{
510 create();
511 if (socket != InvalidSocket) {
512 sockAddr = SockaddrIn(ip, port);
513
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);
517
518 flags |= cListening;
519 if (!(flags & cDoNotProcess)) {
520 addConnection(this);
521 }
522 return true;
523 }
524 }
525#if defined( _WIN32 )
526 check_net_error(WSAGetLastError());
527#else
528 check_net_error(errno);
529#endif
530 disconnect();
531 }
532 return false;
533}
534
539{
540 if (flags & cListening) {
541 Socket newsocket;
542 sockaddr_in addr;
543 socklen_t size = sizeof(addr);
544
545 newsocket = ::accept(socket, reinterpret_cast<sockaddr*>(&addr), &size);
546 if (newsocket != InvalidSocket) {
547 SockaddrIn sockaddr(addr);
548 ConnectionTCP* newConnection = accept(newsocket, sockaddr);
549
550 if (newConnection) {
551 newConnection->setNoDelay(flags & cNoDelay);
552 return true;
553 }
554#if defined( _WIN32 )
555 ::shutdown(newsocket, SD_BOTH);
556 ::closesocket(newsocket);
557#else
558 ::shutdown(newsocket, SHUT_RDWR);
559 ::close(newsocket);
560#endif
561 }
562 return false;
563 }
564 return true;
565}
566
567#endif
Log implementation class.
Definition: LogManager.h:139
void create()
Internal wrapper for the socket() call.
Definition: Connection.cpp:257
void close()
Internal wrapper for closing the socket.
Definition: Connection.cpp:272
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.
Definition: Connection.cpp:141
bool send(const void *data, uint64_t byteCount, uint64_t &bytesSent)
Attempts to send as much of the specified data as possible.
Definition: Connection.cpp:105
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.
Definition: Connection.cpp:217
void setNonblocking(bool val=true)
Sets socket non-blocking mode to val.
Definition: Connection.cpp:60
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.
Definition: Connection.cpp:181
virtual bool processIncoming() override
Manages accepting incoming connections when this connection is a listener.
Definition: Connection.cpp:538
virtual bool connect(const SockaddrIn &addr)
Sets up this connection ready to connect to the specified remote address and port.
Definition: Connection.cpp:399
void shutdown()
Shutdown this connection.
Definition: Connection.cpp:349
virtual bool reconnect() override
Reconnects this connection to the remote host previously specified through a call to connect.
Definition: Connection.cpp:408
void setNoDelay(bool val=true)
Set the TCP_NODELAY flag (Enable / Disables Nagles algorithm).
Definition: Connection.cpp:361
virtual bool disconnect() override
Disconnects this connection.
Definition: Connection.cpp:488
virtual ~ConnectionTCP()
Destroys this connection.
Definition: Connection.cpp:388
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...
Definition: Connection.cpp:294
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180