私はOSX 10.11.6を使用しており、起動時に通常はUDPポート8008
でリッスンするプログラムを実行しようとしています。
このプログラムは通常、操作中にいくつかのヘルパー子プロセスも生成しますが、ポートは親プロセスによってバインドされます。
残念ながら、プログラムを終了すると、プログラム(親+子)が存在しなくなっても、ポートが開いたままになることがあります。
これが発生した場合、プログラムを再度実行しようとすると、当然EADDRINUSE
エラーで失敗します。これらの場合、何を試しても、私が見つけた唯一の解決策はマシンを再起動することでした。
再起動せずにポートを解放できないと信じるのに苦労しています。
これまでに実行したいくつかの診断を次に示します(これらすべてをSudo
の有無にかかわらず実行しました)。
ポート8008
とlsof
を使用してプロセスを見つけます。
$ lsof -i -n -P | grep UDP | grep 8008
しかし、驚くべきことに、結果は返されません。
しかし、私はnetstat
でもっと運が良かった:
$ netstat -tulnvp udp | grep 8008
udp4 0 0 *.8008 *.* 196724 9216 47205 0
したがって、ポートは実際にバインドされており、原因はpid 47205
ですが、次のようになります。
$ ps aux | grep 47205
何も返しません。 PID 47206
と47207
(最も確実に子に割り当てられたPID)についても同じことが言えます。 grep
の他のバリエーション(プログラム名、パスなど)も試しました。
また、親として47205
を報告するプロセスも探しました。
$ ps -axo pid,ppid,command | grep 47205
したがって、子プロセスも明らかに死んでいます。
何もkill
できないので、ゾンビの子プロセスを削除する可能性があることを期待して、launchd
をSIGHUPしようとしました。
$ Sudo kill HUP 1
$ Sudo kill -s HUP 1
しかし残念ながら、netstat
はまだポートバウンドを示しています。
最後に、ループバックインターフェイスを再起動しようとしました。
$ Sudo ifconfig lo down
$ Sudo ifconfig lo up
しかし、繰り返しますが、効果はありません。
プログラムが最後に実行されてから数時間待っていたので、タイムアウトが発生したことは間違いありませんが、ポートは解放されません。
再起動せずにポートを強制的に解放する方法についてのアイデアはありますか?
編集:
マシンを再起動せずに手動でポートを閉じることは確かに可能です。さまざまなLinuxフレーバーでは、これは通常、プロセスを装ったsyscallを発行することによってGDBで実行されます(たとえば、ソケットファイル記述子のclose(fd)
syscall)。
そのためのプロセス:
netcat -u 127.0.0.1 33333
_。netstat -npu (u for UDP)
。これにより、そのポートを占有するPIDが得られます。lsof -np $pid
_を実行して、ソケットのファイル記述子を取得します。Sudo gdb -p 73599
_call close(file_descriptor)
を実行します例:
_COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
netcat 73599 ubunt cwd DIR 259,2 4096 13895497 /home/ubunt/Downloads
netcat 73599 ubunt rtd DIR 259,2 4096 2 /
netcat 73599 ubunt txt REG 259,2 31248 28835938 /bin/nc.openbsd
netcat 73599 ubunt mem REG 259,2 47600 23990813 /lib/x86_64-linux-gnu/libnss_files-2.23.so
netcat 73599 ubunt mem REG 259,2 1868984 23990714 /lib/x86_64-linux-gnu/libc-2.23.so
netcat 73599 ubunt mem REG 259,2 101200 23990866 /lib/x86_64-linux-gnu/libresolv-2.23.so
netcat 73599 ubunt mem REG 259,2 81040 23990710 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
netcat 73599 ubunt mem REG 259,2 162632 23990686 /lib/x86_64-linux-gnu/ld-2.23.so
netcat 73599 ubunt 0u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 1u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 2u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 3u IPv4 22142418 0t0 UDP 127.0.0.1:45255->127.0.0.1:33333
_
次にGDB:
_$Sudo gdb -p 73599
...
(gdb) call close(3u)
$1 = 0
_
ポートがもう存在しないことがわかります。
_ubunt@ubunt-MS-7A94:~$ lsof -np 73599
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
netcat 73599 ubunt cwd DIR 259,2 4096 13895497 /home/ubunt/Downloads
netcat 73599 ubunt rtd DIR 259,2 4096 2 /
netcat 73599 ubunt txt REG 259,2 31248 28835938 /bin/nc.openbsd
netcat 73599 ubunt mem REG 259,2 47600 23990813 /lib/x86_64-linux-gnu/libnss_files-2.23.so
netcat 73599 ubunt mem REG 259,2 1868984 23990714 /lib/x86_64-linux-gnu/libc-2.23.so
netcat 73599 ubunt mem REG 259,2 101200 23990866 /lib/x86_64-linux-gnu/libresolv-2.23.so
netcat 73599 ubunt mem REG 259,2 81040 23990710 /lib/x86_64-linux-gnu/libbsd.so.0.8.2
netcat 73599 ubunt mem REG 259,2 162632 23990686 /lib/x86_64-linux-gnu/ld-2.23.so
netcat 73599 ubunt 0u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 1u CHR 136,19 0t0 22 /dev/pts/19
netcat 73599 ubunt 2u CHR 136,19 0t0 22 /dev/pts/19
_
GDBはMacOSで利用できるので、あなたのケースでも機能するはずです。
コードで、ソケットを作成した後、bind
呼び出しの前に、以下を呼び出します。
int val = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
次に、bind
を呼び出します。上記により、ポートが使用中の場合でもソケットバインドが成功します。
同じポートでrecvfrom
を試行する2つのプロセスでは、一方のプロセスがパケットを受信し、もう一方のプロセスは受信しません。そして、どちらを選択するかは決定論的ではありません。したがって、実際に2つのプロセスが合法的に実行され、ポートを共有していないことを確認してください。
システムは、I/Oプロセスがまだ進行中になるまでソケットを開いたままにする場合があります。プロセスが停止したが、明示的にソケットを閉じていない場合でも。ソケットが何時間も閉じられていない場合は、おそらく何かが足りません。 netstatやlsofなどのトップレベルのユーティリティではなく、低レベルのカーネル調査を使用してみてください。
免責事項
私はOSXの専門家ではなく、Linux用のほとんどのコマンドです。他の誰かが同じ問題を抱えている場合、私はまだそこに置いておきます。
1。ソケットがまだ生きているかどうかを確認してください(オプション)
ソケット通信を確認することをお勧めします。
tcpdump -A -s0 port 8080 and tcpdump -A -s0 -ilo port 8080
ソケットを介して転送されたデータが表示された場合は、プロセスがアクティブであることを確認できます。またはその子の1つである可能性があります。後で strace でpidをキャッチできます
2。プロセスとそのステータスを確認してください
Linuxには素晴らしいprocfsがあります。そこからたくさんのものを手に入れることができます。そして、開いているすべてのファイル記述子を確認できます
ls -al /proc/47205/fd
出力が表示され、/ proc/47205が存在する場合でも、解放されていないpidはpsと表示されます。開いているすべてのファイルとそのfdsが表示されます。
133->ソケット:[32242509]
ここで、133はfd番号です。
残念ながら、OSXには/ procファイルシステムがありません。私が見つけた代替コマンド。
procexp 47205 fds
しかし、100%機能するかどうかはわかりません。
3。別のプロセスでファイル記述子(ソケット)を閉じる
LinuxにはNiceコマンドがあります
fuser -k -n udp 8080
これにより、ポートをブロックしているすべてのプロセスが明示的に閉じられます。 OS Xのようです フューザーもあるかもしれません
@MindaugasBernatavičiusが書いたように、ファイル記述子番号はプロセス環境でのみ有効であるため、もう1つの実際のハッカーの方法は、gdbを使用してプロセスに接続し、プロセス内でコマンドを実行することです。
gdb -p 47205
>call shutdown([fd_number],2)
>call close([fd_number])
3番目の方法があり、可能な場合はネットワーク全体を再起動できます。ループバックインターフェイスだけでは十分ではありません。 Linuxで実行
systemctl restart network
4。システムでソケットがスタックするのを防ぐために何をすべきか
プログラムが終了する前に、常にsockedが閉じていることを確認する必要があります。 nodejsで多くの問題が発生しました ソケットは開いたままです。 Socket.destroy()を呼び出すと問題が解決します
アプリを終了する前に、ここにソケット破棄コードを配置できます。
app.on( 'close'、function(code){
//ユーザーがアプリを閉じました。ホストプロセスを強制終了します。
process.exit();
});
1つの関連する質問:macはSO_REUSEADDRとSO_REUSEPORTの動作を変更しました:
SO_REUSEADDRとSO_REUSEPORTの動作が変更されましたか?
私はiptux [1]のメンテナです。SO_REUSEPORTを使用すると、プログラムを起動できますが、このポートからメッセージを受信できず、すべてのメッセージがブラックホールとして閉じられていないポートに送信されます。
あなたの質問は次のようになります:
あなたが言ったように:
最後に、ループバックインターフェイスを再起動しようとしました。
$ Sudo ifconfig lo down
$ Sudo ifconfig lo up
ループバックだけでなく、使用可能なすべてのネットワークインターフェイス(lanまたはwlan)を再構築しようとしましたか?
ifconfig
の代わりに、ネイティブMacOSコマンドユーティリティ( ここ から)を使用して、デバイス自体の電源をオフにしてからオンにすることもできます(adapt en0
からyour device name
):
networksetup -setairportpower en0 off
networksetup -setairportpower en0 on
最後に、次の方法でDHCPのリリースと更新を試みることもできます。
Sudo dhclient -v -r
よろしく