Skip to content

Conversation

@LowLevelLore
Copy link
Contributor

Add optional timeout API to TCP connect, send, and recv calls
Worked on issue #2682

Overview

This pull request introduces an optional timeout parameter (timeout_ms) to the existing TCP sink in spdlog, enabling users to specify a maximum duration for:

  • Connecting to a remote host
  • Sending log data over an established socket
  • Receiving acknowledgments or responses (where applicable)

By default, timeout_ms = 0, preserving the original blocking behavior. Setting a non-zero value switches to a non-blocking socket with select-based waiting.


Key Changes

  1. API Extension

    • tcp_client::connect(host, port, timeout_ms = 0) (both Windows and Linux implementations)
      • timeout_ms = 0 → original blocking connect
      • timeout_ms > 0 → non-blocking connect with select timeout
  2. Send/Receive Timeouts

    • After a successful connection, SO_SNDTIMEO and SO_RCVTIMEO are set to timeout_ms (on platforms supporting these).
    • Ensures that send(…) and recv(…) calls will return with an error if they block longer than the specified duration.
  3. Configuration Struct

    • Extended tcp_sink_config to include an optional timeout_ms field.
    • Legacy code using tcp_sink_config without timeout_ms continues to default to blocking behavior.
  4. Cross-Platform Helper Function

    • Extracted non-blocking timeout logic into connect_socket_with_timeout(...) for Windows, mirroring the Linux implementation.

Usage

  • Optional non-blocking timeout behavior
    auto sink   = std::make_shared<spdlog::sinks::tcp_sink_mt>(
              "127.10.0.1",  // host
              5000,           // port
              10000           // timeout in milliseconds
          );
  • Blocking behavior (no timeouts):
    auto sink = std::make_shared<spdlog::sinks::tcp_sink_mt>("127.0.0.1", 5000);
    — identical to pre-PR behavior.

@LowLevelLore
Copy link
Contributor Author

LowLevelLore commented Jun 30, 2025

@gabime sorry for the delay and not understanding the objective in the previous PR, but this time I have added a timeout to connect(), and also send() and recv() for tcp client.

@LowLevelLore
Copy link
Contributor Author

@gabime can I get some reviews on this PR ? So that I know if I should move forward.

@gabime
Copy link
Owner

gabime commented Jul 8, 2025

Sorry, I don’t have time to investigate it. Did you test it in both linux and windows? That the connect timeout works as expected?

@LowLevelLore
Copy link
Contributor Author

Yes I have tested on both linux and windows locally, the same can be proved from the appveyor CI pipeline.

@LowLevelLore
Copy link
Contributor Author

@gabime any updates on this PR ?

@gabime
Copy link
Owner

gabime commented Jul 13, 2025

I asked chatgpt to review.

here are a few minor improvements it suggests for the windows impl:

Always check return value of ioctlsocket (could fail).
Always check return value of getsockopt (could fail).
Don't rely on select modifying tv — it's const here (good), but just a reminder that it's normally modified.
Use SOCKET_ERROR instead of raw -1 consistently.

@LowLevelLore
Copy link
Contributor Author

Will work on these today

@LowLevelLore
Copy link
Contributor Author

@gabime , I have made the changes you requested and also re-tested on Windows.

@gabime
Copy link
Owner

gabime commented Jul 16, 2025

You put some includes in middle of the code

Changed improper misplaced includes.
@LowLevelLore
Copy link
Contributor Author

I fixed that, please have a look

@gabime
Copy link
Owner

gabime commented Jul 17, 2025

Accroding to chatgpt:

After getting WSAEWOULDBLOCK, you still must wait for the socket to become writable (i.e., the connection completes) using select() or WSAEventSelect() — and that is not done in the current code.

Right now, the logic assumes:

If connect() doesn’t return an immediate error other than WSAEWOULDBLOCK, then we're good to go.
But this is not enough. The socket is not guaranteed to be connected yet, and any attempt to send data might fail.

@LowLevelLore
Copy link
Contributor Author

// Wait until socket is writable or timeout expires
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(sockfd, &wfds);
rv = ::select(0, nullptr, &wfds, nullptr, const_cast<timeval *>(&tv));

Arent we waiting for writability here ?

@gabime gabime merged commit 9ecdf5c into gabime:v1.x Jul 17, 2025
13 of 14 checks passed
@gabime
Copy link
Owner

gabime commented Jul 17, 2025

Merged. Thanks @LowLevelLore

@LowLevelLore
Copy link
Contributor Author

You are welcome, it was quite an experience and learning..

junekimdev pushed a commit to junekimdev/spdlog that referenced this pull request Aug 13, 2025
* Now lets test on windows

* I guess testing on windows passes.

* Update tcp_client-windows.h

Added default value to argument

* Final edit

* Update tcp_client-windows.h

Changed improper misplaced includes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants