简体   繁体   English

确定JNA下的setSockopt平台

[英]Determine platform under JNA for setsockopt

I'm writing an implementation of setsockopt under JNA. 我正在JNA下编写setsockopt的实现。 Java itself supports setsockopt , but it doesn't support all the platform specific socket options. Java本身支持setsockopt ,但不支持所有平台特定的套接字选项。 For instance, it doesn't support [TCP_KEEPIDLE][2] under Linux. 例如,它在Linux下不支持[TCP_KEEPIDLE][2] Clearly, many of these options are not very portable, and using JNA is a route to poor portability; 显然,这些选项中的许多都不是很容易移植,并且使用JNA可以降低可移植性。 I am aware of this. 我知道这一点。 Please don't bother to tell me the idea is deeply horrible. 请不要打扰告诉我这个想法非常可怕。

What I'd like to do, however, is make my code a little more reuseable than something that just works under Linux. 但是,我想做的是使我的代码比仅在Linux下工作的代码更具可重用性。 I'd like it to work (as far as is possible) on several target platforms. 我希望它能够(尽可能)在多个目标平台上工作。 If the socket option is not available, it can throw an exception. 如果套接字选项不可用,则可能引发异常。

My challenge is this. 我的挑战是这个。 The JNA works fine, but the values of the socket options are different across platforms. JNA可以正常工作,但是套接字选项的值在不同平台之间是不同的。 For instance, SO_RCVBUF is 0x1002 under OS-X and 8 under Linux (I realise SO_RCVBUF is controllable by the normal Java setSockOpt - it's an example that's easy to test with lsof ). 例如,在OS-X下, SO_RCVBUF0x1002 ,在Linux下为8 (我意识到SO_RCVBUF可通过常规Java setSockOpt来控制-这是一个很容易用lsof进行测试的示例)。 SO_DONTROUTE is 5 under Linux, and 0x0010 under OS-X (and that isn't controllable via Java setSockOpt ). 在Linux下, SO_DONTROUTE5 ,在OS-X下为SO_DONTROUTE (该值不能通过Java setSockOpt控制)为0x0010

So what I'd like it to do is to take an enum value representing the socket option ( SO_SNDBUF , SO_RCVBUF or whatever), and look that up in a platform dependent map, so I get 0x1002 / 0x010 under OS-X and 8 / 5 under Linux. 所以我想做的是获取一个代表套接字选项的enum值( SO_SNDBUFSO_RCVBUF或其他),并在依赖于平台的映射中进行查找,因此在OS-X和8下获得0x1002 / 0x010 5在Linux下。

That's easy enough, but how do I tell what the platform is under JNA so I know which map to use? 这很容易,但是我如何知道JNA下的平台是什么,所以我知道要使用哪个地图? JNA must somehow have a sense of its own platform, or it would not (I presume) know how to call the native libraries. 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() {
    }
}

The class com.sun.jna.Platform provided by JNA is used by JNA itself and has functions for querying the OS family and CPU architecture. JNA本身使用JNA提供的com.sun.jna.Platform类,并具有查询OS系列和CPU体系结构的功能。

There are static methods for isMac() and isLinux() . isMac()isLinux()有静态方法。

jnaplatform does this by string parsing System.getProperty("os.name"); jnaplatform通过字符串解析System.getProperty("os.name"); jnaplatform实现这一点System.getProperty("os.name"); , which seems pretty horrible to me, but if jnaplatform does it, I guess that should be good enough for me. ,这对我来说似乎很可怕,但是如果jnaplatform做到了,我想这对我来说应该足够了。

Results (ie how I used the above idea to solve the issue in the question) at https://github.com/abligh/jnasockopt - specifically https://github.com/abligh/jnasockopt/blob/master/src/org/jnasockopt/JNASockOptionDetails.java https://github.com/abligh/jnasockopt-尤其是https://github.com/abligh/jnasockopt/blob/master/src/org上的结果(即我如何使用上述想法解决问题) /jnasockopt/JNASockOptionDetails.java

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM