【Python/ソケット通信】AF_UNIXを利用したプロセス間通信の実現

このページにはプロモーションが含まれています

このページでは、アドレスファミリー AF_UNIX を利用したプロセス間通信について説明していきます。

AF_UNIX とは

下記ページでも解説しているように、Python では socket モジュールを利用することでソケット通信を行うことが可能となります。より具体的には、socket モジュールで定義される socket クラスのインスタンスを利用してソケット通信を行うことができます。

Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)

AF_UNIX はアドレスファミリーの1つ

また、この socket クラスのインスタンスは socket クラスのコンストラクタを実行することで生成することができ、この socket のコンストラクタの第1引数にはアドレスファミリーを指定することが可能です。そして、その生成されるソケットでは、このアドレスファミリーに応じたプロトコルに従った通信が行われることになります。例えばアドレスファミリーに AF_INET を指定すれば IPv4 での通信が行われ、AF_INET6 を指定すれば IPv6 での通信が行われることになります。

socketクラスのコンストラクタの第1引数にアドレスファミリーを指定する様子

そして、アドレスファミリーに AF_INETAF_INET6 を指定した場合、異なる通信端末 (PC やスマホなど) の間で通信を行うことができ、一方の通信端末のソケットから他方の通信端末のソケットにデータを送信するようなことが可能となります。

アドレスファミリーAF_INETを指定することでIPv4での通信が行われる様子

ちなみに、このページ掲載の図における “NIF” とはネットワークインターフェースの略になります。

スポンサーリンク

AF_UNIX はプロセス間通信用のアドレスファミリー

このページで説明している AF_UNIX に関してもアドレスファミリーの1つになります。

ですが、このアドレスファミリーは少し特殊で、AF_UNIX を指定して生成したソケットでは異なる通信端末間での通信が不可です。

アドレスファミリーAF_UNIXでは異なる端末間での通信が不可であることを示す図

すなわち、同じ通信端末内の間でのみ通信が行われます。なので、言ってしまえば単なるプロセス間通信になります(図ではプログラムと記していますが、より正確にはプログラムではなくプロセスが正しいです)。

アドレスファミリーAF_UNIXが同じ端末内での通信で利用されることを示す図

プロセス間通信を実現する方法はいくつもあり、プロセス間通信にソケットを利用することは必須ではありません。ただ、ソケット通信と同じ感覚でプロセス間通信が実現できるため、ソケット通信に慣れている方であれば他の方法よりも簡単にプロセス間通信を実現することができます。この点が、アドレスファミリー AF_UNIX を指定したソケットでプロセス間通信を行うメリットであると言えます。

AF_UNIX ではネットワークインターフェースを介さない

また、AF_INETAF_INET6 でも、データの送信先の  IP アドレスに 127.0.0.1 を指定してやれば、自分自身の通信端末に対してデータを送信することが可能です。ただ、この場合は IPv4 や IPv6 で実際にループバック用のネットワークインターフェースをわざわざ介してプロセス間の通信が行われることになります。

このように、AF_INETAF_INET6 の場合、必ずネットワークインターフェースを介して通信が行われます。ですが、AF_UNIX の場合は、ネットワークインターフェースを介さずに通信が行われることになります。

アドレスファミリーAF_INETでは、必ずネットワークインターフェースを介して通信が行われることを示す図

さらに、AF_UNIX の場合はポート番号も使用されません。特にポート番号は、AF_INETAF_INET6 においては同じ通信端末内の複数の各ソケットを見分ける識別子の意味合いもあり、データの宛先にポート番号を指定することで、そのポート番号に bind されたソケットがそのデータを受け取ることができるようになっています。

ポート番号の役割を示す図

ですが、AF_UNIX の場合はポート番号は利用されません。では、ポート番号なしで、どうやって各ソケットを見分けるのか?という点が実装上のポイントになります。このあたりは、次の AF_UNIX でのソケット通信プログラムの作り方 の章で解説していきます。

これらに加え、AF_INETAF_INET6 の場合は、プロセス間でデータの送受信を行うだけであれば余分となるヘッダー等の送信や制御も行われることになるため、AF_UNIX の方がより高速な同一通信端末内での通信が実現できるようです(実際試してみたらあまり変わりませんでしたが…)。

AF_UNIX が使用可能かどうかは環境次第

ただし、AF_UNIX が使用可能かどうかは環境次第となります。おそらく Mac や Linux の場合は利用可能ですが、Windows の場合はちょっと怪しいかな…。少なくとも、私の Windows PC で Python の socket モジュールから AF_UNIX を利用することは不可でした。

Windows でも AF_UNIX が利用できるようになったという記事もありますので、もしかしたら今後は Windows PC で Python の socket モジュールから AF_UNIX が利用可能になる可能性もありますし、他のプログラミング言語やライブラリ等であれば既に使用できるようになっている可能性もあると思います。が、いずれにしても AF_UNIX が使用可能かどうかは環境によって異なることは覚えておきましょう。

スポンサーリンク

AF_UNIX でのソケット通信プログラムの作り方

次は、アドレスファミリー AF_UNIX でのソケット通信プログラムの作り方について説明します。

通信プログラムの処理の流れ自体はアドレスファミリーに AF_INET を指定したものと同様になりますので、基本的にはこの流れを真似してプログラムを開発してやれば良いです。この流れは下記ページで解説していますので、AF_INET でのソケット通信プログラムを開発したことのない方がおられましたら是非下記ページを参照していただければと思います。

Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)

ここでは、AF_INET での基本的なソケット通信プログラムの開発方法を理解していることを前提に、AF_UNIX での通信を行う際に注意が必要になる部分を中心に解説を進めさせていただきたいと思います。

ソケットを生成する

前述のとおり、Python の socket モジュールでは、socket クラスのコンストラクタによって生成されるインスタンスを利用してソケット通信を行うことができます。そして、このコンストラクタでは第1引数にアドレスファミリーが指定可能ですので、この第1引数に socket.AF_UNIX を指定してやれば、それによって生成されるインスタンスを利用した通信ではアドレスファミリー AF_UNIX に基づいた通信が行われることになります。

さらに、コンストラクタの第2引数でソケットのタイプを指定することができ、TCP のように通信を行いたいのであれば socket.SOCK_STREAM を、UDP のように通信を行いたいのであれば socket.SOCK_DGRAM を指定することになります。

“のように” とわざわざ書いているように、厳密には TCP 通信や UDP 通信ではありません。アドレスファミリー AF_UNIX での通信はもはや IP 通信ではないので、厳密に言うと TCP 通信や UDP 通信は行われないことになります。ただ、アドレスファミリー AF_UNIX で SOCK_STREAM を指定した場合は、AF_INET で TCP 通信を行うときと同様にストリーム型で通信が行われ、通信時に実行するメソッドやメソッドの実行順序も同じになります。socket.SOCK_DGRAM も同様に AF_INET で UDP 通信を行うときと同様にデータグラム型で通信が行われ、通信時に実行するメソッドやメソッドの順序も同じになります。

なので、AF_UNIX の場合は TCP 通信や UDP 通信が行われるわけではないのですが、指定したソケットタイプに応じて、TCP / UDP 通信と同じような通信が行われ、さらにスクリプトの書き方も同様になります。

そういった理由もありますし、毎回 “のように” を書くのは面倒なので、今後は “のように” を省いて単に “TCP 通信” や “UDP 通信” と記させていただきます。

例えば、AF_UNIX で TCP 通信を行うのであれば、下記のような処理によってソケットを生成することになります。

AF_UNIXでのTCP通信
import socket

# ソケットを作成
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

ソケットをファイルパスに bind する

次に必要に応じてソケットの bind を行います。この bindAF_UNIX での通信を上手く実現する上での最大のポイントになると思います。で、この bindAF_UNIX の場合もサーバー側での実行が必須となります。それに対し、AF_INET の場合はクライアント側は bind が必須ではないのですが、AF_UNIX の UDP 通信の場合のみクライアント側も bind が必要になるようです(ちょっと確証はないのですが、私の経験上 bind しないと上手く通信が行えなかったため、このページでは UDP 通信のクライアントも bind が必要という前提で解説を進めます)。

AF_INET の場合のおさらい

ここで、ちょっとだけ AF_INET における bind のおさらいをしておきたいと思います。

AF_INET の場合(AF_INET6 の場合もそうですが)、ソケットは IP アドレス及びポート番号に対して bind を行うことになります。これにより、その IP アドレス及びポート番号がソケットに関連付けられることになり、さらに、そのソケットでデータの受信待ちを行っておけば、その IP アドレス及びポート番号宛に送信されてきたデータを受信することができるようになります。そして、そのソケットを使用しているプログラムが、そのデータを受け取ることができます。また、IP アドレスは各ネットワークインターフェースに対して割り振られることになります。

AF_INETではIPアドレスとポート番号をbindすることを示す図

特に、この IP アドレスに関しては、各通信端末や、各通信端末の持つネットワークインターフェースを識別するために利用され、ポート番号に関しては、それらの通信端末内に複数存在するソケットを識別するために利用されることになります。

世の中には無数に通信端末が存在し、さらに、それらの通信端末内は複数のプログラムが動作していますが、そのような状況でも目当てのプログラムに対して正しくデータを届けることができるのは、これらの IP アドレスやポート番号のおかげということになります。

AF_UNIX ではファイルパスに bind する

ただ、AF_UNIX の場合、前述で解説したように、データの送信相手は同じ通信端末内のプロセス(実行中のプログラム)となり、ネットワークインターフェースを介した通信は行われません。そのため、IP アドレスでネットワークインターフェースや他の通信端末を識別するようなことは不要です。

ただし、通信端末内には複数のソケットが存在する可能性があるため、送信相手先となるソケットを特定するような識別子は必要です。AF_UNIX の場合、この識別子として “ファイルパス” を利用することになっており、AF_UNIX の場合はソケットに対して “ファイルパス” を bind するようになっています。このようにファイルパスに bind した状態のソケットで受信待ちを行っておけば、そのファイルパス宛に送信されてきたデータを受信することができるようになります。

AF_UNIXの場合、ソケットとbindするのはファイルパスであり、このファイルパスがデータの送信先を特定する識別子となることを示す図

AF_UNIX の場合は、ソケット自体が bind されたパスのファイルとなり、それを識別するための情報が、bind したときに指定したファイルパスとなります。そして、このソケット、すなわちファイルを介してプロセス間でデータの送受信が行われることになるようです。

bind 実行時にファイルが生成される

さらに、AF_UNIX の場合は、bind 実行時に引数に指定したファイルパスにファイルが新規作成されることになります。なので、引数に指定するファイルパスは、ファイルが作成可能なパスである必要があります。

bindの引数に指定したパスにbind実行時にファイルが新規作成される様子

もし、引数に指定したファイルパスにファイルが既に存在していれば  bind 時に下記の例外が発生することになります。

[Errno 98] Address already in use

実行する環境等によってエラーコードやメッセージは異なる可能性はありますが、どんな環境であれ、引数に指定したファイルパスにファイルが既に存在していると例外が発生する点は共通になると思います。

重要なのは、引数に指定したパスに既にファイルが存在している場合、bind が行えず、それ以降の通信が成立しないという点になります。このため、引数には毎回異なるパスを指定する or 引数に指定するパスにファイルが既に存在する場合は事前にファイルを削除しておくことが必要となります。前者の場合、どんどんファイルが増えていくことになるので、後者の方がオススメです。

以上を考慮した bind の実行例は下記のようになります。

bindの実行例
import socket
import os

# ソケットファイルのパス
server_path = '/tmp/server_socket'

# ソケットを作成
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)

# ファイルを削除
if os.path.exists(server_path):
    os.remove(server_path)

# bindの実行
sock.bind(server_path)

スポンサーリンク

ファイルパスを宛先としてデータを送信する

先ほど説明したように、ファイルパスに対して bind を行ったソケットでデータの受信待ちを行うと、そのファイルパスが宛先であるデータをソケットがデータを受信できるようになります。

なので、AF_UNIX の場合、目当てのソケットに対してデータを届けるためには、データの送信側は “相手が bind したファイルパス” に対してデータを送信する必要があります。

AF_UNIXの場合、データの送信先にもファイルパスを指定する必要があることを示す図

例えば、connect メソッドは接続要求を送信するメソッドで、AF_INET の場合、引数に “要素を IP アドレスとポート番号とするタプル” を指定して送信先を指定する必要があります。それに対し、AF_UNIX の場合、引数には “ファイルパス” を指定することになります。これにより、そのファイルパスで bind されたソケットに対して接続要求が送信されることになります。そして、相手が accept メソッドを実行すれば、それらの2つのソケット間で接続が確立されることになります。

同様に、sendto メソッドの第2引数ではデータの送信先を指定する必要があり、AF_UNIX の場合、第2引数には “ファイルパス” を指定することになります。

こんな感じで、AF_UNIX の場合、データを送信する時にもファイルパスが送信相手を特定するための識別子となる点に注意してください。

後は通常通りに通信を行う

bind やデータ送信時に “IP アドレスとポート番号” の代わりに “ファイルパス” を指定する必要があることさえ意識できれば、後はいつも通り、AF_INET の時と同様に通信を行えば良いだけです。この AF_INET での TCP や UDP で通信を行うときの処理の流れや実行すべきメソッドは下記ページで詳細を解説していますので、詳しくは下記ページを参照してください。

Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)

結論だけ述べておくと、TCP 通信を行うのであれば、下図のシーケンスに従ってメソッドを実行するサーバーとクライアントを開発してやれば良いことになります。ただし、下図のピンク部分が示すように、bind 前にはファイルの削除が必要となります。ここが AF_INET のシーケンス上の違いとなります。

AF_UNIXでのTCP通信のシーケンス

UDP 通信の場合も、ほぼ同様ではあるのですが、前述でも少し述べたようにクライアント側でも bind が必要になるようです。なので、下図のシーケンスに従ってメソッドを実行するサーバーとクライアントを開発してやれば良いことになります。また、AF_UNIX の場合、ファイルパスがソケットを区別する識別子となるため、クライアントとサーバー側とで異なるファイルパスに対して bind を行う必要があります。

AF_UNIXでのUDP通信のシーケンス

こんな感じで、bind に関する部分が異なるものの、基本的には AF_INET の場合も AF_UNIX の場合も処理の流れは同様で、アドレスファミリー AF_UNIX を利用することで、通常のソケット通信と同様の感覚でプロセス通信を実現することが可能です。

プロセス間通信プログラムがソケット通信プログラムと同じ感覚で開発可能であることを示す図

AF_UNIX でのソケット通信プログラムの例

最後に、ここまで説明してきた内容を踏まえ、アドレスファミリー AF_UNIX を利用したソケット通信プログラムのスクリプトの例を示していきたいと思います。

ここでは、下記の4つのスクリプトを示していきます。ソケットを生成する でも説明したように、AF_UNIX の場合は IP 通信は行われず、実際の TCP 通信 / UDP 通信とは異なるのですが、あえて表現を簡略化するために TCP 通信 / UDP 通信と記している点に注意してください。

また、これから紹介していく上記の4つのスクリプトは下記ページで紹介したスクリプト(AF_INET の場合のスクリプト)をベースに変更を加えたものになります。

Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)

ベースとなるスクリプトは上記のカッコ内に示すリンク先から参照できるようにしており、差分を確認していただければ、AF_INET と AF_UNIX とでのスクリプトの書き方の違いが分かると思いますので参考にしてください。

MEMO

ここで紹介する Python スクリプトは Linux (Ubuntu) と Mac での未動作確認を行っています

そもそも、私の Windows の Python ではアドレスファミリーに AF_UNIX を指定することができませんでした

スポンサーリンク

TCP 通信を行うプログラム

では、まずは TCP 通信を行うプログラムのスクリプト例を示していきます。

TCP 通信のサーバー

TCP 通信を行うサーバーのスクリプトの例は下記のようになります。

tcp_server.py
import socket
import os

# socket.socketのサブクラス
class UnixDomainSocket(socket.socket):

    def __init__(self, type=-1, proto=-1, fileno=None):
        # 第1引数はAF_UNIX固定でsocketのインスタンス生成
        super().__init__(socket.AF_UNIX, type, proto, fileno)

    def bind(self, address):
        # 指定されたファイルパスのファイルを削除
        if os.path.exists(address):
            os.remove(address)

        # bind実行
        super().bind(address)

# ソケットを作成する
sock = UnixDomainSocket(socket.SOCK_STREAM)

# ファイルパスにバインドする
address = '/tmp/server_socket'
sock.bind(address)

'''後はいつも通りの流れでメソッドを実行'''

sock.listen(5)

# 無限ループで常駐させる
while True:

    # 接続要求の受付 / 接続の確立
    e_sock, addr = sock.accept()

    # データの受信待ち
    data = e_sock.recv(1024)
    
    # サービス・受信データに応じた処理
    str_data = data.decode()
    upper_str_data = str_data.upper()

    # 処理結果の送信
    send_data = upper_str_data.encode()
    e_sock.send(send_data)
   
    e_sock.close()

sock.close()

このスクリプトでは UnixDomainSocket というクラスを定義しており、これは socket モジュールの socket クラスのサブクラスになります。

この UnixDomainSocket では、 socket クラスの __init__ と bind をオーバーライドしています。前者ではスーパークラス(すなわち socket クラス)の __init__ を実行しているだけなのですが、必ずアドレスファミリーとして AF_UNIX が指定されるようになっています。ソケットタイプ等は type 引数で指定可能で、socket.SOCK_STREAM を指定することで、この __init__ で生成されるソケットでは TCP 通信が行えるようになります。

また、後者の bind では、引数 address でファイルパスを受け取り、そのファイルパスに既にファイルが存在する場合はファイルの削除を行った後に、スーパークラスの bind を実行するようになっています。

つまり、この UnixDomainSocket は “アドレスファミリー AF_UNIX での通信” 専用のクラスになっており、アドレスファミリーとして AF_UNIX を指定した場合に必要な処理のみをメソッドのオーバーライドによって実現するようになっています。

なので、socket.socket() を実行するのではなく UnixDomainSocket() を実行してソケットを生成すれば、後は AF_INET の時と同様の処理の流れで TCP 通信でのデータの送受信を実現することが可能となっています。

ただし、bind 実行時にはファイルパスを指定する必要があるので注意してください。上記では、そのファイルパスを /tmp/server_socket としていますが、必要に応じてご利用されている OS 等に合わせた適切なファイルパスに変更していただければと思います。

また、ここで紹介した UnixDomainSocket クラスは、今後紹介するスクリプトでも同じものを利用しています。

TCP 通信を行うクライアント

TCP 通信を行うクライアントのスクリプトの例は下記のようになります。

tcp_client.py
import socket
import os

# socket.socketのサブクラス
class UnixDomainSocket(socket.socket):

    def __init__(self, type=-1, proto=-1, fileno=None):
        # 第1引数はAF_UNIX固定でsocketのインスタンス生成
        super().__init__(socket.AF_UNIX, type, proto, fileno)

    def bind(self, address):
        # 指定されたファイルパスのファイルを削除
        if os.path.exists(address):
            os.remove(address)

        # bind実行
        super().bind(address)

# ソケットを作成する
sock = UnixDomainSocket(socket.SOCK_STREAM)

input_str_data = input('文字列を入力してください : ')

# サーバーのソケットにデータを送信
address = '/tmp/server_socket'
sock.connect(address)

# データの送信
data = input_str_data.encode()
sock.send(data)

# データの受信
data = sock.recv(1024)
str_data = data.decode()

print(str_data)

sock.close()

このスクリプトのポイントは、サーバーに対して接続要求を connect メソッドで送信する際、その宛先としてファイルパスを指定している点になります。AF_UNIX の場合、データの送信先はファイルパスで指定する必要があります。これにより、このファイルパスで bind してデータの受信待ちをしているソケットが、そのデータを受信することができるようになります。

TCP 通信を行うプログラムの動作確認

簡単に、先ほど示したサーバーとクライアントの動作確認を行っておきましょう!

まず、TCP 通信のサーバー で示したスクリプトを tcp_server.py という名前、さらに TCP 通信のクライアント で示したスクリプトを tcp_client.py という名前で適当な同じフォルダに保存してください。そして、ターミナルアプリ(Windows の場合はコマンドプロンプトや PowerShell 等)を2つ起動し、cd コマンドを実行して両方のターミナルでスクリプトを保存したフォルダーに移動してください。

続いて、一方側のターミナルで下記のように tcp_server.py を python コマンドで実行してください。このターミナルでの操作は以上で終了です。これによりサーバーが立ち上がり、クライアントからのデータの送信を待ち続けるようになります。

python tcp_server.py

次に、他方側のターミナルで下記のように tcp_client.pypython コマンドで実行してください。

python tcp_client.py

すると、文字列の入力が促されますので、適当に小文字で文字列を入力してエンターキーを押してください。

文字列を入力してください :

これにより、入力した文字列を大文字に変換した結果が表示されるはずです。

文字列を入力してください : hello world
HELLO WORLD

これは、サーバーがクライアントから受け取った文字列を大文字に変換した結果となります。この結果をクライアントが受け取り、それを表示しています。これが表示されれば、サーバーとクライアント間でデータの送受信が行えていることが確認できたことになります。アドレスファミリーには AF_UNIX を指定しているため、AF_UNIX での通信によってデータの送受信が行えていることになります。

サーバーは無限ループで常駐するようになっているため、サーバーを終了させたい場合は、tcp_server.py を実行した方のターミナルで control + c を入力してプログラムを強制終了させてください。

また、サーバーのスクリプトで bind したファイルパスを確認してみると、そのパスにファイルが作成されていることが確認できると思います。AF_UNIX の場合、このように実際にファイルが作成され、これを利用して2つのプログラム間で通信が実現されていることになります。また、bind 時に、既にファイルが存在すると例外が発生することになるため、存在する場合は事前にファイルを削除することも重要となります。

ls -al /tmp/server_socket
srwxr-xr-x 1 daeu daeu 0 Jul 26 07:46 /tmp/server_socket

UDP 通信を行うプログラム

つづいて、UDP 通信を行うプログラムのスクリプト例を示していきます。

ここで紹介するスクリプトでは、サーバーだけでなくクライアントも bind を実行するようにしており、そのためにサーバーのソケットに bind するファイルパスだけでなく、クライアントのソケットに bind するファイルパスも定義するようにしています。

AF_INET の場合は、クライアント側は bind しなくても動的なソケットへのポート番号の bind が自動で行われるため、わざわざクライアント側は bind を実行する必要はないです(実行しても良い)。なんですが、AF_UNIX の場合は、少なくとも私の環境では自動的な bind が行われなかったため、クライアントのソケットの bind 先のファイルパスを静的に定義し、それを bind するようにしています。

UDP 通信を行うサーバー

UDP 通信を行うサーバーのスクリプトの例は下記のようになります。

udp_server.py
import socket
import os

# socket.socketのサブクラス
class UnixDomainSocket(socket.socket):

    def __init__(self, type=-1, proto=-1, fileno=None):
        # 第1引数はAF_UNIX固定でsocketのインスタンス生成
        super().__init__(socket.AF_UNIX, type, proto, fileno)

    def bind(self, address):
        # 指定されたファイルパスのファイルを削除
        if os.path.exists(address):
            os.remove(address)

        # bind実行
        super().bind(address)

# ソケットを作成する
sock = UnixDomainSocket(socket.SOCK_DGRAM)

# ファイルパスにバインドする
address = '/tmp/server_socket'
sock.bind(address)

'''後はいつも通りの流れでメソッドを実行'''

# 無限ループで常駐させる
while True:

    # データの受信待ち
    data, addr = sock.recvfrom(1024)
    
    # サービス・受信データに応じた処理
    str_data = data.decode()
    upper_str_data = str_data.upper()

    # 処理結果の送信
    send_data = upper_str_data.encode()
    sock.sendto(send_data, addr)
   
sock.close()

アドレスファミリーに AF_UNIX を指定した場合はファイルパスに bind する必要がありますが、基本的には AF_INET の時と同様の流れの処理で UDP のサーバーが実現できることを確認していただけると思います。

ちなみに、recvfrom の2つ目の返却値では、クライアント側のソケットに bind されたファイルパスを取得することができます。なので、recvfrom の後に第2引数に recvfrom の2つ目の返却値を指定して sendto を実行すれば、クライアント側のソケットにデータが届くことになります。

UDP 通信を行うクライアント

UDP 通信を行うクライアントのスクリプトの例は下記のようになります。

udp_client.py
import socket
import os

server_address = '/tmp/server_socket'

# socket.socketのサブクラス
class UnixDomainSocket(socket.socket):

    def __init__(self, type=-1, proto=-1, fileno=None):
        # 第1引数はAF_UNIX固定でsocketのインスタンス生成
        super().__init__(socket.AF_UNIX, type, proto, fileno)

    def bind(self, address):
        # 指定されたファイルパスのファイルを削除
        if os.path.exists(address):
            os.remove(address)

        # bind実行
        super().bind(address)

# ソケットを作成する
sock = UnixDomainSocket(socket.SOCK_DGRAM)

# ファイルパスにバインドする
address = '/tmp/client_socket'
sock.bind(address)

input_str_data = input('文字列を入力してください : ')

# データの送信
data = input_str_data.encode()
sock.sendto(data, server_address)

# データの受信
data = sock.recv(1024)
str_data = data.decode()

print(str_data)

sock.close()

前述のとおり、クライアント側でも bind を実行し、明示的にクライアント側がデータの受信待ちを行うファイルパスを指定するようにしています。

そのため、上記の UDP 通信のサーバーと UDP 通信のクライアントを実行すれば、サーバーのソケットに bind されたファイルパスと、クライアントのソケットに bind されたファイルパスの2つの位置にファイルが生成されることになります。

もしかしたら、AF_INET のとき同様にクライアント側の bind は不要にできるのかもしれないですが、私が試してもダメだったので、とりあえずクライアント側でも bind を実行するスクリプトの例を紹介させていただいています。

UDP 通信を行うプログラムの動作確認

動作確認の手順に関しては、TCP 通信を行うプログラムの動作確認 と同様になるので説明は省略させていただきます。クライアント側でも bind が行われるため、その引数に指定したパスにもファイルが作成されるところがポイントになると思います。

まとめ

このページでは、アドレスファミリー AF_UNIX を利用したプロセス間通信について説明しました!

AF_UNIX は同一通信端末内での通信を実現する時に利用されるアドレスファミリーになります。このアドレスファミリーのソケットでは bind 時にファイルパスを指定する必要がある& connect や sendto でのデータの送信先をファイルパスで指定する必要があるものの、それ以外は基本的に AF_INIT を利用する時と同様の手順で通信プログラムを開発することが可能です。

基本的には、アドレスファミリーには AF_INET を利用すればよいのですが、同一通信端末内でのみの通信で良いのであれば AF_UNIX の利用も検討してみましょう!

同じカテゴリのページ一覧を表示