在JNA下为setsockopt确定平台

时间:2015-03-14 19:37:15

标签: java sockets jna

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

然而,我想做的是使我的代码比在Linux下运行的代码更具可重用性。我希望在几个目标平台上工作(尽可能)。如果socket选项不可用,则可能抛出异常。

我的挑战是这个。 JNA工作正常,但套接字选项的值在不同平台上是不同的。例如,SO_RCVBUF在OS-X下是0x1002,在Linux下是8(我意识到SO_RCVBUF可由普通Java setSockOpt控制 - 它是'一个易于使用lsof进行测试的示例。 SO_DONTROUTE在Linux下是5,在OS-X下是0x0010(并且不能通过Java setSockOpt控制)。

所以我要做的就是取一个代表套接字选项的enum值(SO_SNDBUFSO_RCVBUF或其他什么),然后查看依赖于平台的地图,因此我在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() {
    }
}

2 个答案:

答案 0 :(得分:1)

JNA提供的类com.sun.jna.Platform由JNA本身使用,具有查询OS系列和CPU架构的功能。

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

答案 1 :(得分:-1)

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

结果(即我如何使用上述想法来解决问题中的问题)https://github.com/abligh/jnasockopt - 特别是https://github.com/abligh/jnasockopt/blob/master/src/org/jnasockopt/JNASockOptionDetails.java