为 setsockopt 确定 JNA 下的平台

Determine platform under JNA for setsockopt

我正在 JNA 下编写 setsockopt 的实现。 Java 本身 supports setsockopt,但它不支持所有特定于平台的套接字选项。例如,它不支持 Linux 下的 [TCP_KEEPIDLE][2]。显然,这些选项中的许多都不是很便携,使用 JNA 是导致可移植性差的途径;我知道这一点。请不要费心告诉我这个想法非常可怕。

然而,我想做的是使我的代码比仅在 Linux 下工作的代码更具可重用性。我希望它(尽可能)在多个目标平台上工作。如果套接字选项不可用,它会抛出异常。

我的挑战是这样的。 JNA 工作正常,但套接字选项的值在不同平台上是不同的。例如,SO_RCVBUF 在 OS-X 下是 0x1002,在 Linux 下是 8(我意识到 SO_RCVBUF 可以由正常的 Java setSockOpt - 这是一个很容易用 lsof 测试的例子。 SO_DONTROUTE 在 Linux 下是 5,在 OS-X 下是 0x0010(并且无法通过 Java setSockOpt 控制).

所以我想要它做的是取一个表示套接字选项(SO_SNDBUFSO_RCVBUF 或其他)的 enum 值,然后在依赖于平台的地图,所以我在 OS-X 下得到 0x1002 / 0x010 并且在 Linux.

下得到 8 / 5

这很简单,但是我如何知道 JNA 下的平台是什么,以便我知道要使用哪个地图? JNA 必须以某种方式了解自己的平台,否则它不会(我猜想)知道如何调用本机库。

package sockettest;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.Socket;

import com.sun.jna.LastErrorException;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

public class JNASockOpt {

    private static Field fdField;
    static {
        Native.register("c");
        try {
            fdField = FileDescriptor.class.getDeclaredField("fd");
            fdField.setAccessible(true);
        } catch (Exception ex) {
            fdField = null;
        }
    }

    public static int getInputFd(Socket s) {
        try {
            FileInputStream in = (FileInputStream)s.getInputStream();
            FileDescriptor fd = in.getFD();
            return fdField.getInt(fd);
        } catch (Exception e) { }
        return -1;
    }

    public static int getOutputFd(Socket s) {
        try {
            FileOutputStream in = (FileOutputStream)s.getOutputStream();
            FileDescriptor fd = in.getFD();
            return fdField.getInt(fd);
        } catch (Exception e) { }
        return -1;
    }

    public static int getFd(Socket s) {
        int fd = getInputFd(s);
        if (fd != -1)
            return fd;
        return getOutputFd(s);
    }

    // The list of SOL_ and SO_ options is platform dependent
    public static final int SOL_SOCKET = 0xffff; // that's under OS-X, but it's 1 under Linux
    public static final int SO_RCVBUF = 0x1002; // that's under OS-X, but it's 8 under Linux
    public static final int SO_DONTROUTE = 0x0010; // that's under OS-X, but it's 5 under Linux

    private static native int setsockopt(int fd, int level, int option_name, Pointer option_value, int option_len) throws LastErrorException;

    public static void setSockOpt (Socket socket, int level, int option_name, int option_value) throws IOException {
        if (socket == null)
            throw new IOException("Null socket");
        int fd = getFd(socket);
        if (fd == -1)
            throw new IOException("Bad socket FD");
        IntByReference val = new IntByReference(option_value);
        try {
            setsockopt(fd, level, option_name, val.getPointer(), 4);
        } catch (LastErrorException ex) {
            throw new IOException("setsockopt: " + strerror(ex.getErrorCode()));
        }
    }

    public static native String strerror(int errnum);

    private JNASockOpt() {
    }
}

jnaplatform 通过字符串解析 System.getProperty("os.name"); 来做到这一点,这对我来说似乎很可怕,但如果 jnaplatform 做到这一点,我想这对我来说应该足够好了。

https://github.com/abligh/jnasockopt - specifically https://github.com/abligh/jnasockopt/blob/master/src/org/jnasockopt/JNASockOptionDetails.java

结果(即我如何使用上述思路解决问题中的问题)

JNA提供的classcom.sun.jna.Platform是JNA自己使用的,具有查询OS家族和CPU架构的功能

isMac()isLinux() 有静态方法。