使用 IPC 在 Python 和 C# 之间进行通信的最简单方法?

问题描述 投票:0回答:3

我有一些 C# 代码需要调用 Python 脚本数千次,每次传递一个字符串,然后期望返回一个浮点数。 python 脚本可以使用任何版本的 Python 运行,所以我不能使用 Iron python。建议我使用 IPC 命名管道。我没有这方面的经验,并且无法弄清楚如何在 C# 和 Python 之间执行此操作。这是一个简单的过程,还是我在看大量的工作?这是解决我问题的最佳方法吗?

c# python ipc
3个回答
3
投票

使用zeromq.

  • 允许非常简单的消息传递而不需要代理
  • 适用于许多平台
  • 可以通过 TCP 套接字、unix 套接字或进程内部进行通信

这是我使用 zeromq 的答案https://stackoverflow.com/a/23227631/346478

它服务于不同 Python 程序之间的消息传递,但在其他平台之间可以使用完全相同的通信方法。只要保持传递的消息可互操作——双方都必须正确理解内容。您可以使用二进制数据,但通常情况下 json 表示也可以快速轻松地工作。但是有许多序列化框架,如结果缓冲区等,它们可能会帮助您对结果进行编码。


0
投票

根据您所说的,您可以连接到 python 进程并捕获标准输出文本。简单、快速、可靠!


0
投票

环境启动


为了实现这一点,首先,您需要在 C# 可执行文件的目录中设置 python 环境,以便拥有模块化应用程序并启用有效的进程间通信。这种方法提供的模块化方面是因为应用程序现在作为一个整体独立于操作系统,因为它有一个独立的 Python 虚拟环境,Python 应用程序可以在其中运行。这将使应用程序不依赖于主机已安装 Python 运行时的可能性。 要在 Linux 或 Windows 上创建虚拟环境,请输入:

python -m venv [ PATH WHERE THE ENVIRONMENT SHOULD BE CREATED ]
。 在Linux上,要创建Python虚拟环境,需要输入命令
sudo apt-get install python[python version]-venv
,才能下载具有创建Python虚拟环境功能的包,例如:
sudo apt-get install python3.11-venv



环境初始化完成后,进入Windows

python.exe
所在目录,或Linux
python
binary所在目录,创建或上传您需要运行的Python脚本文件。




进程间通信方式


带参数的脚本初始化

一种优雅而稳定的进程间通信方法是通过在脚本初始化时传递参数来调用脚本。如果您只需要将一些信息传递给脚本,并且不执行 C# 应用程序和 Python 应用程序之间的数据交换,则此方法是完美的。

[C#代码]

        static void Main(string[] args)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();  // <-----   Process object
            proc.StartInfo.WorkingDirectory = "C:\\Users\\Teodor Mihail\\source\\repos\\Inter_Process_Communication_Example_Python\\Inter_Process_Communication_Example_Python\\bin\\Debug\\Scripts";   // <----- Path where python executable is located
            proc.StartInfo.FileName = "python.exe";   // <-----  Executable name ( Windows ) or binary (Linux/MacOS)
            proc.StartInfo.Arguments = "main.py Param1 Param2";  // // <----- Python file to be executed by the Python executable and the command line arguments passed to the process
            proc.Start();  // <---- Start the process



            Console.ReadLine();
        }


[ Python 代码 ]

import sys


def main(param1, param2):
    print("Param1 =", str(param1))
    print("Param2=", str(param2))


# Get the first command line argument passed
# to the application and store it in a variable.
received_param1 = sys.argv[1]


# Get the second command line argument passed
# to the application and store it in a variable.
received_param2 = sys.argv[2]


# Call the "main" function and pass the two command 
# line arguments to the method as parameters
main(received_param1, received_param2)
input()


使用 stdinstdoutstderr

进行实时数据传输

Stdin、Stdout 和 Stderr 是操作系统内核用于接收输入、发送输出和发送与函数相关的错误消息的应用程序的主要 I/O 流。这些主要 I/O 流可用于进程间通信,这是通过将这些 I/O 流从子进程重定向到操作系统,再到父进程来完成的。

[C#代码]

        static void Main(string[] args)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process(); // <-----   Process object
            proc.StartInfo.WorkingDirectory = "C:\\Users\\Teodor Mihail\\source\\repos\\Inter_Process_Communication_Example_Python\\Inter_Process_Communication_Example_Python\\bin\\Debug\\Scripts"; // <----- Path where python executable is located
            proc.StartInfo.FileName = "python.exe";  // <-----  Executable name ( Windows ) or binary (Linux/MacOS)
            proc.StartInfo.Arguments = "main.py";  // <----- Python file to be executed by the Python executable
            proc.StartInfo.RedirectStandardInput = true;  // <----- Redirect the Stdin stream of the Python application through the C# application
            proc.StartInfo.RedirectStandardOutput = true;  // <----- Redirect the Stdout stream of the Python application through the C# application
            proc.StartInfo.RedirectStandardError = true;  // <----- Redirect the Stderr stream of the Python application through the C# application
            proc.StartInfo.UseShellExecute = false;  // <----- Do not use the OS shell to execute the application and use the C# application as the shell
            proc.Start();  // <---- Start the process


            // Read the output of the Python application on the Stdout stream
            char[] buffer = new char[1000];
            proc.StandardOutput.Read(buffer, 0, buffer.Length);
            Console.WriteLine(buffer);


            // Send a message to the Python application through the Stdin stream
            proc.StandardInput.WriteLine("Hello from C# application over STDIN");
            proc.StandardInput.FlushAsync();


            // Read the output of the Python application on the Stdout stream
            buffer = new char[1000];
            proc.StandardOutput.Read(buffer, 0, buffer.Length);
            Console.WriteLine(buffer);


            // Read the error message thrown by the Python application on the Stderr stream
            buffer = new char[1000];
            proc.StandardError.Read(buffer, 0, buffer.Length);
            Console.WriteLine(buffer);

            Console.ReadLine();
        }


[ Python 代码 ]

import sys

def main():

    # Send a message to the C# application on the Stdout stream
    sys.stdout.write("Hello from Python application over STDOUT")


    # Receive a message from the C# application on the
    # Stdin stream and store it inside a variable.
    received = input()


    # Send the message received from the C# application
    # back to the C# application through the Stdout stream
    sys.stdout.write(received)


    # Send an error message through the Stderr stream to the C# application
    raise Exception("\n\n\nHello from Python application over STDERR")


main()

来自 stdout 和 stderr 的数据必须按字符读取,否则流可能会锁定在等待响应状态。这是因为 stdout 和 stderr 不是异步 I/O 流,它们可能导致流锁定等待缓冲数据:https://devblogs.microsoft.com/oldnewthing/20110707-00/?p=10223

char[] buffer = new char[1000];
proc.StandardOutput.Read(buffer, 0, buffer.Length);


使用命名管道进行实时数据传输

管道是一种使用操作系统文件系统发送和接收信息或连接的套接字。这种类型的 IPC 是快速数据传输的理想选择,并且与 stdin、stdout 和 stderr 相比具有同时拥有多个运行连接的能力:https://www.baeldung.com/cs/pipes-vs-插座.

[C#代码]

        static void Main(string[] args)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process(); // <-----   Process object
            proc.StartInfo.WorkingDirectory = "C:\\Users\\Teodor Mihail\\source\\repos\\Inter_Process_Communication_Example_Python\\Inter_Process_Communication_Example_Python\\bin\\Debug\\Scripts"; // <----- Path where python executable is located
            proc.StartInfo.FileName = "python.exe";  // <-----  Executable name ( Windows ) or binary (Linux/MacOS)
            proc.StartInfo.Arguments = "main.py";  // <----- Python file to be executed by the Python executable
            proc.Start();  // <---- Start the process




            // Named pipe server object with an "Out" direction. This means that this pipe can only send messages
            System.IO.Pipes.NamedPipeServerStream connection1 = new System.IO.Pipes.NamedPipeServerStream("main_write_pipe", System.IO.Pipes.PipeDirection.Out);


            try
            {
                // Wait for a conection to e established on the pipe. This is a blocking method call, meaning that the thread will wait for this method to finish the execution
                connection1.WaitForConnection();



                // Byte buffer that stores the UTF8 encoded binary value of the string "Message from C# application over FIFO Pipe"
                byte[] buffer = Encoding.UTF8.GetBytes("Message from C# application over FIFO Pipe");



                // Write the binary buffer's contents on the pipe's I/O stream
                connection1.Write(buffer, 0, buffer.Length);



                // Flush the binary buffer's contents on the pipe's I/O stream
                connection1.Flush();
            }
            catch
            {

            }
            finally
            {
                if (connection1 != null)
                {
                    connection1.Dispose();
                }
            }




            // Named pipe server object with an "In" direction. This means that this pipe can only read messages
            System.IO.Pipes.NamedPipeServerStream connection2 = new System.IO.Pipes.NamedPipeServerStream("main_read_pipe", System.IO.Pipes.PipeDirection.In);


            try
            {
                // Wait for a conection to e established on the pipe. This is a blocking method call, meaning that the thread will wait for this method to finish the execution
                connection2.WaitForConnection();



                // Byte buffer that stores the UTF8 encoded binary value of the string "Message from Python application over FIFO Pipe"
                byte[] buffer = new byte[1024];
                connection2.Read(buffer, 0, buffer.Length);



                // Print the message
                Console.WriteLine(Encoding.UTF8.GetString(buffer));
            }
            catch
            {

            }
            finally
            {
                if (connection1 != null)
                {
                    connection1.Dispose();
                }
            }




            Console.ReadLine();

        }


[ Python 代码 ]

def main():
    # On Linux and MacOs the pipes created by C# are located in "/tmp" so you have to enter "/tmp/pipe_name"


    # Open the OS's file system pipe FIFO and specify that it has only "read" permissions.
    # This must be done because the "main_write_pipe" pipe server in C# is created with
    # write permissions and the receiver can only read from the stream.
    pipe_fifo1 = open(r"\\.\pipe\main_write_pipe", "r")


    # Read the content from the stream and store it in a variable.
    # Because the stream is buffered, the data will be received
    # after the pipe server instance in the C# application is
    # closed.
    received = pipe_fifo1.readline()


    # Open the OS's file system pipe FIFO and specify that it has only "write" permissions.
    # This must be done because the "main_read_pipe" pipe server in C# is created with
    # read permissions and the receiver can only write to the stream.
    pipe_fifo1 = open(r"\\.\pipe\main_read_pipe", "w")


    # Write the content to the pipe stream
    pipe_fifo1.write("Message from Python over FIFO Pipe")


    # Flush the stream to ensure that all the data within the
    # stream is flushed to the receiver.
    pipe_fifo1.flush()
    input()


main()


使用 TCP/UDP 套接字进行实时数据传输


套接字是一种二进制接收器/发送器,通过 IP 地址接收或发送二进制数据。进程间数据通信使用的IP地址是loopback地址(127.0.0.1),这是计算机自调用使用的地址。

[C#代码]

        static void Main(string[] args)
        {
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.StartInfo.WorkingDirectory = "C:\\Users\\Teodor Mihail\\source\\repos\\Inter_Process_Communication_Example_Python\\Inter_Process_Communication_Example_Python\\bin\\Debug\\Scripts";
            proc.StartInfo.FileName = "python.exe";
            proc.StartInfo.Arguments = "main.py";
            proc.Start();

            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 80));
            server.Listen(1);

            try
            {

                Socket client = server.Accept();
                try
                {
                    byte[] buffer = Encoding.UTF8.GetBytes("Message from Python application over TCP");

                    client.Receive(buffer, 0);

                    Console.WriteLine(Encoding.UTF8.GetString(buffer));

                    buffer = Encoding.UTF8.GetBytes("Message from C# application over TCP");

                    client.Send(buffer, 0);
                }
                catch
                {

                }
                finally
                {
                    if(client != null)
                    {
                        client.Dispose();
                    }
                }
            }
            catch
            {

            }
            finally
            {
                if(server != null)
                {
                    server.Dispose();
                }
            }


            Console.ReadLine();
        }

[ Python 代码 ]

import socket


def main():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(("127.0.0.1", 80))

    client.send("Message from Python application over TCP".encode("utf-8"))
    info = client.recv(len("Message from C# application over TCP".encode("utf-8")))
    print(info.decode("utf-8"))

    input()


main()
© www.soinside.com 2019 - 2024. All rights reserved.