在windows上从Java到C++的高效数据传输

Efficient data transfer from Java to C++ on windows

本文关键字:高效 数据传输 C++ windows 上从 Java      更新时间:2023-10-16

我希望将大量数据(高达~1 Gbit)从Java流式传输到C++应用程序(两者都在同一台机器上)。我目前在Linux上使用FIFO,但也需要Windows解决方案。

最跨平台的方法似乎是本地套接字,但是:a) 我不会从TCP校验和和复制到&来自内核空间,以及b) 普通用户的防火墙不会尝试检查并阻止连接吗?

似乎一个更安全的解决方案是使用JNI和命名管道API(\.\Pipe\blah),使连接的两侧都成为一个可怕的平台特定的混乱。

这真的是我的两个最佳选择吗(人们会推荐哪一个?)谢谢

您应该看看Google的Protocol Buffers,它同时支持C++和Java。

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

我不知道Java端存在哪些用于与共享内存接口的原语,但从C++端来看,访问共享内存中的数据比从套接字或命名管道中读取数据更有效。您必须实现自己的流控制和阻塞基元,但这些可能相当直接。

我会使用本地套接字,正如您所说,这是最跨平台的方法。

内核用户空间拷贝不应该是一个问题,因为除了共享内存之外,您可以选择的任何其他方法都需要这种拷贝。它在每个Unix系统上都可用,Windows也有自己的方式

要在Java中使用共享内存,唯一的方法是通过您自己的.DLL/.SO和JNI来访问它。

您最快的解决方案是内存映射共享内存段,并实现环形缓冲区或其他消息传递机制。在C++中,这是直接的,而在Java中,您有FileChannel.map方法,这使它成为可能。

下一种选择是使用这两个进程中的stdin/stdout。如果一个可以执行另一个,这可能会很快。

最后,正如您所指出的,您可以进行套接字IO。对于流式视频,这不是一个很好的选择,但如果您四处传递XML,与其他处理相比,开销将是最小的。

如果你对编写JNI很满意,可以考虑Boost.Interprocess。这将在Linux和Windows上为你提供可移植的共享内存。请记住,不存在读取/写入共享内存的内核往返。

如果它是"一个"函数调用中的一大块数据,我建议使用JNI。

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

文章中的片段,它将数据从c++转移到java,相反也很容易做到:

总之,用Java共享C中的二进制数据(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 copyn");
jbyteArray rawDataCopy = env->NewByteArray(dataSize);
env->SetByteArrayRegion(rawDataCopy, 0, dataSize, rawData);

并像这样将其传递给Java:

printf("Finding callback methodn");
//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 callbackn");
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

如果TCP的比特率太高,我会使用带有否定确认UDP的本地套接字(尽管我会先尝试TCP并确认它有问题)。如果您在同一台机器上进行流传输,则丢弃的数据包应该最少(如果有的话),但添加一个否定确认层可以解决这种情况。

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

如果这不合适,那么Sockets是您的最佳选择。

如果您的C++进程启动Java进程,它可能会从继承的Channel中受益。此外,如果Java进程正在使用一个文件,我建议您探索transferTo和transferFrom方法。在进行文件到文件IO时,这些操作避免了用户和内核空间之间不必要的来回切换;如果您使用一个特殊的套接字通道,同样的优化可能会起作用。

我建议使用UDP"连接",它可以确认收到的每一个无故障的第N个数据包,并请求重新传输它将错过的几个数据包。

我建议不要使用JNI,因为它很难调试。如果C++代码Segfault或抛出一个未捕获的异常,那么JVM将崩溃,您将不知道原因。