HOWTO · Python
ConnectionRefusedError: [Errno 111] Connection Refused の修正方法
この記事では、ConnectionRefusedError とは何かを学びます。このエラーは基本的に、サーバースクリプトを実行しているシステムのポートにクライアントが接続できないことを示します。
このページの内容
ConnectionRefusedError: [Errno 111] Connection refused は、TCP 接続の試行が宛先に拒否されたことを意味します。一般的には、そのアドレスとポートで接続を受け付けるサービスが存在しないことが原因です。ping が成功しても、ホストがネットワークのプローブに応答する可能性があることを示すだけで、特定の TCP サービスが待ち受けていることの確認にはなりません。
Python で ConnectionRefusedError: [Errno 111] Connection refused が発生する理由
要求された TCP エンドポイントが接続を拒否すると、オペレーティングシステムは Python に拒否を報告します。まず、サーバープロセスが実行中で待ち受けているかを確認してください。次に、クライアントが使用する正確なアドレスとポートを、サーバーの待ち受けアドレスとポートと比較してください。127.0.0.1 のみにバインドされたサーバーは、自マシンからの接続は受け付けますが、別のコンピューターからの接続は受け付けません。ファイアウォールが接続を拒否することもあります。
これは、TCP 接続を試行する前に発生するホスト名検索の失敗とは異なります。タイムアウトやルーティングの失敗は通常、クライアントが拒否応答を受け取らなかったことを意味します。誤ったアドレスはネットワークによって異なる結果を招くことがあり、それだけでこのエラーが保証されるわけではありません。
拒否された Python ソケット接続を診断して修正する
まずサーバーを確認し、次に両側で宛先アドレスとポートを検証してください。サーバーが待ち受けるインターフェースを確認します。同一マシンのクライアントにはループバックを、別のホストには意図したネットワークインターフェースを使用してください。0.0.0.0 にバインドするとすべての IPv4 インターフェースで待ち受けるため、サービスが他のマシンに公開される可能性があります。構成に合う最も狭いインターフェースを選んでください。サービスが想定したエンドポイントで待ち受けている場合は、ファイアウォールのルールを確認してください。
この自己完結型の例は、バックグラウンドスレッドでローカルサーバーを起動し、同じループバックポートにクライアントを接続し、バイト文字列を転送します。ポートはオペレーティングシステムが割り当てるため、両方のソケットが、別のファイルやシェルコマンドなしに同じエンドポイントを使用します。クライアントは既知の応答長を受け取るまで読み取ります。サーバーがそれより早く閉じた場合は、部分的なデータを完全なメッセージとして提示するのではなく、応答が不完全であったことを報告します。
import socket
import threading
with socket.socket() as server:
server.bind(("127.0.0.1", 0))
server.listen()
port = server.getsockname()[1]
def serve():
connection, _ = server.accept()
with connection:
connection.recv(1024)
connection.sendall(b"hello from server")
worker = threading.Thread(target=serve)
worker.start()
with socket.create_connection(("127.0.0.1", port)) as client:
client.sendall(b"hello from client")
response_length = len(b"hello from server")
response = bytearray()
complete = True
while len(response) < response_length:
chunk = client.recv(response_length - len(response))
if not chunk:
complete = False
break
response.extend(chunk)
if complete:
print(response.decode())
else:
print("Server closed before the full response arrived")
worker.join()
2 つの別々のプログラムの場合は、クライアントより先にサーバーを起動し、accept() で待機している間サーバープロセスを実行し続けてください。両方のプログラムに同じポートを設定し、クライアントには自マシンから到達可能なアドレスへ接続させてください。127.0.0.1 は両方のプログラムが同じホストで動作している場合にだけ使用してください。リモートクライアントには、サーバーマシンの到達可能なインターフェースアドレスを使用し、アクセスを意図的に構成してください。上記の例は成功するやり取りを 1 つのプロセスで示すデモンストレーションであり、公開向けのリスナーを構成するものではありません。
ホスト名だけが失敗する場合は、名前解決を別に調査してください。socket.gethostbyname("example.com") は IPv4 アドレスを検索するもので、サービスを起動したり、拒否された接続を修復したりするものではありません。ホスト名が解決できたら、返されたアドレスがサーバーが待ち受けているアドレスであることと、クライアントがサーバーのポートを使用していることを確認してください。
まとめ
ConnectionRefusedError は、拒否された TCP 接続を示します。サーバーが実行中で、意図したインターフェースで待ち受け、クライアントが対象とするのと同じアドレスとポートを使用していることを確認してください。そのうえでファイアウォールのルールを確認し、ホスト名解決は別の診断として扱ってください。