【问题标题】:In Java, how do you obtain a Socket or DatagramSocket from the file descriptor of an already opened C socket?在 Java 中,如何从已打开的 C 套接字的文件描述符中获取 Socket 或 DatagramSocket?
【发布时间】: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 套接字。

标签: java c sockets tcp udp


【解决方案1】:

您在问两个不同的问题。你能从一个单独的进程中编写的 C 代码传递一个绑定的套接字,你能从同一个进程中编写的 C 代码传递一个绑定的套接字吗?

对于第一部分,不,如果 C 代码在一个应用程序中而 Java 代码在另一个应用程序中是不可能的,因为如果可能的话,多个不同的应用程序将能够绕过一个套接字(没有 SCM_RIGHTS)。杀死最初创建和绑定套接字的应用程序会给使用/共享该套接字的其他应用程序带来问题。

至于让 C 代码位于 Java 应用程序的本机部分(即通过 jni),在这种情况下,操作系统将无法区分套接字是位于用户代码的 Java 部分还是C部分,所以你不会遇到上一段介绍的问题。可以在 Java 和本机代码之间传递一个套接字(文件描述符 int 和本机套接字描述符)(请参阅link),但这并不能告诉您在这种情况下它是否实用。

至于从来自 jni 代码的绑定套接字文件描述符制作 java.net.Socket 或 java.net.DatagramSocket,我不知道。您必须自己尝试一下。

【讨论】:

  • 您不必像第三个人一样回答您的问题。可能被错误地解释为 DID 或有点程序员精神分裂症。我必须承认,这让我很困惑。
  • 哈哈哈。我实际上经常自言自语,哈哈。但说真的,我认为如果那里已经有一个不好/不完整/无功能的答案,那么有人更有可能发布(好的/完整/功能性)答案。
【解决方案2】:

您可以在进程之间传输文件描述符(至少在 POSIX 系统上)accroding Can I share a file descriptor to another process on linux or are they local to the process?

正如 cmets 中提到的(感谢@Andrew Henle),您可以通过反射破解 Java 并为现有文件描述符创建 IO 流:Can I get a Java Socket from a file descriptor number? 还可以扩展 Socket 类并覆盖 getInputStream/getOutputStream 方法。

但实际上我建议您使用第一部分作为代理。 Java 部分只是在 localhost 上打开服务器套接字,然后 C++ 部分在 localhost 和 WAN 地址之间重新传输流量。

【讨论】:

  • “但实际上我建议你使用第一部分作为代理”。最后一句语法不正确。将 TCP 服务器套接字与 Java 绑定并将本机套接字传递给 C++ 后,如何将生成的客户端套接字返回给 Java?
  • 你不应该传输套接字,你应该传输流量:客户端 C++ 服务器 Java 服务器( - 套接字连接)
猜你喜欢
  • 1970-01-01
  • 2011-02-14
  • 2016-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
相关资源
最近更新 更多