プロセス間のソケット通信を実験してみた

※ 当サイトではアフィリエイト広告を利用しています。リンクは広告リンクも含みます。

この記事は約12分で読めます。
広告

プロセス間でデータをやりとりするとき使われる手段の一つとしてソケット通信がある。
他に、共有メモリ、セマフォ、マップドメモリ、パイプがあるらしい。
パイプは|でよく使う。プロセスA|プロセスBでAの出力をBに渡して処理する。
例えば“example” という文字列を含むファイルを検索する場合
ls(ディレクトリ名、ファイル名表示)の出力をgrep(文字列検索)で処理して出力する。

ls | grep "example"

もう一つ、よく目にするのがソケットです。
ssコマンドでソケットの状態が表示されるが
よくわからなかったので実験してプロセス間のソケット通信とは何か確認した。
私が勘違いしているところもあると思いますのでご了承ください。
こちら↓の環境を使用しています。

お名前ドットコムVPSへ移行への道

2024/4お名前.comのレンタルサーバーからVPSへ移行したときの記録になります。

TCPソケットはこちら

広告

Geminiに聞いて作った

「UbuntuでPythonでホスト内でプロセス間でソケット通信を行うサンプル」
とGeminiに聞いた。
答えに表示するprintがなかったので追加した。
その他、解説も手を加えた。

Geminiに聞いた答え
以下のサンプルコードは、UbuntuでPythonを使用して、ホスト内でプロセス間でソケット通信を行う例です。
1. サーバ側

server_socket.py

import socket

# ソケットの作成
server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

# ソケットのbind
server_socket.bind('\0tmp/sock')

# ソケットのlisten
server_socket.listen(1)

# クライアントからの接続待ち
client_socket, client_address = server_socket.accept()

# データの受信
data = client_socket.recv(1024)
print('サーバーが受信したデータは'+data.decode('utf-8'))#←追加:dataは、byteなのでそのままprintするとエラーになるのでutf-8に変換
# データの送信
client_socket.send(b'Hello, world!')

# ソケットのクローズ
client_socket.close()
server_socket.close()

2. クライアント側

client_socket.py

import socket

# ソケットの作成
client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

# ソケットのconnect
client_socket.connect('\0tmp/sock')

# データの送信
client_socket.send(b'Hello, server!')

# データの受信
data = client_socket.recv(1024)
print('クライアントが受信したデータは'+data.decode('utf-8'))#←追加:dataは、byteなのでそのままprintするとエラーになるのでutf-8に変換
# ソケットのクローズ
client_socket.close()

実行方法

  1. サーバ側のコードを実行します。バックグラウンド実行にするため末尾に&
    バックグラウンド実行すれば続けてコマンド入力できる。
    python3 server_socket.py &
  2. クライアント側のコードを実行します。
    python3 client_socket.py

動作

  1. クライアント側がサーバ側に接続要求を送信します。
  2. サーバ側は接続要求を受け付け、クライアントとの接続を確立します。
  3. クライアント側はサーバ側にデータを送信します。
  4. サーバ側はクライアントからのデータを受け取り、処理します。
  5. 受信データをprintします。
  6. サーバ側はクライアントにデータを送信します。
  7. クライアント側はサーバからのデータを受け取り、処理します。
  8. 双方がソケットをクローズします。

解説

  1. socket.socket 関数は、ソケットを作成します。
  2. socket.AF_UNIX は、UNIXドメインソケットを表します。
  3. socket.bind メソッドは、ソケットを特定のパスにバインドします。
  4. socket.listen メソッドは、ソケットを接続待ち状態にします。
  5. socket.accept メソッドは、クライアントからの接続要求を受け付け、接続を確立します。
  6. socket.recv メソッドは、ソケットからデータを受信します。
  7. 受信データをprintします。
  8. socket.send メソッドは、ソケットにデータを送信します。
  9. socket.close メソッドは、ソケットをクローズします。

その他

  • 上記のサンプルコードは、基本的なUNIXドメインソケット通信の例です。
  • より複雑な通信を行う場合は、selectpoll などのモジュールを使用する必要があります。
  • UNIXドメインソケットは、ネットワーク越しではなく、ホスト内のプロセス間通信にのみ使用できます。

目次へ

結果

実行したところからのターミナル表示のコピーです。

python3 server_socket.py &
[1] 3871618
python3 client_socket.py
サーバーが受信したデータはHello, server!
クライアントが受信したデータはHello, world!
[1]+  Done                    python3 server_socket.py
何か失敗したときプロセスが残っている場合はkillで削除したほうがいいかも
文字列python3で実行したプロセスを調べるコマンド
ps aux | grep "python3"
そのとき今回実行したことによってできたプロセスでないものをkillしたら大変なので
プロセスを開始した時間などで判別する。
root        5989  0.0  0.0  15348   220 ?        S    Mar29   0:28 /usr/bin/python3 /usr/bin/pidproxy /var/spool/postfix/pid/master.pid /usr/sbin/postfix start-fg
user  3874060  0.2  0.9  16144  9284 pts/1    S    06:23   0:00 python3 server_socket.py
user  3874135  0.6  0.9  16144  9276 pts/1    S    06:23   0:00 python3 client_socket.py
目次へ

プロセスができているのか確認

プロセスができているか確認するため
client_socket.pyのデータ送信前に1分待つようにした。
その間にps aux | grep "python3"でプロセスの一覧を表示してみる。
import time
略
# 1分待つ
time.sleep(60)  # 60秒待機
# データの送信

実行は両方とも末尾に&を付けバックグラウンド実行

python3 server_socket.py &
[1] 3874060
python3 client_socket.py &
[2] 3874135
ps aux | grep "python3"

user  3874060  0.2  0.9  16144  9284 pts/1    S    06:23   0:00 python3 server_socket.py
user  3874135  0.6  0.9  16144  9276 pts/1    S    06:23   0:00 python3 client_socket.py
user  3874142  0.0  0.2   8160  2336 pts/1    S+   06:23   0:00 grep --color=auto python3
サーバーが受信したデータはHello, server!
クライアントが受信したデータはHello, world!
^C
[1]-  Done                    python3 server_socket.py
[2]+  Done                    python3 client_socket.py

ss コマンドを使って、ソケットとプロセスの関連情報を取得

python3 server_socket.py &
[1] 3880706
python3 client_socket.py &
[2] 3880728
ss -anp | grep "python3"
u_str              LISTEN              0                    1                                                                                         @tmp/sock 12420870                                              * 0                        users:(("python3",pid=3880706,fd=3))                                           
u_str              ESTAB               0                    0                                                                                         @tmp/sock 12420871                                              * 12421486                 users:(("python3",pid=3880706,fd=4))                                           
u_str              ESTAB               0                    0                                                                                                 * 12421486                                              * 12420871                 users:(("python3",pid=3880728,fd=3))  

pid=3880706がサーバーのプロセス
pid=3880728がクライアントのプロセス
サーバープロセスはLISTENとESTABがありLISTENのほうは新しい接続がきたら
もう1個ESTABするものと思われる。
「「u_str」はUNIXドメインソケット(AF_UNIX)を表し、
同じマシン上のプロセス間で通信するために使用されます。」(Bing談)
ソケットをつくるときsocket.AF_UNIXと指定しているので
ssでみたときはu_strになっているみたい。
目次へ

SSH接続したVsコードを使っているときのss表示

SSH接続したVsコードのターミナルで
TCP/IPソケットを表示するコマンドss -antpを実行すると

ESTAB    0        156         ホストのIP:*       自分のパソコンのIP:5***7                                                    
ESTAB    0        0                 127.0.0.1:55555           127.0.0.1:77777                                                    
ESTAB    0        0                 127.0.0.1:77777           127.0.0.1:55555    users:(("code-****",pid=3*****4,fd=11))  
1行目はホストと自分のパソコンをTCP接続しているのだからわかるが
あとのESTAB(通信成立)しているのが何か推測すると
内部のネットワークでTCP接続している。
つまりホスト内部でもソケットはAF_UNIXだけではなくAF_INETを使っていると思われる。 
Vsコードを切断してVPSのシリアルコンソールで確認すると、
上でESTABしている127.0.0.1がLISTENしているわけではなかった。
127.0.0.53%lo:53というDNSに関係するものしかLISTENしてなかった。

また以下の構成でNginxをproxyサーバーとして
ブリッジネットワークでDjangoのコンテナと通信している。

この場合は同じホストであるにもかかわらずTCP接続しているはずです。(多分、間違い。追記参照)
ただ、これもLISTENしているのが外側のportだけで
ブリッジネットワーク内のプロセスがLISTENしてないのは謎です。
→TCPを使ってないということ。追記参照

LISTEN   0        4096                0.0.0.0:80                0.0.0.0:*                                                        
LISTEN   0        4096                0.0.0.0:443               0.0.0.0:*  

追記

こちら↓を見ると図にUnix Domain Socketと書いてある。
Gunicorn + Flask + nginx で Python の Web アプリ入門 | ほげほげテクノロジー (hogetech.info)
gunicornやWSGIが、いつもよくわからないのだが、少し前進した感じになった。

所感

プロセス間でデータのやり取りの仕組みは、そこまで気にしなくても済むが
ソケットのことをある程度知っていると
何か不具合で原因を調べるとき、ソケットの存在に惑わされないで
問題の的がしぼれると思います。
イチゲをOFUSEで応援する(御質問でもOKです)Vプリカでのお支払いがおすすめです。
MENTAやってます(ichige)
目次へ

コメント

タイトルとURLをコピーしました