【发布时间】:2015-12-24 00:40:23
【问题描述】:
我有一个分成两部分的 Linux 程序。
其中一部分进行 NAT 遍历以获得 UDP 套接字(UDP 打孔)或 TCP 套接字(TCP 打孔)。第一部分是用 C 编写的,以允许促进或增强 NAT 遍历过程的本机功能。第二部分实际上使用了通过第一部分执行的 NAT 遍历获得的连接套接字。
现在问题来了。我希望第一部分,即获取套接字的部分,独立于第二部分,即将套接字用于特定应用目的的部分。例如,我希望第一部分可重用于各种不同的应用程序,这些应用程序都需要在对等点之间建立的 UDP 和 TCP 连接。
现在,我希望第二部分(应用程序部分)用 Java 而不是 C 或 C++ 编写。我希望第二部分使用由负责 NAT 遍历的 C 代码获得的套接字连接。假设第一部分建立了一个连接,然后返回一个结构:
// Represents a TCP or UDP connection that was obtained in part one.
struct ConnectionObtained {
int socket_file_descriptor;
int source_port;
int destination_port;
int source_address; // 4 byte ipv4 address
int destination_address;
int is_UDP; // 1 for UDP client socket, 0 for TCP client socket
};
第一部分中的 C 代码可以通过 JNI(Java 本地接口)或通过进程间通信将这个 POD/结构提供给第二部分中的 Java 代码。
我希望 Java 代码使用该信息来构造一个声明类型为 java.net.DatagramSocket 或 java.net.Socket 的对象,然后在需要 DatagramSocket 或 Socket 的任何地方使用该对象。
作为起点,请考虑以下示例代码...
/**
* Determines the Unix file descriptor number of the given {@link ServerSocket}.
*/
private int getUnixFileDescriptor(ServerSocket ss) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Field $impl=ss.getClass().getDeclaredField("impl");
$impl.setAccessible(true);
SocketImpl socketImpl=(SocketImpl)$impl.get(ss);
Method $getFileDescriptor=SocketImpl.class.getDeclaredMethod("getFileDescriptor");
$getFileDescriptor.setAccessible(true);
FileDescriptor fd=(FileDescriptor)$getFileDescriptor.invoke(socketImpl);
Field $fd=fd.getClass().getDeclaredField("fd");
$fd.setAccessible(true);
return (Integer)$fd.get(fd);
}
代码看起来可以“在给定的文件描述符上重新创建绑定的 {@link ServerSocket}”。这是否意味着也可以“在给定的文件描述符上重新创建绑定的 {@link java.net.Socket}”?绑定的 {@link java.net.DatagramSocket} 怎么样?
/**
* Recreates a bound {@link ServerSocket} on the given file descriptor.
*/
private ServerSocket recreateServerSocket(int fdn) throws Exception {
FileDescriptor fd=new FileDescriptor();
Field $fd=FileDescriptor.class.getDeclaredField("fd");
$fd.setAccessible(true);
$fd.set(fd,fdn);
Class $PlainSocketImpl=Class.forName("java.net.PlainSocketImpl");
Constructor $init=$PlainSocketImpl.getDeclaredConstructor(FileDescriptor.class);
$init.setAccessible(true);
SocketImpl socketImpl=(SocketImpl)$init.newInstance(fd);
ServerSocket ss=new ServerSocket();
ss.bind(new InetSocketAddress(0));
Field $impl=ServerSocket.class.getDeclaredField("impl");
$impl.setAccessible(true);
$impl.set(ss,socketImpl);
return ss;
}
【问题讨论】:
-
如果Java代码只需要关心连接的套接字,为什么你的Java代码在
ServerSocket?上满是花样 -
如果我理解正确,你想使用现有打开的套接字作为新 Java 客户端 Socket 的底层套接字吗?
-
@perencie - 是的。如果我想使用现有打开的 C 套接字作为新 Java 客户端 Socket 的底层套接字?
-
@EJP - 因为这些是我能找到的从文件描述符获取和创建 Java 套接字的唯一示例。我认为可以将“recreateServerSocket(int fdn)”更改为“recreateUDPSocket(int fdn)”或“recreateClientSocket(int fdn)”,然后使用该代码从一个 C 文件描述符创建 UDP 或 TCP 套接字已经打开了 C 套接字。