【Python/ソケット通信】「Address already in use」の原因と解決方法

address already in use例外が発生する原因と解決方法の解説ページアイキャッチ

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

通信プログラムを開発していると度々発生する Address already in use。この例外が発生する原因と、その解決方法について解説していきます。

ちなみに、この例外は下記のようなメッセージで発生することもありますし、

OSError: [Errno 98] Address already in use

下記のようなメッセージで発生することもあります。

OSError: [WinError 10048] 通常、各ソケット アドレスに対してプロトコル、ネットワーク アドレス、またはポートのどれか 1 つのみを使用できます。

メッセージが異なるのはプログラムが実行されている OS が異なるからで、原因は全て同じになります。以降では、上記の例外をまとめて Address already in use で記して説明していきます。

原因

まず、この Address already in use主に TCP でソケット通信を行うサーバープログラムで発生する例外で、サーバープログラムが bind を実行するときに発生することが多いです。クライアントプログラムでも発生することはあるのですが、主に発生するのがサーバープログラムであるため、このページではサーバープログラムが bind を実行したときに発生する Address already in use に限定して解説をしていきます。

で、この Address already in use の例外がプログラム実行時に発生する原因は、そのプログラムが「同じ通信端末上で動作している他のプログラムによって既にソケットに bind されている IP アドレス&ポートと “同じ IP アドレス&ポート” に bind したから」になります。要は、同じ通信端末上で動作する複数のプログラムが同一の IP アドレス&ポート番号を bind しようとした時に発生する例外になります。

同じIPアドレス・ポート番号へのbindによってAddress already in useが発生する様子

通信端末上で動作しているサーバープログラムは、データを受信するために、特定のネットワークインターフェース(全てのネットワークインターフェースでも可能)・ポートに対して受信待ちを行います。そして、その待ちを行うネットワークインターフェース・ポートを指定するために、そのネットワークインターフェースの IP アドレスとポート番号を引数に指定して bind を実行することになります。

bind
# sockはsocket.socketクラスのインスタンス
sock.bind((IPアドレス, ポート番号)

このように bind を行った後に recvrecvfrom 等でデータの受信待ちを行えば、bind した IP アドレスが割り振られたネットワークインターフェースとポート番号のポートに対してデータが送信されてくると、その bind を実行したソケットを持つプログラムが送信されてきたデータを受信することができます。

例えば、socket1.bind(('192.168.10.100', 40001)) を実行して socket1recv メソッド等で受信待ちを行えば、IP アドレス 192.168.10.100 のポート 40001 宛のデータが送信された際に socket1 がそのデータを受信することになります。 

bindによって受信するデータが決まる様子

このように、IP アドレスとポート番号に対して bind することで、特定のネットワークインターフェース&ポート宛に送信されたデータを正しく目当てのソケット・プログラムが受信できるようになっています。

このあたりは下記ページで解説していますので、詳細に関しては下記ページをご参照いただければと思います。

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

原因:使用しようとしたポートが既に使用中

ですが、同じ IP アドレスとポートで bind したソケットが同じ端末上に複数存在すると、その IP アドレス&ポート宛にデータが送信されてきたときに、どのソケットがデータを受け取るべきかを判断できなくなることになります。そのため、基本的には複数のソケットに対して同じ IP アドレスとポートで bind するのは NG です。

同じIPアドレス・ポートにbindされたソケットが複数存在する場合のデータの受信

なので、同じ通信端末上で、複数のソケットで同じ IP アドレス&ポートを bind しようとすると例外が発生するようになっています。そして、この時に発生する例外が Address already in use になります。つまり、 Address already in use が発生したということは、bind しようとした IP アドレスとポートが既に同じ通信端末上で bind されているということになります。

同じIPアドレス・ポート番号へのbindによってAddress already in useが発生する様子

特に、通信端末上には複数のネットワークインターフェースが備えられているものの、このネットワークインターフェースの数は限られており、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 の例外が発生することになります。

同じプログラムを2重で起動して例外が発生する様子

プログラム開発中だと起こりやすい現象で、動作確認をするためにプログラムを起動しておいたのに、それを忘れて再度プログラムを起動してしまったような場合に 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秒に設定されていることが多いと思います。とりあえず、上記の原因で例外が発生する場合は時間を空けてから再度プログラムを起動すれば例外が発生しなくなると思います。

TIME_WAIT状態のソケットが消滅してからプログラムを再度起動する様子

待つのが面倒であれば、ソケットの SO_REUSEADDR オプションを ON にしてやるという手もあります。このオプションが ON に設定されている場合、既に同じポートを使用しているソケットが存在しても、そのソケットの状態が TIME_WAIT であれば Address already in use の例外が発生せずに bind が成功するようになります。なので、プログラム終了後にソケットが TIME_WAIT 状態で存在していても、そのソケットが消滅するのを待つことなく、再度プログラムを起動して動作確認を行うことが可能となります。

SO_REUSEADDRをONにしておけばTIME_WAIT状態のソケットが残っていても例外が発生しない様子

このようなソケットのオプションは、Python の socket モジュールの場合、socket クラスの setsockopt メソッドを利用することで変更することが可能です。SO_REUSEADDR オプションを ON にするのであれば、bind 実行前に setsockopt メソッドを実行する必要があるので注意してください。

setsockopt メソッドで SO_RESUSEADDR オプションを ON にするスクリプトの例は下記となります。最初に説明したように、Address already in use の例外はサーバー側で発生することが多いため、サーバープログラムの例を示しています。

SO_REUSEADDRをONにする
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 状態のままで残存してしまっていても、次回スクリプト実行時には bindAddress 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つ解決していけばよいだけです!是非、このページの内容も覚えておき、例外発生時に正しく解決できるようにしておきましょう!

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