为 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_SNDBUF
、SO_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()
有静态方法。
我正在 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_SNDBUF
、SO_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()
有静态方法。