在Windows上高效地从Java传输数据到C++

7
我想要从Java向C++应用程序(位于同一台机器上)流传大量数据(最高可达1 Gbit)。我目前在Linux上使用FIFO,但也需要一个Windows解决方案。
最具跨平台性的方法似乎是使用本地套接字,但是: a) 我会不会因为TCP校验和复制到/从内核空间而产生巨大开销? b) 普通用户的防火墙会不会尝试检查并可能阻止连接?
似乎更安全的解决方案是使用JNI和命名管道API (\.\pipe\blah),这将使连接的双方都变成一个非常混乱的特定于平台的状况。
这真的是我的两个最佳选择吗?哪个更受人们推荐呢? 谢谢!
11个回答

7
你应该看一下 Google 的 Protocol Buffers,它支持 C++ 和 Java 两种语言。

4
Protobuf 定义传输的内容,而不是传输方式。这个问题涉及到传输方式(HOW)。 - Bruce
1
Protobuff有自己的数据开销,关于如何高效传输没有解决方案(正如@Bruce所提到的)。使用共享内存。 - masoud

5

命名管道比TCP更高效,但只使用共享内存块呢?

我不知道Java端与共享内存交互的原语是什么,但从C++端访问共享内存中的数据比从套接字或命名管道中读取数据更有效。您需要实现自己的流控制和阻塞原语,但这些可能相当简单。


4
我会使用本地套接字,正如您所说,这是最具跨平台性的方法。
内核-用户空间拷贝不应该成为问题,因为除了共享内存之外,您选择的任何其他方法都需要这种拷贝。共享内存在每个Unix系统上都可用,Windows也有其自己的方法来实现
要在Java中使用共享内存,唯一的方法是通过自己的.DLL/.SO实现,并使用JNI进行访问。

2
您最快的解决方案将是内存映射共享内存段,然后实现环形缓冲区或其他消息传递机制。在C++中,这很简单,而在Java中,您可以使用FileChannel.map方法实现。
下一个选择是使用两个进程的stdin/stdout。如果一个进程能够执行另一个进程,则这种方法可以非常快速。
最后,正如您已经注意到的,您可以进行套接字IO。对于流视频来说,这不是一个很好的选择,但如果您传递XML数据,与其他处理相比,开销将是最小的。

1

如果是在“一个”函数调用中有大量数据,我建议使用JNI。

看一下这个:通过jni接口共享输出流

文章中的片段,它将数据从C++传输到Java,反之亦然:

总的来说,从C向Java共享二进制数据(A/V文件、图像等)的一般策略需要使用字节数组。你可以像这样在C中创建一个Java字节数组:

const char[] rawData = {0,1,2,3,4,5,6,7,8,9}; //Or get some raw data from somewhere
int dataSize = sizeof(rawData);
printf("Building raw data array copy\n");
jbyteArray rawDataCopy = env->NewByteArray(dataSize);
env->SetByteArrayRegion(rawDataCopy, 0, dataSize, rawData);

然后像这样将其传递给Java:

printf("Finding callback method\n");
//Assumes obj is the Java instance that will receive the raw data via callback
jmethodID aMethodId = env->GetMethodID(env->GetObjectClass(obj),"handleData","([B)V");
if(0==aMethodId) throw MyRuntimeException("Method not found error");
printf("Invoking the callback\n");
env->CallVoidMethod(obj,aMethodId, &rawDataCopy);

你会拥有一个类似于这样的Java对象:

public class MyDataHandler {
  OutputStream dataStream;
  public MyDataHandler(OutputStream writeTo) { dataStream = writeTo;}
  public void handleData(byte[] incomingData) { dataStream.write(incomingData); }
}

这个处理程序将通过本地方法传递给C,如下所示:

public class NativeIntegration {
  public native void generateBinaryWithHandler(MyDataHandler handler);

  //Here we assume response is something like a network stream
  public void doCallNativeFunction(ResponseStream response) {
    MyDataHandler handler = new MyDataHandler(response);
    generateBinaryWithHandler(handler);
  }
}

此外,您还可以使用其他技术: CORBA、ASN.1(ASN.1 工具)、UDP 或 TCP


1
如果您喜欢编写JNI,请考虑使用Boost.Interprocess。这将为您提供在Linux和Windows上的可移植共享内存。请记住,读/写共享内存没有内核往返。

0
如果比特率对于TCP来说太高的话(尽管我会先尝试TCP并确认它是否存在问题),我会使用带有负面确认UDP的本地套接字。如果您在同一台机器上进行流媒体传输,则应最小化或完全避免数据包丢失,但添加负面确认层将为您处理这种情况。

0

使用System.out和System.in怎么样?

如果不适合,那么Sockets是你最好的选择。


0

如果您的C++进程启动了Java进程,则可以从inheritedChannel中受益。此外,如果Java进程正在使用文件,则建议探索transferTotransferFrom方法。在进行文件到文件IO时,这些方法避免了在用户空间和内核空间之间来回不必要地跳转。如果您在使用特殊的套接字通道,则可能会出现相同的优化。


0
我建议使用UDP“连接”,它会确认每个成功接收的第N个数据包,并请求重传少量丢失的数据包。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接