0
    int tcp_sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);    

    struct sockaddr_in remoteaddr;
    struct sockaddr_in localAddr;
    short local_port = 22222;
    short remote_port = 33333;

    // local addr
    localAddr.sin_family      = AF_INET;
    localAddr.sin_port        = htons(local_port);
    localAddr.sin_addr.s_addr = 0xC0A80AA5; // we don't give a shit
    int addrLen = sizeof(struct sockaddr_in);

    //Now bind TCP to local addr
    int result = bind(tcp_sock,(struct sockaddr*)&localAddr,addrLen);
    if (result < 0)
    {
        perror("\nbind failed");
        close(tcp_sock);
        return -1;
    }

    result = connect(tcp_sock, (struct sockaddr*)&remoteaddr, sizeof(struct sockaddr_in));
    printf("\nConnect returned %d, error no: %d", result, errno);

Here the connect() call fails after a long time. Is there any way that I can make connect function return after a time of my choice? I tried calling close() from another thread but this doesn't change anything.

Tahlil
  • 2,680
  • 6
  • 43
  • 84
  • You can't call `close` in another thread. You can't even try it. Think about it -- how can you ensure the `close` occurs after the `connect` begins blocking rather than just before it? – David Schwartz Apr 13 '15 at 06:06
  • @DavidSchwartz: does that matter? A `close` beforehand should cause an error code (`EBADF`?) on the `connect` attempt. I'd be more worried about what happens if the `connect()` is already in flight.... – Tony Delroy Apr 13 '15 at 06:31
  • 1
    FWIW /- my tests on Linux yield "Bad file descriptor" if the socket is `close`d before `connect` from any thread, but if `connect` is already in flight it does not "timeout" / return. – Tony Delroy Apr 13 '15 at 07:05
  • 1
    @TonyD It matters enormously. Imagine if after you call `close`, but before `connect` executes, a system library running in another thread calls `socket` and you wind up connecting a socket that wasn't yours. That could cause sensitive information to be sent to an untrusted endpoint. Your tests are like crossing a street that happens to be empty without looking both ways and inferring that it's safe to cross a street without looking both ways. (And yes, this has actually happened.) – David Schwartz Apr 13 '15 at 08:17
  • @DavidSchwartz: solid point in those cases where background threads might be creating sockets; separately - there's a presumably unintended inference in your first comment that if `close` could be guaranteed to happen after `connect` was blocking then it might function as a timeout; that's misleading as illustrated by my testing. – Tony Delroy Apr 13 '15 at 08:49
  • @TonyD Your testing only indicates what happened when you tested it, not what's guaranteed to happen. And it's *impossible* to guarantee the result. What you did is like testing cases of undefined behavior. It may teach you something about what happens to happen on your precise combination of compiler, library, CPU, OS, and so on. But you go into it knowing that no particular behavior is guaranteed and you *can't* use it for anything that people need to rely on. – David Schwartz Apr 13 '15 at 09:56
  • @DavidSchwartz which part of the testing are you speaking of? That the socket can be closed from another thread, before `connect` is called, is not undefined behaviour and the code illustrating it was just a spot sanity check. For the case of calling `close` after `connect` was called, it provides a counter-example that - regardless of documentation - shows it's not reliable on Linux, and that would be extremely unlikely unless it was also documented as undefined or unsupported behaviour. No surprises there. – Tony Delroy Apr 13 '15 at 12:41

3 Answers3

4

Put the socket into non-blocking mode before calling connect(), then you can use select() to specify a timeout. select() will tell you whether the connection succeeds or times out. If successful, you can put the socket back into blocking mode, if you want. If failed/timeout, close the socket instead.

int tcp_sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);    

...

int flags = fcntl(tcp_sock, F_GETFL, 0);
fcntl(tcp_sock, F_SETFL, flags | O_NONBLOCK);

int errCode = 0;

result = connect(tcp_sock, ...);
if (result < 0)
{
    errCode = errno;

    if (errCode == EINPROGRESS)
    {
        fd_set wfd;
        FD_ZERO(&wfd);
        FD_SET(tcp_sock, &wfd);

        struct timeval timeout;
        timeout.tv_sec = ...;
        timeout.tv_usec = ...;

        result = select(tcp_sock+1, NULL, &wfd, NULL, &timeout);
        if (result > 0)
        {
            socklen_t len = sizeof(errCode);
            result = getsockopt(tcp_sock, SOL_SOCKET, SO_ERROR, &errCode, &len);
            if (result < 0)
                errCode = errno;
            else
                result = (errCode == 0) ? 0 : -1;
        }
        else if (result == 0)
        {
            errCode = ETIMEDOUT;
            result = -1;
        }
        else
        {
            errCode = errno;
        }
    }
}

if (result == 0)
{
    // connected
    fcntl(tcp_sock, F_SETFL, flags);
    ...
}
else
{
    // error, use errCode as needed
    ...
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I need to use it only in blocking mode. Thanks. – Tahlil Apr 13 '15 at 05:55
  • 1
    There is no way to make `connect()` timeout in blocking mode. – Remy Lebeau Apr 13 '15 at 06:03
  • 2
    @Tahlil Either you want `connect` to block until it succeeds or fails, or you don't. Pick. – David Schwartz Apr 13 '15 at 06:07
  • 1
    According to documentation you need to check the socket state. **A successful select does not say its connected!** See doc on [EINPROGRESS](https://man7.org/linux/man-pages/man2/connect.2.html): After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure). – kuga Apr 15 '21 at 10:04
2

Is there any way that I can make connect function return after a time of my choice?

Send the process a signal.

#define TIME_OUT_SECONDS (15)

void alarm_handler(int sig)
{
   /* Do nothing. */
}

int main(void)
{
  ...

  signal(alarm_handler);
  alarm(TIME_OUT_SECONDS);

  result = connect(tcp_sock, ...);

  ...
alk
  • 69,737
  • 10
  • 105
  • 255
1

The only way to get out of connect if its taking time is to make the socket non-blocking. If its a blocking socket, you cannot get out.

Is there any way that I can make connect function return after a time of my choice?

Your choice is to mark the socket non-blocking. No other way round. Blocking means 'block' the executing thread until a event happens on the socket.You can't having blocking socket and timing out as per your needs go together.

Use, select or epoll mechanisms to monitor the sockets.

Prabhu
  • 3,443
  • 15
  • 26