@@ -36,10 +36,14 @@ namespace comm
3636 */
3737enum class SocketState
3838{
39- Invalid, // /< Socket is initialized or setup failed
40- Connected, // /< Socket is connected and ready to use
41- Disconnected, // /< Socket is disconnected and cannot be used
42- Closed // /< Connection to socket got closed
39+ Invalid, // /< Socket is initialized but was never connected
40+ Connecting, // /< A first-time connect() attempt is in progress
41+ Connected, // /< Socket is connected and ready to use
42+ LostConnection, // /< Connection dropped unexpectedly; auto-reconnect is expected to pick it up
43+ Reconnecting, // /< An automatic reconnect attempt (after a drop) is in progress
44+ Disconnecting, // /< A deliberate disconnect() is in progress
45+ Disconnected, // /< Deliberately disconnected; will NOT auto-reconnect until connect() is called
46+ Closed // /< Neutral low-level close (clearable by a subsequent (re)connect)
4347};
4448
4549/* !
@@ -53,18 +57,25 @@ class TCPSocket
5357 std::chrono::milliseconds reconnection_time_;
5458 bool reconnection_time_modified_deprecated_ = false ;
5559
56- // Cancellation token used to abort a connection attempt that is currently in
57- // progress (either waiting inside connect() or sleeping between attempts).
58- // This is intentionally orthogonal to state_: a SocketState::Closed left over
59- // from a deliberate close()+connect() reconnect must NOT abort a fresh
60- // attempt, whereas requestStop() (used at teardown) must abort reliably and
61- // cannot be clobbered by setup()'s internal state_ resets.
62- std::atomic<bool > stop_requested_{ false };
63-
6460 void setupOptions ();
6561
62+ // True while a deliberate disconnect() is in progress or has completed (the "deliberate-stop
63+ // set"). The connect/retry machinery checks this to abort, and never overwrites these states,
64+ // so a teardown disconnect() that races a reconnect attempt is observed reliably.
65+ bool isStopRequested () const
66+ {
67+ const SocketState s = state_.load ();
68+ return s == SocketState::Disconnecting || s == SocketState::Disconnected;
69+ }
70+
71+ // Atomically moves state_ to `desired`, unless a deliberate disconnect() (Disconnecting or
72+ // Disconnected) is in effect. Returns true if the state was set, false if a deliberate stop is
73+ // active (in which case state_ is left untouched). This is how the connect/retry machinery
74+ // updates its in-progress state without ever clobbering a teardown signal.
75+ bool setStateUnlessStopRequested (SocketState desired);
76+
6677 // Performs an interruptible, non-blocking connect on an already-created socket.
67- // Polls in short slices so that a concurrent requestStop () aborts the attempt
78+ // Polls in short slices so that a concurrent disconnect () aborts the attempt
6879 // promptly on all platforms (POSIX close() of a blocked connect() is reliable,
6980 // Winsock's is not). Restores blocking mode on success.
7081 bool openInterruptible (socket_t socket_fd, struct sockaddr * address, size_t address_len);
@@ -78,6 +89,18 @@ class TCPSocket
7889 bool setup (const std::string& host, const int port, const size_t max_num_tries = 0 ,
7990 const std::chrono::milliseconds reconnection_time = DEFAULT_RECONNECTION_TIME );
8091
92+ /* !
93+ * \brief Re-establishes a connection after an unexpected drop, without clearing a deliberate
94+ * disconnect().
95+ *
96+ * Used by the automatic reconnect path (e.g. the producer loop). If a deliberate disconnect()
97+ * is in progress or has completed, this returns false immediately instead of reconnecting, so a
98+ * concurrent teardown is never undone. Otherwise behaves like setup() but marks the socket as
99+ * Reconnecting while the attempt is in progress.
100+ */
101+ bool reconnect (const std::string& host, const int port, const size_t max_num_tries = 0 ,
102+ const std::chrono::milliseconds reconnection_time = DEFAULT_RECONNECTION_TIME );
103+
81104 std::unique_ptr<timeval> recv_timeout_;
82105
83106public:
@@ -147,29 +170,42 @@ class TCPSocket
147170 bool write (const uint8_t * buf, const size_t buf_len, size_t & written);
148171
149172 /* !
150- * \brief Closes the connection to the socket.
173+ * \brief Establishes a connection to the configured host/port.
174+ *
175+ * This is the explicit (re)connect entry point. It clears any prior deliberate disconnect()
176+ * (moving the socket to Connecting) and then attempts to connect, retrying up to max_num_tries
177+ * times (unlimited when 0). Call this on the controlling thread; the automatic reconnect path
178+ * uses the internal reconnect() instead.
179+ *
180+ * \param host Host to connect to
181+ * \param port Port to connect to
182+ * \param max_num_tries Maximum number of connection attempts before failing. Unlimited when 0.
183+ * \param reconnection_time Time between connection attempts
184+ *
185+ * \returns True on success, false if the connection could not be established or was aborted by
186+ * a concurrent disconnect()
151187 */
152- void close ();
188+ bool connect (const std::string& host, const int port, const size_t max_num_tries = 0 ,
189+ const std::chrono::milliseconds reconnection_time = DEFAULT_RECONNECTION_TIME );
153190
154191 /* !
155- * \brief Requests that any in-progress (or future) connection attempt be aborted .
192+ * \brief Deliberately disconnects the socket and leaves it ready to connect() again .
156193 *
157- * Sets a sticky cancellation flag and closes the socket. A reconnect thread that is
158- * waiting inside setup() (either in connect() or in the between-attempt back-off) returns
159- * promptly instead of blocking until the reconnection timeout expires. Use this at teardown
160- * (e.g. from a destructor) before joining a reconnect thread. The flag stays set until
161- * clearStop() is called, so it cannot be lost by a racing internal state reset .
194+ * Moves the socket into the deliberate-stop set (Disconnecting then Disconnected) and closes the
195+ * underlying file descriptor. Any connect/reconnect attempt currently in progress (blocked in a
196+ * connect or sleeping between attempts) aborts promptly, and the automatic reconnect path will
197+ * not reconnect until connect() is called again. Use this at teardown (e.g. from a destructor)
198+ * before joining a reconnect thread .
162199 */
163- void requestStop ();
200+ void disconnect ();
164201
165202 /* !
166- * \brief Clears the cancellation flag set by requestStop() .
203+ * \brief Closes the connection to the socket .
167204 *
168- * Must be called on a controlled (re)start before attempting to connect again, so a socket
169- * that was previously stopped can be reused. Never call this concurrently with a reconnect
170- * thread.
205+ * Neutral low-level close. Unlike disconnect(), it does not prevent a subsequent automatic
206+ * reconnect, and it never downgrades a deliberate disconnect() that is already in effect.
171207 */
172- void clearStop ();
208+ void close ();
173209
174210 /* !
175211 * \brief Setup Receive timeout used for this socket.
0 commit comments