JAVA SOCKET connect超时设置是如何实现的?

JAVA SOCKET编程中 SOCKET中connect方法是可以设置连接超时时间的,如下:

java.net.Socket
public void connect(SocketAddress endpoint, int timeout) throws IOException

注:timeout为0表示不限超时 connect调用会一直阻塞直到连接建立或发生错误; 如果timeout>0 连接在timeout毫秒内没有建立,会返回抛出SocketTimeoutException异常。

而在Linux系统编程中 connect 系统调用是不能够设置超时时间的,API如下:

int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);

注:sockfd 是阻塞的

从TCP协议栈的角度考虑,连接建立需要三次握手,只要没有收到应答报文,那么就会重新发送(发送的时机需要深入Linux时钟机制);如果连接最终无法建立,那么TCP最终会放弃 connect 调用;对于基于 Berkeley BSD的系统,默认时间是 75秒; 这个时间对于应用来说太长了。

既然 Linux 没有提供一种控制 TCP协议栈 connect超时时间的 API, 那么该如何设置 connect 超时呢?

1、设置 fd 非阻塞, connect 调用立即返回,错误号为 EINPROGRESS

2、注册 fd 写事件 到 select 调用,同时设置超时时间为 timeout

查找 JDK 源码,看看 JAVA 是如何实现的?

相关文件: openjdk\jdk\src\share\transport\socket\socketTransport.c

                openjdk\jdk\src\solaris\transport\socket\socket_md.c

/*
     * To do a timed connect we make the socket non-blocking
     * and poll with a timeout;
     */
    if (attachTimeout > 0) {
        dbgsysConfigureBlocking(socketFD, JNI_FALSE);
    }

    err = dbgsysConnect(socketFD, (struct sockaddr *)&sa, sizeof(sa));
    if (err == DBG_EINPROGRESS && attachTimeout > 0) {
        err = dbgsysFinishConnect(socketFD, (long)attachTimeout);

        if (err == DBG_ETIMEOUT) {
            dbgsysConfigureBlocking(socketFD, JNI_TRUE);
            RETURN_ERROR(JDWPTRANSPORT_ERROR_TIMEOUT, "connect timed out");
        }
    }
int
dbgsysFinishConnect(int fd, long timeout) {
    int rv = dbgsysPoll(fd, 0, 1, timeout);
    if (rv == 0) {
        return DBG_ETIMEOUT;
    }
    if (rv > 0) {
        return 0;
    }
    return rv;
}
int
dbgsysPoll(int fd, jboolean rd, jboolean wr, long timeout) {
    struct pollfd fds[1];
    int rv;

    fds[0].fd = fd;
    fds[0].events = 0;
    if (rd) {
        fds[0].events |= POLLIN;
    }
    if (wr) {
        fds[0].events |= POLLOUT;
    }
    fds[0].revents = 0;

    rv = poll(&fds[0], 1, timeout);
    if (rv >= 0) {
        rv = 0;
        if (fds[0].revents & POLLIN) {
            rv |= DBG_POLLIN;
        }
        if (fds[0].revents & POLLOUT) {
            rv |= DBG_POLLOUT;
        }
    }
    return rv;
}
实现思路是一致的。

你可能感兴趣的:(java,tcp,socket,timeout,connect)