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

使用jdk中的setSoTimeout(int timeout) 方法,以毫秒为单位。

  参考代码:

SocketAddress address = new InetSocketAddress("192.168.0.104", 8888);
ServerSocket s = new ServerSocket();  
s.bind(address); 
Socket socket = s.accept();
socket.setSoTimeout(5 * 1000); //5秒钟

  将此选项设为非零的超时值时,在与此 Socket 关联的 InputStream 上调用 read() 将只阻塞此时间长度。

  如果超过超时值,将引发 java.net.SocketTimeoutException,虽然 Socket 仍旧有效。选项必须在进入阻塞操作前被启用才能生效。

  另外:超时值必须是 > 0 的数。超时值为 0 被解释为无穷大超时值。                  

    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;
    }
    实现思路是一致的。

    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值