Python和Java之间的进程间通信(IPC)

19

首先,让我简要解释一下为什么我会提出这个问题: 我正在编写一个Python程序(带有wxPython图形用户界面),需要从Python调用Java AWT程序并从中提取数据。在Windows上,我有一个进程内的工作解决方案。在OSX上,只要我运行Java应用程序无头模式,就可以有一个进程内的解决方案。不幸的是,我没有找到任何合理的解决方案可以在OSX上在同一个进程中运行两个GUI,因为AWT和WX都想要第一个线程,并且不能共享wx消息循环。

我想要做的是,在我的Python程序中启动一个独立的Java程序,并建立一个管道或队列或其他传递数据(特别是字节数组)的方法。

我非常感谢任何建议,甚至是对正确方向的指引,因为我在IPC方面几乎没有经验。


你能在不启动GUI的情况下加载Java类吗? - Ignacio Vazquez-Abrams
是的,我使用JNI编写了一个包装器。 - Adam Fraser
7个回答

5
我试图使用管道编写解决方案,但似乎它们并不适合用于发送带有潜在大数据的多个消息。相反,它们似乎非常适合打开一个“工作程序”运行、响应和退出。
在研究套接字编程时,我在这里找到了一个很棒的资源:https://web.archive.org/web/20080913064702/http://www.prasannatech.net/2008/07/socket-programming-tutorial.html 该教程介绍了四种语言编写的简单聊天程序的TCP和UDP变体。最终,我使用并修改了TCP Java客户端和Python服务器。

4

1
这很棒。虽然它在很大程度上依赖于 O-O 模型,但对于像 C 这样的语言可能不太可能。但它回答了这个问题 + 使用套接字发送消息。 - ArmenB

3

使用Python中的subprocess实现IPC

这里有一个IPC.java文件,其中Java代码将接收数字并发送它的平方。

import java.util.Scanner;

public class IPC {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String data="";
        while(scanner.hasNext()){
            // Receive data from Python code 
            data = scanner.nextLine();

            // Process data (calculate square)
            int x = Integer.parseInt(data);
            int square = x*x;


            // Send data to python code
            System.out.println(square);
        }
        scanner.close();
    }
}

IPC.py文件
import subprocess
subprocess.run(["javac", "IPC.java"])
proc = subprocess.Popen(["java", "IPC"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
for i in range(10):
    # Send to java code
    proc.stdin.write(b'%d\n' % i)
    proc.stdin.flush()
    proc.stdout.flush()

    # Receive data from java code
    output = proc.stdout.readline()
    print (output.rstrip())
proc.communicate()

3

嗯,从Python的角度来看,这似乎很容易,但根据我所读的,Java只能将FIFOs视为文件并且无法以与操作系统无关的方式创建它们。这对于将图像发送到Java应该没问题,但我不确定是否足以让它们返回。 - Adam Fraser
FIFO是平台相关的,实际上在UNIX中通过文件接口使用(http://en.wikipedia.org/wiki/Named_pipe)。如果您对此感到不舒服(或者更喜欢由标准库支持的东西),您可以使用套接字。 - Jeremy Brown

2
使用subprocess.Popen启动Java进程并建立管道以与其通信。为了以语言中立、平台中立、可扩展的方式高效地序列化和反序列化数据,请查看Protocol Buffers(由Jon Skeet贡献!)。

0
我曾经遇到过类似的情况,需要在Java进程和Linux进程之间进行通信。我使用了命名管道。
尝试在Python中实现mkfifo()。

它只在Linux和MacOS上能工作,无法在Windows上使用。 - teodor mihail

0

Python和Java之间的进程间通信



在Python应用程序中实现一个自包含、系统无关的JRE。
为了使Python应用程序独立于整个系统,必须在Python应用程序内部创建一个JRE。如果希望应用程序不依赖于系统,并且所有内容都包含在应用程序中,则必须执行此操作。如果整个应用程序必须准备好在任何用户的计算机上运行而无需进行任何其他资源和安装程序,则这非常有帮助。
前往https://learn.microsoft.com/en-us/java/openjdk/download,下载所需硬件架构和操作系统的JDK,并将其加载到Python应用程序的环境中。然后将Java应用程序项目作为整体或从Java应用程序制作的.jar文件放置在Python应用程序的目录中。

enter image description here

您可以在 Python 应用程序中启动 Java 应用程序作为子进程。

进程间通信方法


使用 stdinstoutstderr 进行实时进程间通信



stdinstdoutstderr 流是操作系统内核用于应用程序和进程的主要 I/O 系统。通过修改这些流从子进程到操作系统,再到子进程到父进程的流动,这些流将被绕过父进程,并使父进程能够直接与子进程执行输入和输出操作。


[ Java 代码 ]

public class Main {

    public static void main(String[] args)
    {
        // Send input over the "stdout" stream to the Python parent process
        System.out.println("Message from Java application");
        System.out.flush();

        // Read input over the "stdin" stream from the Python parent process
        Scanner s = new Scanner(System.in);
        String input = s.nextLine();
        s.close();

        // Send input over the "stdout" stream to the Python parent process
        System.out.println(input);
        System.out.flush();
    }
}

[ Python 代码 ]

import subprocess

# Path to java.exe, in case the application contains a self-contained java environment, else just the "java" command
path_to_java_exe = "C:\\Users\\Teodor Mihail\\PycharmProjects\\Python_IPC\\jre\\bin\\java.exe"

# Command line argument that specifies the compiler what it should compile, in this case is a ".jar" file
java_exe_arguments = "-jar"

# The path to the file that the compiler will run and the runtime will execute, in this case it is the whole program compiled as a ".jar" file
java_jar_file_path = "C:\\Users\\Teodor Mihail\\PycharmProjects\\Python_IPC\\Java_I.P.C.jar"

# Global variable that holds the sub-process
process = None




def subprocess_startup():
    # Specify that the "process" variable is a reference to the global variable "process"
    global process

    # Open the process and redirect the "stdin", "stdout", and "stderr" streams from the sub-process to the OS to the Python process
    process = subprocess.Popen([path_to_java_exe, java_exe_arguments, java_jar_file_path], stdin=subprocess.PIPE,
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE)


def subprocess_read_stdout():
    # Read the output sent by the Java application sub-process over the "stdout" stream
    received = process.stdout.readline()
    print(received)


def subprocess_write_stdin(data):
    # Write input to the Java application over the "stdin" stream
    process.stdin.write(data.encode("utf-8"))
    process.stdin.flush()


def main():
    subprocess_startup()

    # A loop can be implemented here for continuous read and write operations.
    # Both the read and write operation methods can be open on different threads
    # to perform continuous read and write operations executed in parallel


    subprocess_read_stdout()

    # Data to be written on the stdin stream must end in "\n" otherwise the Java
    # Scanner will lock. This will happen because the Scanner in Java is
    # scanning until a "\n" is found, otherwise it will wait indefinitely
    # until it will find a "\n" within the input stream.
    subprocess_write_stdin("Message from Python application\n")



    subprocess_read_stdout()


if __name__ == '__main__':
    main()


在每次调用process.stdin.write方法时,发送到子进程的数据必须以换行符("\n")结尾。这是因为Java扫描器将扫描stdin流以查找换行符,并且由于流被缓冲,这将导致在执行此操作的线程上出现锁定。


1
请问您能否裁剪您的截图,以去除所有不相关的巨大浪费空间? - starball

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