通信プログラムを開発していると度々発生する Address already in use
。この例外が発生する原因と、その解決方法について解説していきます。
ちなみに、この例外は下記のようなメッセージで発生することもありますし、
OSError: [Errno 98] Address already in use
下記のようなメッセージで発生することもあります。
OSError: [WinError 10048] 通常、各ソケット アドレスに対してプロトコル、ネットワーク アドレス、またはポートのどれか 1 つのみを使用できます。
メッセージが異なるのはプログラムが実行されている OS が異なるからで、原因は全て同じになります。以降では、上記の例外をまとめて Address already in use
で記して説明していきます。
Contents
原因
まず、この Address already in use
は主に TCP でソケット通信を行うサーバープログラムで発生する例外で、サーバープログラムが bind
を実行するときに発生することが多いです。クライアントプログラムでも発生することはあるのですが、主に発生するのがサーバープログラムであるため、このページではサーバープログラムが bind
を実行したときに発生する Address already in use
に限定して解説をしていきます。
で、この Address already in use
の例外がプログラム実行時に発生する原因は、そのプログラムが「同じ通信端末上で動作している他のプログラムによって既にソケットに bind
されている IP アドレス&ポートと “同じ IP アドレス&ポート” に bind
したから」になります。要は、同じ通信端末上で動作する複数のプログラムが同一の IP アドレス&ポート番号を bind
しようとした時に発生する例外になります。
通信端末上で動作しているサーバープログラムは、データを受信するために、特定のネットワークインターフェース(全てのネットワークインターフェースでも可能)・ポートに対して受信待ちを行います。そして、その待ちを行うネットワークインターフェース・ポートを指定するために、そのネットワークインターフェースの IP アドレスとポート番号を引数に指定して bind
を実行することになります。
# sockはsocket.socketクラスのインスタンス
sock.bind((IPアドレス, ポート番号)
このように bind
を行った後に recv
や recvfrom
等でデータの受信待ちを行えば、bind
した IP アドレスが割り振られたネットワークインターフェースとポート番号のポートに対してデータが送信されてくると、その bind
を実行したソケットを持つプログラムが送信されてきたデータを受信することができます。
例えば、socket1.bind(('192.168.10.100', 40001))
を実行して socket1
が recv
メソッド等で受信待ちを行えば、IP アドレス 192.168.10.100
のポート 40001
宛のデータが送信された際に socket1
がそのデータを受信することになります。
このように、IP アドレスとポート番号に対して bind
することで、特定のネットワークインターフェース&ポート宛に送信されたデータを正しく目当てのソケット・プログラムが受信できるようになっています。
このあたりは下記ページで解説していますので、詳細に関しては下記ページをご参照いただければと思います。
Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)原因:使用しようとしたポートが既に使用中
ですが、同じ IP アドレスとポートで bind
したソケットが同じ端末上に複数存在すると、その IP アドレス&ポート宛にデータが送信されてきたときに、どのソケットがデータを受け取るべきかを判断できなくなることになります。そのため、基本的には複数のソケットに対して同じ IP アドレスとポートで bind
するのは NG です。
なので、同じ通信端末上で、複数のソケットで同じ IP アドレス&ポートを bind
しようとすると例外が発生するようになっています。そして、この時に発生する例外が Address already in use
になります。つまり、 Address already in use
が発生したということは、bind
しようとした IP アドレスとポートが既に同じ通信端末上で bind
されているということになります。
特に、通信端末上には複数のネットワークインターフェースが備えられているものの、このネットワークインターフェースの数は限られており、PC の場合、多くても10個程度なのではないかと思います。また、このネットワークインターフェースは通信経路(有線 LAN や無線 LAN など)によって決まるものであり、他のソケットの bind
先の IP アドレス(ネットワークインターフェースに割り当てられた IP アドレス)と重複するのは当然のことと言えば当然です。
なので、どちらかというと、Address already in use
が発生するのは、bind
するポートが重複していることが問題であると考えて良いと思います。そのため、ここからはポートにのみ注目して説明を行っていきます。また、プログラムがソケットに対して特定のポートに bind
することを、以降では、そのプログラムが “そのポートを使用している” という表現をしていきます。
つまり、Address already in use
が発生するのは、プログラムが使用しようとしているポートが、他のプログラムによって既に使用されていることが原因であることになります。
スポンサーリンク
解決のポイント:そのポートが既に使用中である理由
Address already in use
の例外が発生する理由については、なんとなくでも理解していただけたのではないでしょうか?
この例外が発生したときは、少なくとも、そのプログラムが使用しようとしたポートが既に他のプログラムによって使用中であることは確定と考えて良いです。でも、そのポートが既に使用中である理由は何なのでしょうか?
次にポイントになるのは、この点になります。この例外の解決方法は、ポートが既に使用中である理由によって異なります。そして、そのポートが既に使用中である理由としては、主に下記の3つが挙げられると思います。ここからは、下記の3つの原因ごとに解決方法を説明していきたいと思います。
- そのポートを他のアプリが既に使用している
- プログラムが2重で起動された
- そのポートを自身のプログラムが使用中のままになっている
ここからは、上記の3つの理由ごとに、解決方法について解説していきたいと思います。
解決方法
では、本題である Address already in use
の例外の解決方法について解説していきます。
前述のとおり、この例外がプログラム実行時に発生するのは、既に使用中のポートを、そのプログラムが使用しようとしたからになります。
そして、そのポートが既に使用中である理由によって解決方法が異なります。ということで、理由毎に解決方法を解説していきます。
そのポートを他のアプリが既に使用している
まず、プログラムが使用するポートとして “他のアプリ・プログラムが既に使用中のポート” を選択してしまうと Address already in use
が発生することになります。この現象に関しては、ここまでの解説を読んでくださった方であればすんなりと理解していただけると思います。
この場合、プログラムが使用するポートを別のものに変更してやれば例外が解決することになります。そして、この時には通信端末上(PC やスマホなど)で使用されていないポートに変更する必要があります。
では、通信端末上で使用されていないポートを調べるためにはどうすればよいでしょうか?
この点について補足しておくと、まず、通信端末上で利用されているポートは、Windows の場合は PowerShell 等で netstat
コマンドを実行すれば、また Mac や Linux の場合はターミナルで ss
コマンドを実行すれば確認可能です。
例えば、Windows で netstat -ano
コマンドを実行すれば、下記のような結果が得られることになります。:
の右側に記載されているのがポート番号であり、この netstat -ano
コマンドの実行結果より、どのポート番号が自身の PC 上で利用中であるのかを確認することができます。
TCP 127.0.0.1:3306 0.0.0.0:0 LISTENING 4844 TCP 127.0.0.1:33060 0.0.0.0:0 LISTENING 4844 TCP 127.0.0.1:37848 0.0.0.0:0 LISTENING 4780 TCP 127.0.0.1:45545 0.0.0.0:0 LISTENING 4844 TCP 127.0.0.1:49707 127.0.0.1:49708 ESTABLISHED 2028 TCP 127.0.0.1:49708 127.0.0.1:49707 ESTABLISHED 2028 TCP 127.0.0.1:49745 127.0.0.1:49746 ESTABLISHED 4744 TCP 127.0.0.1:49746 127.0.0.1:49745 ESTABLISHED 4744 TCP 127.0.0.1:49793 127.0.0.1:49794 ESTABLISHED 2068 TCP 127.0.0.1:49794 127.0.0.1:49793 ESTABLISHED 2068
Linux の場合は ss -a
コマンドで同様のことが確認できます(:
の後ろが文字列になっているものはシステムポート番号が割り当てられたソケットになると思います)。
udp UNCONN 0 0 127.0.0.53%lo:domain 0.0.0.0:* udp UNCONN 0 0 10.255.255.254:domain 0.0.0.0:* udp UNCONN 0 0 127.0.0.1:snmp 0.0.0.0:* udp UNCONN 0 0 127.0.0.1:323 0.0.0.0:* udp UNCONN 0 0 [::1]:snmp [::]:* udp UNCONN 0 0 [::1]:323 [::]:* tcp LISTEN 0 1000 10.255.255.254:domain 0.0.0.0:* tcp LISTEN 0 4096 127.0.0.53%lo:domain 0.0.0.0:* tcp LISTEN 0 511 127.0.0.1:45545 0.0.0.0:* tcp LISTEN 0 70 127.0.0.1:33060 0.0.0.0:* tcp LISTEN 0 151 127.0.0.1:mysql 0.0.0.0:* tcp ESTAB 0 0 127.0.0.1:45545 127.0.0.1:40092 tcp ESTAB 0 0 127.0.0.1:45545 127.0.0.1:40076 tcp ESTAB 0 0 127.0.0.1:40092 127.0.0.1:45545 tcp ESTAB 0 0 127.0.0.1:40076 127.0.0.1:45545
こんな感じで、通信端末上で使用されているポートはコマンドから調べることができます。あとは、そこにリストアップされていないポートをプログラムで使用する(bind
する)ようにしてやれば良いことになります。
スポンサーリンク
プログラムが2重で起動された
先ほどは、2つの異なるプログラムやアプリ間で同じポートを使用していた場合に Address already in use
の例外が発生するという話でしたが、別に異なるプログラム・アプリに限った話ではなく、同じプログラムを2重で起動した場合にも Address already in use
の例外が発生することになります。
例えば、ポート 40001
を固定で使用するプログラムを起動している最中に、そのプログラムを再度起動した場合、2つのプログラム(プロセス)がポート 40001
を使用することになって、後から起動したプログラムで Address already in use
の例外が発生することになります。
プログラム開発中だと起こりやすい現象で、動作確認をするためにプログラムを起動しておいたのに、それを忘れて再度プログラムを起動してしまったような場合に Address already in use
の例外が発生してしまいます。特にサーバープログラムは常駐するように作ることが多いので、終了させるのを忘れて再度プログラムを起動してこの例外が発生してしまったということを経験した方は多いのではないかと思います。
この場合は、余分にプログラムを起動させてしまっているのが問題なので、一方のプログラムを終了させてやれば良いだけです。まずは、同じプログラムを起動しっぱなしになっていないかを確認してみましょう!
そのポートを自身のプログラムが使用中のままになっている
ただし、プログラムを終了したとしてもポートが使用中のままの状態になってしまうことがあり、この場合はプログラムを終了させた後に再度プログラムを起動しても Address already in use
の例外が発生することになります。
ソケットをクローズしたりプログラムを終了したりしたとしても、すぐにソケットは消滅せず、そのポートを利用中のソケットが TIME_WAIT
状態で一定時間残り続けることがあります。この場合、そのポートは使用中としてみなされるため、同じポートを使用するプログラムを起動すると Address already in use
の例外が発生します。
例えば、開発中のサーバープログラムの動作確認を行うために起動や終了を繰り返していると、ちゃんとプログラムは終了させているのに次回起動時に Address already in use
の例外が発生するようなことがあります。
このケースにおいての解決策の1つは、「プログラム終了後は、ある程度時間を空けてから再度プログラムを起動する」になります。毎回待つのも面倒なので、Address already in use
の例外が発生したときのみ、ある程度時間を空けてから再度プログラムを起動するので良いと思います。TIME_WAIT
状態で残っているソケットは一定時間経てばクローズされて消滅するため、時間を空ければ Address already in use
の例外も発生しなくなります。
OS や、その設定によっても異なるのですが、例えば Windows の場合は TIME_WAIT
状態のソケットは2分ほどで削除されるようになっているようです。そのため、上記が原因で Address already in use
の例外が発生した場合は、2分ほど待ってからプログラムを起動してやれば例外が発生しなくなるはずです。Linux の場合は60秒に設定されていることが多いと思います。とりあえず、上記の原因で例外が発生する場合は時間を空けてから再度プログラムを起動すれば例外が発生しなくなると思います。
待つのが面倒であれば、ソケットの SO_REUSEADDR
オプションを ON にしてやるという手もあります。このオプションが ON に設定されている場合、既に同じポートを使用しているソケットが存在しても、そのソケットの状態が TIME_WAIT
であれば Address already in use
の例外が発生せずに bind
が成功するようになります。なので、プログラム終了後にソケットが TIME_WAIT
状態で存在していても、そのソケットが消滅するのを待つことなく、再度プログラムを起動して動作確認を行うことが可能となります。
このようなソケットのオプションは、Python の socket
モジュールの場合、socket
クラスの setsockopt
メソッドを利用することで変更することが可能です。SO_REUSEADDR
オプションを ON にするのであれば、bind
実行前に setsockopt
メソッドを実行する必要があるので注意してください。
setsockopt
メソッドで SO_RESUSEADDR
オプションを ON にするスクリプトの例は下記となります。最初に説明したように、Address already in use
の例外はサーバー側で発生することが多いため、サーバープログラムの例を示しています。
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# SO_REUSEADDRをONにする
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('0.0.0.0', 40001))
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()
ポイントは bind
の直前で setsockopt
メソッドを実行して SO_REUSEADDR
オプションを ON にしている点で、このようにオプションを設定しているため、このスクリプトが終了してソケットが TIME_WAIT
状態のままで残存してしまっていても、次回スクリプト実行時には bind
で Address already in use
の例外は発生しません。
その他の部分に関しては下記ページの TCP 通信のサーバー で示したスクリプトと全く一緒です。TCP 通信のサーバー でスクリプトの解説も行っていますので、不明点があればリンク先の解説を参照していただければと思います。
Pythonでのソケット通信(ポート番号・プロトコル・サーバー / クライアント)一点補足しておくと、SO_REUSEADDR
オプションを ON にした時の動作は Windows の場合は少し特殊で、Linux や Mac の場合は TIME_WAIT
状態でないソケットが同じポートを利用している場合、そのポートに対して bind
を行うと Address already in use
の例外が発生することになります。例えば、上記のスクリプトを2重で起動すると bind
実行時に Address already in use
の例外が発生します。それに対し、Windows の場合はソケットの状態に関わらず同じポートへの bind
が成功することになるようです(多分)。つまり、同じポートで受信待ちを行うソケットが1つの端末上に複数存在することになり、これによって、そのポート宛のデータのソケット間での取り合いが発生することになります。もちろん、Address already in use
の例外を防ぐという目的は達成できますが、意図した相手とのデータの送受信が行えなくなる可能性もあるので注意してください。
まとめ
このページでは、Address already in use
の例外が発生する原因および、本例外の解決方法について解説しました!
Address already in use
は、同一端末上で複数のソケットが同じ IP アドレス&ポートに対して bind
した際に発生する例外になります。複数のソケットが同じ IP アドレスに対して bind
されることは多いため、どちらかというと同じポートに対して bind
が行われることが原因と考えて良いと思います。
この例外が発生したからといって焦る必要はなく、まずは自身の開発しているプログラムが他のアプリ・プログラムと同じポートを使用しようとしていないか、また、同じ通信プログラムを2重で起動してしまっていないかどうかを確認してみましょう。
時々、上記のようなケースに当てはまらず、通信プログラムをちゃんと終了してから再起動したにもかかわらず Address already in use
の例外が発生することもあります。この場合は時間を空けてからプログラムを再起動すればよいですし、SO_REUSEADDR
オプションを ON にして時間を空けることなく Address already in use
の例外の発生を防ぐことも可能です。
ソケット通信プログラムのプログラミングを行っていると例外が発生することも多いかと思いますが、焦らず1つ1つ解決していけばよいだけです!是非、このページの内容も覚えておき、例外発生時に正しく解決できるようにしておきましょう!