C ++和Java之间的低延迟IPC

时间:2011-05-30 10:37:55

标签: java c++ sockets java-native-interface ipc

在以下情况下实施C ++ / Java IPC的最佳方法是什么?

(最近有人询问了similar question,但我的要求更具体)

  1. 我有两个程序 - 一个用C ++编写,另一个用Java编写 - 需要相互通信。两者都在同一台机器上运行。

  2. 程序互相发送消息。消息通常很短(少于几百字节),但可能大小为100KB或更多。

  3. 不需要确认消息(即,不是HTTP之类的请求/响应模型)。例如,C ++程序向Java程序发送消息,Java程序可以通过稍后向C ++程序发送消息进行回复 - 反之亦然。

  4. 理想的解决方案是:a)非常低的延迟,b)没有安全麻烦(用户不必授权打开端口等),c)将与平台无关。

    < / LI>

    我的第一个想法是使用套接字 - 每个程序都将充当另一个程序的服务器。套接字比其他形式的IPC有更多的开销,如果我让系统自动分配端口号,我不知道服务器如何通知客户端端口号。我还考虑过命名管道,但不同平台不支持它们(至少不一致)。 JNI 看起来像一个选项,但是它可以跨越流程边界吗?

    有什么建议吗?

    谢谢!

    后续问题

    1. 如果我使用套接字,我是否需要打开两个套接字以允许如上所述的异步通信?

6 个答案:

答案 0 :(得分:9)

我建议您使用 TCP套接字

根据我的经验,TCP套接字的实际开销与应用程序的其他任务的工作负载相比非常低,至少是我用来开发的。我的意思是,有时即使套接字的延迟是其他IPC机制的延迟的两倍,在整个工作流程中它们的影响也很小。它可以节省您在Java应用程序和C ++之间进行IPC的麻烦,这最终将要求您使用使用JNI的特定Java库,以及JNI和库本身的开销。

在我的Java应用程序中,我实际上已经测量过垃圾收集器的影响比“ loopback ”TCP套接字引起的延迟重要得多。

此外,TCP套接字比传统IPC更具可扩展性(可移植性!)。如果将来你必须在不同的机器上运行客户端和服务器怎么办?在“TCP套接字”场景中,你将不得不做5分钟的黑客攻击,在“传统的IPC”场景中,你将不得不重写整个IPC的东西。

但是,您的应用程序的一般工作流程是什么?

即使不需要确认,我也建议使用TCP(而不是UDP)来避免未经分类的传送(当重新排列收到的内容时会导致痛苦 - 你的一些消息是100KB,这不适合UDP数据包)。

在回答您的上一个问题时,要让服务器通知客户端端口,您可以让服务器使用特定的“端口”命令行参数启动客户端,或者让服务器在/下保存一个小文件。 tmp(或其他临时目录),其中写有端口号。

答案 1 :(得分:6)

我已经听说ZeroMQ有关这类情景的好消息。在某些情况下,它甚至比TCP更快。简而言之,尝试它肯定不会受到伤害。

答案 2 :(得分:4)

另一种方法是使用内存映射文件并通过检查编译器设置保持可移植性(如果您是posix或不是posix)。 POSIX OS有mmap(),在Windows中你可以使用CreateFileMapping()

在boost库中是C ++的可移植实现,在java中你应该可以使用FileChannel()

此页面很好地解释了如何将其用于IPC http://en.wikipedia.org/wiki/Memory-mapped_file

答案 3 :(得分:3)

当您说延迟非常低时,您需要符合条件。您可以通过Socket环路发送消息,RTT为20微秒。如果这足够快,我会这样做。

如果这还不够快,我只需将C ++放在Java应用程序中并通过JNI调用它。这将为您提供大约30纳秒的RTT。

使用内存映射数据的问题是获得联锁权。您可能会找到适用于一个系统的解决方案,但可能无法在其他系统上运行。

答案 4 :(得分:2)

使用JNI将允许访问系统中的所有可能性,而不仅仅是Java中直接支持的那些可能性;例如,如果你使用共享内存,那将是必要的。然而,JNI本身相当昂贵。

延迟问题很棘手,因为我所知道的机制都没有给出任何保证。总而言之,最快的可能是某种形式的共享内存,使用信号在数据存在时唤醒其他进程。这需要在Java端使用JNI,但是如果正确完成,可能仍会提供最低延迟 - 正确地执行它(确保没有消息丢失)可能远非微不足道。基于Unix的平台确实支持排队信号,并将它们作为事件处理在一个单独的线程中;我不了解Windows。

除此之外,命名管道通常非常有效;延迟可以和共享内存一样好,但是需要更多时间来获取数据(因为它必须通过系统复制)。并且应该可以直接从Java访问它,而无需使用JNI。在Unix下,也可以配置套接字以便快速响应(事实上,这就是命名管道的内幕);我不知道Java接口是否支持这些配置选项,但是,我不知道它们是否在Windows下可用。

答案 5 :(得分:1)

另一种方法是使用嵌入式数据库(因为你正在考虑使用多个IPC,我认为两个应用程序都在同一台机器上)。

我之前在c ++应用程序从各种通道中获取数据并将其放入DB(内存数据库; TimesTen)中的应用程序。为了向用户显示该数据,Java应用程序将从数据库中查询它。

供您使用,我不知道您是否愿意考虑使用Oracle的Timesten,但您也可以使用Berkeley的嵌入式数据库。