我正在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_SNDBUF
,SO_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() {
}
}
答案 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