11
11
12
12
#pragma once
13
13
14
+ #include < limits>
14
15
#include < poll.h>
15
16
#include < unistd.h>
16
17
#include < sys/types.h>
33
34
34
35
#include < common/StateEnum.hpp>
35
36
#include " Log.hpp"
37
+ #include " NetUtil.hpp"
36
38
#include " Util.hpp"
37
39
#include " Buffer.hpp"
38
40
#include " SigUtil.hpp"
@@ -129,6 +131,8 @@ class SocketDisposition final
129
131
std::shared_ptr<Socket> _socket;
130
132
};
131
133
134
+ class ServerSocket ; // fwd
135
+
132
136
// / A non-blocking, streaming socket.
133
137
class Socket
134
138
{
@@ -151,6 +155,7 @@ class Socket
151
155
, _lastSeenTime(_creationTime)
152
156
, _bytesSent(0 )
153
157
, _bytesRcvd(0 )
158
+ , _isCounted(false )
154
159
{
155
160
init ();
156
161
}
@@ -162,19 +167,24 @@ class Socket
162
167
// Doesn't block on sockets; no error handling needed.
163
168
if constexpr (!Util::isMobileApp ())
164
169
{
165
- ::close (_fd);
170
+ if (::close (_fd))
171
+ LOG_SYS (" Ignored error closing socket #" << _fd);
166
172
LOG_DBG (" Closed socket " << toStringImpl ());
167
173
}
168
174
else
169
- {
170
175
fakeSocketClose (_fd);
176
+ if (_isCounted)
177
+ {
178
+ _isCounted = false ;
179
+ decrConnectionCount (_fd);
171
180
}
172
181
}
173
182
174
183
// / Returns true if this socket is open, i.e. allowed to be polled and not shutdown
175
184
bool isOpen () const { return _open; }
176
185
// / Returns true if this socket has been closed, i.e. rejected from polling and potentially shutdown
177
186
bool isClosed () const { return !_open; }
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,10 @@ class Socket
409
419
LOG_TRC (" Ignore further input on socket." );
410
420
_ignoreInput = true ;
411
421
}
422
+
423
+ // / Returns connection count, lock-free
424
+ static size_t connectionCount () { return statsConnectionCount; }
425
+
412
426
protected:
413
427
// / Construct based on an existing socket fd.
414
428
// / Used by accept() only.
@@ -422,6 +436,7 @@ class Socket
422
436
, _lastSeenTime(_creationTime)
423
437
, _bytesSent(0 )
424
438
, _bytesRcvd(0 )
439
+ , _isCounted(false )
425
440
{
426
441
init ();
427
442
}
@@ -493,6 +508,50 @@ class Socket
493
508
494
509
// / We check the owner even in the release builds, needs to be always correct.
495
510
std::thread::id _owner;
511
+
512
+ bool _isCounted; // if true, must call `decrConnectionCount` in connection `shutdown`
513
+
514
+ // / Decrements global connection count
515
+ // / @param fd the related file descriptor for logging
516
+ static void decrConnectionCount (int fd)
517
+ {
518
+ std::lock_guard<std::mutex> lock (statsMutex);
519
+ const size_t u = statsConnectionCount;
520
+ auto logPrefix = [fd](std::ostream& os) { os << ' #' << fd << " : " ; };
521
+ if (u > 0 )
522
+ {
523
+ const size_t v = --statsConnectionCount;
524
+ LOG_TRC (" TCP Limiter: Count decremented: " << u << " -> " << v);
525
+ }
526
+ else
527
+ LOG_WRN (" TCP Limiter: Count decrement underflow: " << u);
528
+ }
529
+
530
+ // / Increments global connection counter if not exceeding net::DefaultValue::maxTCPConnections.
531
+ // / Returns true if free connections were available, otherwise false.
532
+ // / No limitation is applied if net::DefaultValue::maxTCPConnections == 0.
533
+ // / @param fd the related file descriptor for logging
534
+ static bool checkAndIncrConnectionCount (int fd)
535
+ {
536
+ std::lock_guard<std::mutex> lock (statsMutex);
537
+ const size_t u = statsConnectionCount;
538
+ auto logPrefix = [fd](std::ostream& os) { os << ' #' << fd << " : " ; };
539
+ if (net::Defaults.maxTCPConnections == 0 || u < net::Defaults.maxTCPConnections )
540
+ {
541
+ const size_t v = ++statsConnectionCount;
542
+ LOG_TRC (" TCP Limiter: Count incremented: " << u << " -> " << v);
543
+ return true ;
544
+ }
545
+ else
546
+ {
547
+ LOG_WRN (" TCP Limiter: Limit reached: " << u);
548
+ return false ;
549
+ }
550
+ }
551
+ friend class ServerSocket ; // allow `checkAndIncrConnectionCount` for `ServerSocket::accept()`
552
+
553
+ static std::mutex statsMutex;
554
+ static std::atomic<size_t > statsConnectionCount; // accepted TCP IPv4/IPv6 socket count
496
555
};
497
556
498
557
inline std::ostream& operator <<(std::ostream& os, const Socket &s) { return s.stream (os); }
@@ -617,6 +676,18 @@ class SimpleSocketHandler : public ProtocolHandlerInterface
617
676
void getIOStats (uint64_t &, uint64_t &) override {}
618
677
};
619
678
679
+ // / A no-operation ProtocolHandlerInterface with dummy API.
680
+ class NoOpSocketHandler : public SimpleSocketHandler
681
+ {
682
+ public:
683
+ NoOpSocketHandler () = default ;
684
+
685
+ void onConnect (const std::shared_ptr<StreamSocket>& /* socket*/ ) override {}
686
+ void handleIncomingMessage (SocketDisposition &) override {}
687
+ int getPollEvents (std::chrono::steady_clock::time_point /* now*/ , int64_t &/* timeoutMaxMicroS*/ ) override { return 0 ; }
688
+ void performWrites (std::size_t /* capacity*/ ) override {}
689
+ };
690
+
620
691
// / Interface that receives and sends incoming messages.
621
692
class MessageHandlerInterface :
622
693
public std::enable_shared_from_this<MessageHandlerInterface>
0 commit comments