【Python】socket.accept() についてまとめてみた
目的
Pythonの socket モジュールを使って、UNIXドメインソケットを通じた通信を実現する際に使用する
socket.accept() によって、プロセスが一時停止するブロッキングという現象を理解すること
きっかけ
UNIXドメインソケットを使った通信を実現するにあたって、socket.accept() の役割が整理できず、何をおこなっているのか最初にイメージできなかったため
前提
- 使用言語:Python
- UNIXドメインソケットについてざっくり理解していること
- socket.accept()が何を行なう処理かを理解していること
- 同期・非同期処理についてはこの記事では言及しないこと
クライアントからのリクエスト受け付けるための初期設定・準備(サーバ側)
サーバは Python の socket モジュールを通じて、以下の処理を行ない、クライアントからの処理を受付・処理します。
-
socket.socket(): 受付用ソケット(通信窓口)を作成 -
socket.bind(): ソケットに特定のファイルパス(住所)を設定 -
socket.listen(): 受付用ソケット(通信窓口)を開け、バックログキューという接続待ちの列を用意し、「接続待ち」の状態となる -
socket.accept(): クライアントが接続を試みるまで、一時停止(ブロッキング)する
socket.accept()
この socket.accept() を理解するには、2つの動きがあることを理解する必要があります。
1. サーバプロセスのブロッキング
socket.accept() が実行されると、accept() の行より下に書かれたコードは、クライアントとの接続が確立されるまで実行されなくなります。
この間、サーバプロセスは待機中(スリープモード)になり、一時停止します。
2. ブロッキングの解除
クライアントからの接続が完了すると、
- サーバプロセスのブロッキングが解除される
- プロセスの状態が「待機中」から「実行待ち状態」→「実行状態」となる
ブロッキング
そのメソッドが処理を完了するまで、それを呼び出したプロセス(またはスレッド)の実行を一時停止(ブロック)させる現象のことです。
socket.accept() は、クライアントからの接続要求が届くまで待機し、接続が確立されると、
その接続を表す新しいソケットオブジェクト(接続用ソケット)と、クライアントのアドレスを返します。
接続要求がない間は、プログラムの実行は次の行に進まず、**accept()**の場所で止まったままになります。
補足
Python では、socket.socket オブジェクトとして扱われます。
これは内部でFDを保持しており、read/write に似たメソッド(recv/send)を持つため「ファイルライク」ですが、厳密にはディスク上のデータを扱う「ファイルオブジェクト」とは区別されます。
accept() の返却値(FDの返却)
キューに情報が入ると、socket.accept() が以下の処理を行ない、サーバープロセスに制御を戻します。
1. 新しいソケットの作成
OSのカーネルは、この特定のクライアントとの通信を担当する新しいソケット構造体をメモリ上に作成します。
2. ファイルディスクリプタ (FD) の割り当て
この新しいソケット構造体に対応するファイルディスクリプタ (FD) をサーバプロセスに割り当てます。
3. FDの返却
accept() メソッドは、この新しいFDをラップした Python の接続用ソケットオブジェクトを返します。
まとめ
接続が成立する基本的な流れ
サーバー側での listen (待ち受け) が完了した後に、クライアント側での connect 呼び出しによって接続要求が行われ、それをサーバー側で accept が受け取るという順序になります。
サーバの準備
| メソッド | 実行側 | 役割 |
|---|---|---|
| bind() | サーバー | ソケットを特定のファイルパス(UNIXドメインソケット)に関連付ける |
| listen() | サーバー | 接続要求を受け付ける準備を整え、受付キューを準備する サーバーはこの状態になって初めてクライアントからの接続を受け入れられる |
接続要求と受付
| メソッド | 実行側 | 役割 |
|---|---|---|
| accept() | サーバー | クライアントからの接続要求がキューに入るのを待つ このメソッドがブロック(待機)している状態 |
| connect() | クライアント | サーバーのファイルパスに向けて接続要求を送信 |
| accept() の完了 | サーバー | クライアントの connect() 要求がサーバーのキューに入ると、accept() がそれを処理し、新しい接続用ソケットを生成してブロッキングを解除する |
参考URL
Discussion