94237


現在個人的に使用する言語をJavaからPythonに置き換えてるのですが、Javaで書いた大きなプログラムもあるため一部をPythonで書き、そこで取得した文字列を昔書いたJavaのプログラムに送り処理したいと考えています。

**追記：ローカルで動かしているプログラム間です**

Pythonで取得した文字列を逐次的にJavaプログラムに送る方法、あるいはもっと一般的にアプリ間でデータを送受信する方法は無いでしょうか？  
文字列をファイルに書き込みそれを読み取るという方法も考えましたが、ラグが生じ、またHDD/SSDへの頻繁な読み書きが必要なため直接データの送受信したいと思います。

具体的にやりたい事は以下の通りなのでこれが解決できる手段が他にあればそれでも構いません：

  1. [MisskeyボットをPythonで作る - Qiita](https://qiita.com/CyberRex/items/e49828dba8e5867d8b26)に書かれてる方法でMisskeyのグローバルタイムラインを取得する
  2. 取得したノートをJavaで書いたコードで処理する

以上、よろしくお願いします。


CC BY-SA 4.0
2023-03-18T09:21:03.047
94236
2023-04-12T09:59:30.313
2023-03-18T10:53:33.653
816
816
question
python java
Pythonで取得した文字列をJavaで作成したアプリに送る方法（あるいは一般的にアプリ間でデータを送受信する方法）


開発言語やOSの異なるプログラム間での通信を行う場合、TCP/IP(socket)を使うのがよいと思います。

**処理の流れ**

  1. あらかじめJava側のTCP/IP(socket)サーバを立ち上げておく
  2. Python側からJava側へ、なんらかの処理依頼を送信する
  3. Java側で依頼されたなんらかの処理を実行し、処理結果をPython側へ送信する
  4. Python側で処理結果を受信する

**Java側のコード例**

[code]

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * ソケット通信(サーバー側)
     */
    class SocketServer {
    
        public static void main(String[] args) {
    
            ServerSocket sSocket = null;
            Socket socket = null;
            BufferedReader reader = null;
            PrintWriter writer = null;
            final String SERVER_IP = "127.0.0.1";
            final int SERVER_PORT = 10000;
            try {
                sSocket = new ServerSocket();
                sSocket.bind(new InetSocketAddress(SERVER_IP, SERVER_PORT));
                System.out.println("クライアントからの接続待ち");
                socket = sSocket.accept();
                System.out.println("クライアントからの接続完了");
                //クライアントからのデータ受信用
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                //クライアントへの応答送信用
                writer = new PrintWriter(socket.getOutputStream(), true);
    
                String line = null;
                System.out.println("クライアントからの入力データ待ち");
                while ((line = reader.readLine()) != null) {
                    System.out.println("クライアントからの入力データ:" + line);
    
                    //クライアントからの入力データに応じたJava側の処理
                    System.out.println("クライアントからの入力データに応じたJava側の処理を実行");
                    //処理結果をサーバへ送信
                    writer.println("Java側の処理結果" + line.charAt(0));
                    System.out.println("Java側の処理結果をクライアントへ送信");
                }
    
                try {
                    reader.close();
                    writer.close();
                    socket.close();
                    sSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
[/code]

**Python側のコード例**

[code]

    # coding: utf-8
    # ソケット通信(クライアント)
    import socket
    
    if __name__ == '__main__':
        SERVER_IP = '127.0.0.1'
        SERVER_PORT = 10000
        server = (SERVER_IP, SERVER_PORT)
    
        socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        socket.connect(server)
    
        count = 0
        while True:
            line = str(count) + ': Python側からJava側への送信データ\n'
            # サーバに送信
            socket.send(line.encode("UTF-8"))
            print('サーバーへ処理依頼: '+str(count))
    
            # サーバからの処理結果受信
            result = socket.recv(4096).decode()
            print('サーバーからの処理結果: ' + str(result))
    
            count = count+1
            time.sleep(200)
    
    
        socket.close()
    
[/code]

**実行結果(Java側)**

[code]

    クライアントからの接続待ち
    クライアントからの接続完了
    クライアントからの入力データ待ち
    クライアントからの入力データ:0: Python側からJava側への送信データ
    クライアントからの入力データに応じたJava側の処理を実行
    Java側の処理結果をクライアントへ送信
    クライアントからの入力データ:1: Python側からJava側への送信データ
    クライアントからの入力データに応じたJava側の処理を実行
    Java側の処理結果をクライアントへ送信
    
[/code]

**実行結果(Python側)**

[code]

    サーバーへ処理依頼: 0
    サーバーからの処理結果: Java側の処理結果0
    
    サーバーへ処理依頼: 1
    サーバーからの処理結果: Java側の処理結果1
    
[/code]

**（質問者による）注意**  
コード例はPython側から永遠にデータを送ってますが、必要があればPython側とJava側で終了の依頼を取り決めをして、処理を繰り返してください。

**動作確認に使用したバージョン**  
Python 3.10.5  
openjdk 18 2022-03-22


CC BY-SA 4.0
2023-03-18T14:07:14.330
94237
2023-04-12T09:59:30.313
2023-04-12T09:59:30.313
816
35558
94236
answer
  
  
PythonからJavaで書かれた既にあるプログラムを利用したいと言うことで合ってますでしょうか？手段としては、大きく二つに分かれます。

## 1 PythonからJavaのクラスを利用する。

通常、ある言語から別の言語で書かれたプログラムを直接利用することはできません。しかし、組合せと条件によっては、そのままそのコードを利用することも可能です。

### 1-1 JVM上で実行出来るPython実装を使用する。(Jython)

Pythonの公式実装は主にCで書かれており、コンパイラされたネイティブコードのインタプリンタ－になっています(他の実装と区別する場合はCPythonと言われます)。それとは別に、JVM上で実行可能なPython実装として、[Jython](https://www.jython.org/)があります。

JavaのプログラムがJVM上で動作するのは周知のことかと思いますが、同じようにPythonもJVMで実行されれば、そのままJavaのプログラムを利用可能です(ScalaやKotlinがJavaのプログラムを利用できること同じ)。JythonでもJavaのクラスを直接呼び出せる手段が用意されており、Pythonのコードに組み込むことが可能になっています。

ただ、一つ問題があります。現在のJythonの最新安定バージョンはPython2.7系互換でであり、Python2.7のコードしか動きません。Python3系互換のJypthonは開発中となっていますが、正式リリースがいつになるかは不明です。別のPython3系互換のJVM環境実装として[cafebabepy](https://github.com/cafebabepy/cafebabepy)というのがありますが、こちらは開発が止まっているようです。

### 1-2 JVMと連携できるライブラリを使う。(Py4j)

CPythonであっても[Py4j](https://www.py4j.org/)を使うことで、JavaのクラスをPythonのコード内で呼び出すことができます。

Py4jはPythonとJava二つのプログラムの橋渡しを行います。ですので、実際はPythonとJavaはそれぞれ別のプロセスとして独立して動作します。プロセスの間のデータのやり取りは、後述のプロセス間通信の一つであるループバックアドレスTCP/IP通信が用いられます。通信部分やデータのシリアライズ・デシリアライズはPy4jが担ってくれるため、プログラマーは実際の連係する動作のみをコードすれば良くなります。

## 2 プロセス間通信を使用する。

別の言語同士であるためプログラム内での利用が不可能な場合、一般的には、それぞれ別のプログラムとして作成し、別のプロセスとして起動するしかありません。しかし、別のプロセスであっても、データのやり取りを行う手段が用意されています。このようなプロセス間のデータのやり取りをプロセス間通信(IPC)と言います。プロセス間通信は、同じコンピューター上で行う物(ローカル通信)だけではなく、別のコンピューターどうしても行える物(リモート通信)があります。

今回はローカル通信でよく使われる手法について述べます。

### 2-1 メモリを共有する。(メモリマップトファイル)

各OSにはメモリマップトファイル等のプロセス間でメモリ空間を共有して利用する手段が用意されています。通常、メモリ空間はプロセス毎に用意され、独立しています。デバッガーなどの特殊なアプリを除いて、別のプロセスのメモリを読み書きすることはできません。しかし、メモリマップトファイル等を用いると、互いに共有で利用できるメモリ空間を確保し、お互いに読み書きできるようになります。

メモリマップトファイルはプログラムから見ればファイルのように扱えるメモリ空間です。他にも、UNIX/Linuxな環境では共有メモリという機能もあり、同じように使えます。

確保したメモリ空間をどのように使うかはプログラマーに任されます。設計がしっかりしないとデッドロックしたり、データが破損したりします。メモリ以外は介していないため最も高速ですが、実装の難易度はかなり高いでしょう。

### 2-2 パイプ接続してプロセス起動する。

もし、利用される側のプログラムが既に完成しており、そのデータのやり取りに標準入出力を使うのであれば、パイプを利用することが一番簡単な方法です。パイプはプロセスからプロセスへ標準入出力を繋ぐことです。シェルで`|`を使っているのを見たことがありますが、これが一番単純なパイプの使用例です。

Pythonでは[subprocess](https://docs.python.org/ja/3/library/subprocess.html)等を用いて簡単に別のプログラムをパイプ接続してプロセス起動し、利用することができます。簡単ですが、下記は`sed
s/a/z`という単にaをzに変えるだけのプログラムをパイプ接続して利用する例です。

[code]

    import subprocess
    input_text = "abcdef"
    output_text = subprocess.run(["sed", "s/a/z/"], input=input_text, capture_output=True, text=True).stdout
    print(output_text)
    
[/code]

パイプを利用する利点は、既に標準入出力でデータをやり取りできるプログラムをそのまま利用できると言うことです。欠点として、別のプログラムを利用しようとするたびにプロセス起動が発生することです。プロセス起動は重い処理(特にJVMや.NETなど仮想マシンやPythonやRubyと言ったインタプリターではさらに重い)であるため、何度も呼び出すような場合や応答速度が重要な場合は難しいです。

### 2-2 名前付きパイプを読み書きする。

IO処理となるため先程のパイプに似ていますが、標準入出力ではなく、ファイルのように扱う事ができるというところが違います。

ある名前(OSによっては具体的にファイルシステム上の特殊なファイルだったりする)が付けられたパイプのような物(だから名前付きパイプ)を用意し、片方のプロセスは書き込み専用に、もう片方のプロセスは読み取り専用でフォイルのように開きます。これらは、プログラムから見ると単純なファイルのように見えます。書き込み専用で開いたプロセスで名前付きファイルへ書き込みを行うと、読み取り専用で開いたプロセスで名前付きファイルから読み取ることができます。名前にファイルシステム上のファイル名を使う場合がありますが、その場合でもファイルシステムに対する読み書きは行われず、直接プロセスに渡されるため、高速にデータのやり取りが可能になります。

### 2-4 UNIXドメインソケットを等して通信する。

UNIX/LinuxにはUNIXドメインソケットという機能があります(Windowsにはありません)。UNIX/Linuxでの名前付きパイプと同じように、ファイルシステム上にソケットという特殊なファイルを作成して、その名前を通じて(実際にファイルシステムに読み書きはしない)データのやり取りをします。

名前付きパイプとの違いは通信の仕方です。名前付きパイプはファイルのように扱う事ができ、プログラムからは実際にファイルとして読み書きします。対して、UNIXドメインソケットはネットワーク通信として扱い、実際にネットワーク通信としてデータのやり取りを行います。通常ファイルの読み書きと名前付きパイプの読み書きがそのまま置き換えできるように、通常のネットワーク通信いわゆるTCP/IP通信)のデータのやり取りとUNIXドメインソケット通信のデータのやり取りも置き換えられると言うことです。

UNIXドメインソケット通信のメリットとして、後述のTCP/IP通信に置き換えることが簡単に可能と言うことがあります。また、実際にTCP/IPを処理する必要が無い分、ループバックアドレスTCP/IP通信よりも総じて速いです(遅い場合もあるらしい？)。

ネットワーク通信の実装はそれなりに知識が必要です。名前付きパイプよりは少し難易度が高いと思っています。

※
アプリでDB接続を設定する際、"サーバー名:ポート番号"以外に"UNIXドメインソケットのパス"が可能な場合をみるかと思いますが、これはTCP/IPでもUNIXドメインソケットでも繋いだ後は同じように扱えることができるため、両方のサポートが容易だからです。

### 2-5 ループバックアドレスTCP/IP通信を用いる。

WindowsにはUNIXドメインソケットはありません。その他、様々な理由で使えない場合は、ループバップバックアドレスに対してTCP/IP通信を行うという手段があります。

ループバックアドレスとは、IPv4では127.0.0.0/8(この範囲であればどれでも使えるが、主に127.0.0.1を使う場合が多い)、IPv6では::1のことです。このIPアドレスは必ず同じコンピューターに繋がります。ほとんどのシステムではlocalhostの名前でもアクセスできます。ループバックアドレスはNIC等を通さない内部通信であるため、通常のアドレスに比べて、高速かつ安全です。

ループバックアドレス部分を別のコンピューターのアドレスにすれば、コンピューター間でのプロセス間通信(リモート通信)もできます。そう、普段使っているようなブラウザーとWebサーバーも言わばプロセス間通信の一種です。将来的に重い処理は他のコンピューターで任せたいなどの分散処理を考えているのであれば、この方法を実装しておき、将来に備えるというのも一つの手かも知れません。

実際の実装は [akira ejiri
さんの回答](https://ja.stackoverflow.com/a/94237/7347)を参照してみてください。また、Py4jはこの方法を用いてプロセス間通信を行っています。

* * *

以上ですが、2の部分はPythonとJavaの組合せに限らず利用されている方法です。大きなデータのやり取りでは無く、単純な指令のみであれば、シグナルやメッセージキューなどの手法も用いられます。汎用的にはリモート通信も可能になるようにUNIXドメインソケットとTCP/IP両方をサポートできるようにネットワーク通信として実装されることが多いような気がします。そのような需要が皆無であれば、名前付きパイプあたりが単純で実装しやすいかと思います。用途や現状に合わせて選んでください。


CC BY-SA 4.0
2023-03-19T01:44:08.603
94241
2023-03-19T01:44:08.603
7347
94236
answer