JAVA SOCKET connect超時設置是如何實現的?

JAVA SOCKET編程中 SOCKET中connect方法是能夠設置鏈接超時時間的,以下: java

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

注:timeout爲0表示不限超時 connect調用會一直阻塞直到鏈接創建或發生錯誤; 若是timeout>0 鏈接在timeout毫秒內沒有創建,會返回拋出SocketTimeoutException異常。 編程

而在Linux系統編程中 connect 系統調用是不可以設置超時時間的,API以下: socket

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

注:sockfd 是阻塞的 .net

從TCP協議棧的角度考慮,鏈接創建須要三次握手,只要沒有收到應答報文,那麼就會從新發送(發送的時機須要深刻Linux時鐘機制);若是鏈接最終沒法創建,那麼TCP最終會放棄 connect 調用;對於基於 Berkeley BSD的系統,默認時間是 75秒; 這個時間對於應用來講太長了。 code

既然 Linux 沒有提供一種控制 TCP協議棧 connect超時時間的 API, 那麼該如何設置 connect 超時呢? 事件

一、設置 fd 非阻塞, connect 調用當即返回,錯誤號爲 EINPROGRESS 源碼

二、註冊 fd 寫事件 到 select 調用,同時設置超時時間爲 timeout it

查找 JDK 源碼,看看 JAVA 是如何實現的? io

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

                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;
}
實現思路是一致的。
相關文章
相關標籤/搜索