33
33
34
34
#include < common/StateEnum.hpp>
35
35
#include " Log.hpp"
36
+ #include " NetUtil.hpp"
36
37
#include " Util.hpp"
37
38
#include " Buffer.hpp"
38
39
#include " SigUtil.hpp"
@@ -129,6 +130,8 @@ class SocketDisposition final
129
130
std::shared_ptr<Socket> _socket;
130
131
};
131
132
133
+ class StreamSocket ; // fwd
134
+
132
135
// / A non-blocking, streaming socket.
133
136
class Socket
134
137
{
@@ -151,6 +154,7 @@ class Socket
151
154
, _lastSeenTime(_creationTime)
152
155
, _bytesSent(0 )
153
156
, _bytesRcvd(0 )
157
+ , _isCounted(false )
154
158
{
155
159
init ();
156
160
}
@@ -162,19 +166,25 @@ class Socket
162
166
// Doesn't block on sockets; no error handling needed.
163
167
if constexpr (!Util::isMobileApp ())
164
168
{
165
- ::close (_fd);
169
+ if (::close (_fd))
170
+ LOG_SYS (" Ignored error closing socket #" << _fd);
166
171
LOG_DBG (" Closed socket " << toStringImpl ());
167
172
}
168
173
else
169
- {
170
174
fakeSocketClose (_fd);
175
+ if (_isCounted)
176
+ {
177
+ _isCounted = false ;
178
+ decrTCPConnCount (_fd);
171
179
}
172
180
}
173
181
174
182
// / Returns true if this socket is open, i.e. allowed to be polled and not shutdown
175
183
bool isOpen () const { return _open; }
176
184
// / Returns true if this socket has been closed, i.e. rejected from polling and potentially shutdown
177
185
bool isClosed () const { return !_open; }
186
+ // / Returns true if this socket is counted against free maximum TCP connections
187
+ bool isCounted () const { return _isCounted; }
178
188
179
189
constexpr Type type () const { return _type; }
180
190
constexpr bool isIPType () const { return Type::IPv4 == _type || Type::IPv6 == _type; }
@@ -249,7 +259,7 @@ class Socket
249
259
if constexpr (!Util::isMobileApp ())
250
260
{
251
261
const int val = 1 ;
252
- if (::setsockopt (_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof (val)) == -1 )
262
+ if (isOpen () && ::setsockopt (_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof (val)) == -1 )
253
263
{
254
264
static std::once_flag once;
255
265
std::call_once (once,
@@ -409,6 +419,14 @@ class Socket
409
419
LOG_TRC (" Ignore further input on socket." );
410
420
_ignoreInput = true ;
411
421
}
422
+
423
+ // / Returns connection count
424
+ static size_t getConnectionCount ()
425
+ {
426
+ std::lock_guard<std::mutex> lock (maxTCPConnMutex);
427
+ return maxTCPConnCount;
428
+ }
429
+
412
430
protected:
413
431
// / Construct based on an existing socket fd.
414
432
// / Used by accept() only.
@@ -422,6 +440,7 @@ class Socket
422
440
, _lastSeenTime(_creationTime)
423
441
, _bytesSent(0 )
424
442
, _bytesRcvd(0 )
443
+ , _isCounted(false )
425
444
{
426
445
init ();
427
446
}
@@ -493,6 +512,53 @@ class Socket
493
512
494
513
// / We check the owner even in the release builds, needs to be always correct.
495
514
std::thread::id _owner;
515
+
516
+ // / true if counted against net::Defaults.maxTCPConnections and dtor must call `decrTCPConnCount`
517
+ // / post socket ::close where the socket has been returned to the system.
518
+ bool _isCounted;
519
+
520
+ // / Decrements global connection count
521
+ // / Call this after socket ::close, where it has been returned to the system.
522
+ // / @param fd the related file descriptor for logging
523
+ static void decrTCPConnCount (int fd)
524
+ {
525
+ std::lock_guard<std::mutex> lock (maxTCPConnMutex);
526
+ const size_t u = maxTCPConnCount;
527
+ auto logPrefix = [fd](std::ostream& os) { os << ' #' << fd << " : " ; };
528
+ if (u > 0 )
529
+ {
530
+ const size_t v = --maxTCPConnCount;
531
+ LOG_TRC (" TCP Limiter: Count decremented: " << v);
532
+ }
533
+ else
534
+ LOG_WRN (" TCP Limiter: Count decrement underflow: " << u);
535
+ }
536
+
537
+ // / Increments global TCP connection counter if not exceeding net::DefaultValue::maxTCPConnections.
538
+ // / Returns true if free connections were available, otherwise false.
539
+ // / No limitation is applied if net::DefaultValue::maxTCPConnections == 0.
540
+ // / @param fd the related file descriptor for logging
541
+ static bool checkAndIncrTCPConnCount (int fd)
542
+ {
543
+ std::lock_guard<std::mutex> lock (maxTCPConnMutex);
544
+ const size_t u = maxTCPConnCount;
545
+ auto logPrefix = [fd](std::ostream& os) { os << ' #' << fd << " : " ; };
546
+ if (net::Defaults.maxTCPConnections == 0 || u < net::Defaults.maxTCPConnections )
547
+ {
548
+ const size_t v = ++maxTCPConnCount;
549
+ LOG_TRC (" TCP Limiter: Count incremented: " << v);
550
+ return true ;
551
+ }
552
+ else
553
+ {
554
+ LOG_WRN (" TCP Limiter: Limit reached: " << u);
555
+ return false ;
556
+ }
557
+ }
558
+ friend class StreamSocket ; // allow `checkAndIncrConnectionCount`
559
+
560
+ static std::mutex maxTCPConnMutex;
561
+ static size_t maxTCPConnCount; // accepted TCP IPv4/IPv6 socket count
496
562
};
497
563
498
564
inline std::ostream& operator <<(std::ostream& os, const Socket &s) { return s.stream (os); }
@@ -1322,11 +1388,12 @@ class StreamSocket : public Socket,
1322
1388
_socketHandler.reset ();
1323
1389
}
1324
1390
1325
- // / Create a socket of type TSocket given an FD and a handler.
1391
+ // / Create a socket of type TSocket derived from StreamSocket given an FD and a handler.
1326
1392
// / We need this helper since the handler needs a shared_ptr to the socket
1327
1393
// / but we can't have a shared_ptr in the ctor.
1328
- template <typename TSocket>
1329
- static std::shared_ptr<TSocket> create (std::string hostname, const int fd, Type type,
1394
+ template <typename TSocket,
1395
+ std::enable_if_t < std::is_base_of_v<StreamSocket, TSocket>, bool > = true >
1396
+ static std::shared_ptr<TSocket> create (std::string hostname, int fd, Type type,
1330
1397
bool isClient, HostType hostType,
1331
1398
std::shared_ptr<ProtocolHandlerInterface> handler,
1332
1399
ReadType readType = ReadType::NormalRead,
@@ -1337,7 +1404,31 @@ class StreamSocket : public Socket,
1337
1404
throw std::runtime_error (" StreamSocket " + std::to_string (fd) +
1338
1405
" expects a valid SocketHandler instance." );
1339
1406
1340
- auto socket = std::make_shared<TSocket>(std::move (hostname), fd, type, isClient, hostType, readType, creationTime);
1407
+ bool isCounted = false ;
1408
+ if (!isClient && fd >= 0 && (type == Type::IPv4 || type == Type::IPv6))
1409
+ {
1410
+ // / Only limit TCP connections for server-side IPv4 or IPv6 TCP connections.
1411
+ // /
1412
+ // / If limited, the accepted FD gets released immediately
1413
+ // / and the TSocket ctor only receives a -1 FD, i.e. !isOpen(),
1414
+ // / which also signals ServerSocket::handlePoll to dismiss the instance.
1415
+ if (!Socket::checkAndIncrTCPConnCount (fd))
1416
+ {
1417
+ if constexpr (!Util::isMobileApp ())
1418
+ {
1419
+ auto logPrefix = [fd](std::ostream& os) { os << ' #' << fd << " : " ; };
1420
+ if (::close (fd))
1421
+ LOG_SYS (" Ignored error closing socket #" << fd);
1422
+ }
1423
+ else
1424
+ fakeSocketClose (fd);
1425
+ fd = -1 ; // closed!
1426
+ } else
1427
+ isCounted = true ;
1428
+ }
1429
+ auto socket = std::make_shared<TSocket>(std::move (hostname), fd, type, isClient, hostType,
1430
+ readType, creationTime);
1431
+ socket->_isCounted = isCounted;
1341
1432
socket->setHandler (std::move (handler));
1342
1433
1343
1434
return socket;
0 commit comments