プロセス間でデータをやりとりするとき使われる手段の一つとしてソケット通信がある。
他に、共有メモリ、セマフォ、マップドメモリ、パイプがあるらしい。
パイプは|でよく使う。プロセスA|プロセスBでAの出力をBに渡して処理する。
例えば“example” という文字列を含むファイルを検索する場合
ls(ディレクトリ名、ファイル名表示)の出力をgrep(文字列検索)で処理して出力する。
ls | grep "example"
もう一つ、よく目にするのがソケットです。
ssコマンドでソケットの状態が表示されるが
よくわからなかったので実験してプロセス間のソケット通信とは何か確認した。
私が勘違いしているところもあると思いますのでご了承ください。
こちら↓の環境を使用しています。
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()
実行方法
- サーバ側のコードを実行します。バックグラウンド実行にするため末尾に&
バックグラウンド実行すれば続けてコマンド入力できる。
python3 server_socket.py & - クライアント側のコードを実行します。
python3 client_socket.py
動作
- クライアント側がサーバ側に接続要求を送信します。
- サーバ側は接続要求を受け付け、クライアントとの接続を確立します。
- クライアント側はサーバ側にデータを送信します。
- サーバ側はクライアントからのデータを受け取り、処理します。
- 受信データをprintします。
- サーバ側はクライアントにデータを送信します。
- クライアント側はサーバからのデータを受け取り、処理します。
- 双方がソケットをクローズします。
解説
socket.socket
関数は、ソケットを作成します。socket.AF_UNIX
は、UNIXドメインソケットを表します。socket.bind
メソッドは、ソケットを特定のパスにバインドします。socket.listen
メソッドは、ソケットを接続待ち状態にします。socket.accept
メソッドは、クライアントからの接続要求を受け付け、接続を確立します。socket.recv
メソッドは、ソケットからデータを受信します。- 受信データをprintします。
socket.send
メソッドは、ソケットにデータを送信します。socket.close
メソッドは、ソケットをクローズします。
その他
- 上記のサンプルコードは、基本的なUNIXドメインソケット通信の例です。
- より複雑な通信を行う場合は、
select
やpoll
などのモジュールを使用する必要があります。 - 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)
目次へ
コメント