積読消化中、「Goならわかるシステムプログラミング」を読んでいたらUNIXドメインソケットについて気になった記述がありました。
ファイルパスを指定する以外の作成方法もあります。ただし、Go言語からは使えません。
これまでphp-fpmやMySQLでファイルパス有り(*.sockファイル)のパターンしか扱ったことなかったので一度触れておきたい所です。
ネットでぐぐるとUNIXドメインソケットには以下の3つの種類(接続方式?)があることが分かったのでそれぞれ実装していきます。ネット記事ではCやPythonのコードが散見されたので、被らないようにPHP8で書いてみました。
ファイルパス
まずよくある*.sockを使うパターン。tcpとudpのサンプルは以下URLにありますが、unixのコードは見当たりませんでした。仕様はドキュメントそのままって感じなのでコードの記載くらいにします。
PHP: stream_socket_server - Manual
無名
こちらは以下にコード例の記載がありました。socketpairを使ってフォークした親子間のプロセスで通信するような実装に見えますが、コード眺めるだけだと動きが分かりにくかったので少し手を入れました。1つのソフトウェア上で完結し、外部から連携させたくない場合に有用かも?
PHP: stream_socket_pair - Manual
抽象 名前空間
php.netの日本語/英語を見ても抽象名前空間に関する記述が見当たらず。
抽象名前空間(abstractと表現される模様)の仕様的にはファイルパスのパスの先頭をnullバイト(PHPのエスケープ文字で言うと\0)から始める文字列にするだけで、ファイルパスと変わらんのでは? くらいに思ってたのですがちょっと苦戦しました。
ハマったところ
PHP Fatal error: Uncaught Exception: Failed Socket Server in
このエラーがなかなか解決できず時間がかかりました。"unix://\0testtest"の指定が悪いのかなと色々直してもダメで、PHPのコードを読みにいくとLinux-only という単語をみっけてしまいました。MacOSでは駄目なんですね😅
Linux(Docker)でリトライ
前述のコードがあるソースのルートで下記を実行します。composerとかも使ってなく、ホスト側のコードをそのままマウントして実行して問題ないです。
docker pull php:8.1.9
docker run -it --name php8 -v "$PWD":/app php:8.1.9 bash
docker-php-ext-install sockets pcntl
cd /app
コンテナ内で再度 php_unix_domain_socket_abstract_server.phpとphp_unix_domain_socket_abstract_client.phpを実行したらうまく動作しました。
各*_server.phpをnetstatで確認
コンテナ内でnet-toolsのインストールコマンド
apt install net-tools
ファイルパスのnetstat -a
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ACC ] STREAM LISTENING 27408 test.sock
無名のnestat -a
Proto RefCnt Flags Type State I-Node Path
unix 3 [ ] STREAM CONNECTED 33176
unix 3 [ ] STREAM CONNECTED 33175
抽象名前空間のnetstat -a
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ACC ] STREAM LISTENING 28363 @testtest
まとめ
異なるソフトウェア間(php-fpmとNginx間やRedis-各プログラミング言語間など)ではファイルパスが分かりやすくて良いですが、無名は他の実装方法の方が有力なケースが多い気がしていて使い所があまりピンときていません。。
数年前に出たものですが、「Linuxシステムコール基本リファレンス」(これも積読)を読んでから「Goならわかるシステムプログラミング」を読むと効率的に学習できそうです。今回の3種類については特に記載されてませんでしたけど。
低レイヤーはまだまだ知らないことが多く、勉強不足です(-.-;)y-~~~