Can't seem to get a timeout working when connecting to a socket

前端 未结 2 1722
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-21 05:03

I\'m trying to supply a timeout for connect(). I\'ve searched around and found several articles related to this. I\'ve coded up what I believe should work but unfortunately I

相关标签:
2条回答
  • 2021-01-21 05:30

    From http://lxr.free-electrons.com/source/net/unix/af_unix.c:

    441 static int unix_writable(const struct sock *sk)
    442 {
    443         return sk->sk_state != TCP_LISTEN &&
    444                (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
    445 }
    

    I'm not sure what these buffers are that are being compared, but it looks obvious that the connected state of the socket is not being checked. So unless these buffers are modified when the socket becomes connected it would appear my unix socket will always be marked as writable and thus I can't use select() to determine when the non-blocking connect() has finished.

    and based on this snippet from http://lxr.free-electrons.com/source/net/unix/af_unix.c:

    1206 static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
    1207                                int addr_len, int flags)
    .
    .
    .
    1230         timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
    .
    .
    .
    1271         if (unix_recvq_full(other)) {
    1272                 err = -EAGAIN;
    1273                 if (!timeo)
    1274                         goto out_unlock;
    1275 
    1276                 timeo = unix_wait_for_peer(other, timeo);
    .
    .
    .
    

    it appears setting the send timeout might be capable of timing out the connect. Which also matches the documentation for SO_SNDTIMEO at http://man7.org/linux/man-pages/man7/socket.7.html.

    Thanks, Nick

    0 讨论(0)
  • 2021-01-21 05:50

    Your error handling on select() could use some cleanup. You don't really need to query SO_ERROR unless except_set is set. If select() returns > 0 then either write_set and/or except_set is set, and if except_set is not set then the connection was successful.

    Try something more like this instead:

    int domain_socket_send(const char* socket_name, unsigned char* buffer,
        unsigned int length, unsigned int timeout)
    {
        struct sockaddr_un addr;
        int fd;
        int result;
    
        // Create socket.
    
        fd = socket(AF_UNIX, SOCK_STREAM, 0);
        if (fd == -1)
            return errno;
    
        if (timeout != 0)
            {
    
            // Enabled non-blocking.
    
            int flags = fcntl(fd, F_GETFL);
            fcntl(fd, F_SETFL, flags | O_NONBLOCK);
            }
    
        // Set socket name.
    
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_UNIX;
        strncpy(addr.sun_path, socket_name, sizeof(addr.sun_path) - 1);
    
        // Connect.
    
        result = connect(fd, (struct sockaddr*) &addr, sizeof(addr));
        if (result == -1)
            {
    
            // If some error then we're done.
    
            if ((errno != EINPROGRESS) && (errno != EAGAIN))
                goto done;
    
            // Now select() to find out when connect() has finished.
    
            fd_set write_set;
            fd_set except_set;
    
            FD_ZERO(&write_set);
            FD_ZERO(&write_set);
            FD_SET(fd, &write_set);
            FD_SET(fd, &except_set);
    
            struct timeval tv;
    
            // Set timeout.
    
            tv.tv_sec = timeout / 1000000;
            tv.tv_usec = timeout % 1000000;
    
            result = select(fd + 1, NULL, &write_set, &except_set, &tv);
            if (result == -1)
                {
                goto done;
                }
            else if (result == 0)
                {
                result = -1;
                errno = ETIMEDOUT;
                goto done;
                }
            else if (FD_ISSET(fd, &except_set))
                {
                int socket_error;
                socklen_t len = sizeof(socket_error);
    
                // Get the result of the connect() call.
    
                result = getsockopt(fd, SOL_SOCKET, SO_ERROR, &socket_error, &len);
                if (result != -1)
                    {
                    result = -1;
                    errno = socket_error;
                    }
    
                goto done;
                }
            else
                {
                // connected
                }
            }
    
        // If we put the socket in non-blocking mode then put it back
        // to blocking mode.
    
        if (timeout != 0)
            {
            int flags = fcntl(fd, F_GETFL);
            fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
            }
    
        // Write buffer.
    
        result = write(fd, buffer, length);
    
    done:
        if (result == -1)
            result = errno;
        else
            result = 0;
    
        if (fd != -1)
            {
            shutdown(fd, SHUT_RDWR);
            close(fd);
            }
    
        return result;
    }
    
    0 讨论(0)
提交回复
热议问题