Windows下的套接字读取超时:本机方法中的奇怪硬编码

use*_*601 6 c java sockets windows java-native-interface

我试图了解如何在本机代码中处理套接字读取超时,并发现一些奇怪的硬编码值,即5000毫秒:

if (timeout) {
    if (timeout <= 5000 || !isRcvTimeoutSupported) {
        int ret = NET_Timeout (fd, timeout);
        .....
        .....
    }
}
Run Code Online (Sandbox Code Playgroud)

来源:http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/windows/native/java/net/SocketInputStream.c

我可以看到,变量isRcvTimeoutSupported通常设置为true,但在设置套接字选项时可能会被删除为false:

    /*
     * SO_RCVTIMEO is only supported on Microsoft's implementation
     * of Windows Sockets so if WSAENOPROTOOPT returned then
     * reset flag and timeout will be implemented using
     * select() -- see SocketInputStream.socketRead.
     */

    if (isRcvTimeoutSupported) {
        jclass iCls = (*env)->FindClass(env, "java/lang/Integer");
        jfieldID i_valueID;
        jint timeout;

        CHECK_NULL(iCls);
        i_valueID = (*env)->GetFieldID(env, iCls, "value", "I");
        CHECK_NULL(i_valueID);
        timeout = (*env)->GetIntField(env, value, i_valueID);

        /*
         * Disable SO_RCVTIMEO if timeout is <= 5 second.
         */

        if (timeout <= 5000) {
            timeout = 0;
        }


        if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
            sizeof(timeout)) < 0) {

            if (WSAGetLastError() == WSAENOPROTOOPT) {
                isRcvTimeoutSupported = JNI_FALSE;
            } else {
                NET_ThrowCurrent(env, "setsockopt SO_RCVTIMEO");
            }

        }
        ......
        ......
    }
Run Code Online (Sandbox Code Playgroud)

来源:http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/windows/native/java/net/TwoStacksPlainSocketImpl.c

虽然我不太确定,看起来读取超时应该在高于5秒时通过套接字选项设置,如果小于或等于5秒则通过NET_Timeout设置.这是对的吗?

无论如何,这些硬编码5秒来自哪里?
我没有在MSDN上看到任何解释.

mni*_*tic 1

此代码决定如何实现阻塞套接字超时 - 是否使用SO_RCVTIMEO或 来实现select,因为 Windows 套接字支持两者(SO_RCVTIMEO并非所有平台都支持,甚至并非所有 Windows 实现都支持它,如代码注释中所述)。在幕后NET_Timeout使用。select

基本上算法是:

if SO_RCVTIMEO is supported and timeout is more than 5 seconds:
    use SO_RCVTIMEO
else:
    use select
Run Code Online (Sandbox Code Playgroud)

至于 5 秒阈值的来源,我最好的猜测是他们以某种方式(通过测试或反复试验?)发现select对于小于 5 秒的超时值更可靠或更准确。并不是说它与此问题具体相关(它适用于不同的操作系统),但这里有一个有人报告 SO_RCVTIMEO 对于小超时值不可靠的示例。