使用JNI / C ++代码中的java套接字

我有一个java应用程序,它创建一个与服务器进程通信的套接字,例如new java.net.Socket(String host,int port)。 这个应用程序包含一堆遗留的c ++代码,需要从该服务器吸取大量数据并对其进行处理。 这是通过让本机代码创建自己的套接字并连接到服务器来实现的,例如:

sock = socket(AF_INET, SOCK_STREAM, 0); struct hostent* hp = gethostbyname(host); if (!hp) { unsigned long addr = inet_addr(host); hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); } struct sockaddr_in name; name.sin_family = AF_INET; memcpy(&name.sin_addr, hp->h_addr, hp->h_length); name.sin_port = htons(port); connect(sock, (sockaddr*)&name, sizeof(name)); 

在具有多个NIC(例如有线和wifi或VPN连接)的Windows vista / 7计算机上,这两个套接字最终可能会有不同的本地地址。 java代码似乎选择了“更好”的界面(有线Gb enet =更高的MTU?),原生(天真?)代码获得“默认”界面(坚持使用usb wifi设备,它成为你的默认设置 – yuck) 。

这给我带来了一些问题,我不认为细节是相关的。 两个问题:

  1. 我可以从JNI代码中重用java套接字(可移植吗?假设是Sun JDK)。 这将完全避免这个问题,但到目前为止,我没有看到任何方式与JNI /本机代码中的java.net.Socket进行交互。

  2. 由于第一个问题的答案可能是NO,java如何创建该套接字(选择接口)? 代码片段受到欢迎。 我在openjdk的东西环顾四周,但没有找到我要找的东西。

谢谢,克里斯

回答你的第一个问题:如果可以在本机代码中重用Java的套接字 – 是的,这是可能的,但我不推荐它(你会把自己绑在特定实现/版本的内部); 但如果你真的必须:使用reflection来访问java.net.SocketImpl上的java.io.FileDescriptor,那么使用sun.misc。 JavaIOFileDescriptorAccess的获取本机套接字描述符的get方法。 Checkout DualStackPlainSocketImpl.java )

回答你的第二个问题:在Windows上找到默认界面的Java算法是什么 – 在net_util_md.c中检查getDefaultIPv6Interface方法(不要让v6欺骗你 – 我相信它也用于v4)。

我建议您打开并使用C(JNI)代码或Java代码(最好是后者)中的套接字,因为您会发现清理和error handling最好在管理套接字的代码中处理。 在Java中打开套接字并从C(JNI)传递字节缓冲区的想法是完全正确的,你不应该在合理的缓冲区大小和JNI代码中正确的解除分配中发现堆的任何问题。

想想处理大量数据的Java应用程序服务器。

对于第一个问题,如果在JNI代码中有一个java.net.Socket对象引用,则可以在其上调用方法,这样就可以通过套接字读取和写入数据。

谨防在JVM实现细节中徘徊的解决方案,它们将来可能会破坏或与不同的供应商VM一起破解。 有一种方法可以使用java.nio API进行移植。 有一些方法可以从本机代码与通道进行通信,而无需将缓冲区复制到java堆中。

基本思路是在java代码中创建一个java.nio.SocketChannel来打开连接。 然后在C ++中使用NewDirectByteBuffer创建一个java.nio.ByteBuffer实例,该实例可以传递给通道实例的read / write方法。

有关详细信息,请参阅Java 2 SDK和新I / O API 1.4版中引入的JNI增强function 。

你的第二个问题 –

你总是可以“绑定”到你想要的本地接口(只需要它的IP地址)

public void bind(SocketAddress addr)将此DatagramSocket的SocketException绑定抛出到特定的地址和端口

我想不出为什么Java会选择比更好的本地接口而不是选择代码的原因。 它所做的只是调用本机代码,与您自己的代码非常相似。 您可能会看到依赖于顺序而非依赖于Java的内容。