#ifndef __Socket_h__ #define __Socket_h__ #include "ZString.h" #include #include #include namespace core { class EPoll; /// /// Socket /// /// The core component to managing a socket. /// /// Hooks into the EPoll through the registration and unregistration process and provides a /// communication socket of the specified protocol type. This object provides for all receiving /// data threading through use of the EPoll object and also provides buffering for output data /// requests to the socket. /// /// A program using a socket object can request to open a socket (network or device) and /// communicate through the streambuffer interface of the socket object. /// /// The socket side of the Socket accepts EPOLLIN event and will maintain the data in a buffer /// for the stream readers to read. A onDataReceived event is then sent with the data received in /// the buffer that can be read through the stream. Only sockets that send events to epoll can be /// used with this object. /// /// When writing to the stream the data is written into a buffer and a EPOLLOUT is scheduled. Upon /// receiving the EPOLLOUT event then the buffer is written to the socket output. /// class Socket { public: /// /// Constructor /// /// @param ePoll The EPoll socket descriptor. /// @param text A title for this socket. /// Socket(EPoll &ePoll, std::string text = ""); /// /// Destructor /// virtual ~Socket(); /// /// Use the shutdown() method to terminate the socket connection and remove resources. /// This method is provided to ensure that all destructors are called for all inherited /// objects with a virtual destructor. /// void shutdown(std::string text = "unknown"); /// /// setDescriptor establishes the file descriptor for the socket and registers the socket /// on the EPoll controller. setDescriptor will invoke the onRegister() event. /// void setDescriptor(int descriptor); /// fifo; // Counts consecutive writeSocket() calls that sent zero bytes for // the SAME queued fifo entry (SSL_ERROR_WANT_READ from // TLS::sendData() is the confirmed real case - see writeSocket()'s // own doc comment). resetSocket() re-arms EPOLLWRNORM whenever the // fifo is non-empty regardless of WHY the last send failed, and an // already-writable TCP socket (the common case) makes EPOLLWRNORM // fire again essentially immediately - so a send that keeps // failing for a reason unrelated to socket writability (like SSL // needing a read first) spins this cycle as fast as the CPU can // run it, forever, with zero actual progress. Confirmed live: one // stuck connection generated a 55GB, then a 12.9GB debug log // before being noticed, at roughly 20 log lines per millisecond. // Reset to 0 on any successful send (even a partial one - real // progress happened); past the threshold, the connection is // clearly never going to un-stick itself and gets shut down. int consecutiveWriteStalls = 0; // Set by writeSocket() when the last failed send specifically // needed a read to proceed (sendNeedsRead() true) rather than // being genuinely stalled on write-readiness. resetSocket() skips // adding EPOLLWRNORM to the re-armed event mask while this is // true - re-arming it anyway just spins the CPU as fast as it can // loop, since an already-writable TCP socket keeps firing // EPOLLWRNORM immediately while the actual blocker (more bytes // from the peer) has nothing to do with write-readiness at all. // Cleared on any EPOLLIN event (see eventReceived()) so the write // gets a fresh attempt via that same event's own trailing // resetSocket() call, once there's real reason to expect progress. bool waitingOnReadToWrite = false; void resetSocket(); std::mutex lock; }; } #endif