Отпустите UDP-порт, используемый мертвым процессом в OS X
Я нахожусь в OS X 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
ничего, я попытался выполнить SIGHUP launchd
в надежде, что он может удалить любые дочерние процессы zombie:
$ sudo kill HUP 1
$ sudo kill -s HUP 1
Но, увы, netstat
все еще показывает границу порта.
Наконец, я попытался перезапустить интерфейс loopback:
$ sudo ifconfig lo down
$ sudo ifconfig lo up
Но опять же, без эффекта.
Я ждал несколько часов с момента последнего запуска программы, поэтому я уверен, что любой тайм-аут случится к настоящему времени, но порт просто не освободится.
Любые идеи о том, как принудительно освободить порт без перезагрузки?
Edit:
- Рассматриваемая программа - обернутая электронами Patchwork.
- Этот вопрос исходит из этой проблемы github.
- Хотя найти решение/исправление, которое в первую очередь предотвратит возникновение проблемы, должно быть идеально, меня также интересуют способы вручную закрыть этот порт из терминала.
Ответы
Ответ 1
В вашем коде после создания сокета, но перед вызовом bind
вызовите следующее:
int val = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
Затем вызовите bind
. Вышеупомянутое позволит привязке сокета к успеху, даже если порт используется.
Два процесса, пытающиеся выполнить recvfrom
на одном и том же порту, приведут к тому, что один из процессов получит пакет, но не другой. И это не детерминистично, какой будет. Поэтому убедитесь, что на самом деле у вас нет двух законно работающих процессов и совместного использования порта.
Ответ 2
Действительно, можно закрыть порт вручную без перезагрузки машины. В различных вариантах linux это обычно делается w/GDB, выдавая syscalls, маскируясь как процесс (например, close(fd)
syscall в дескрипторе файла сокетов).
Процесс для этого:
- Откройте UDP-порт:
netcat -u 127.0.0.1 33333
.
- Проверьте порт UDP:
netstat -npu (u for UDP)
, который даст вам PID, который занимает этот порт.
- Запустите:
lsof -np $pid
для этого PID, чтобы получить filedescriptor для сокета.
- Затем запустите GDB для этого PID:
sudo gdb -p 73599
- Когда внутри GDB запускается
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
Вы увидите, что порт больше не существует:
[email protected]:~$ 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, поэтому он также должен работать для вашего дела.
Ответ 3
Система может держать розетку открытой до тех пор, пока процесс ввода-вывода не будет выполнен. Даже когда процесс умер, но явно не закрыл сокет. Если ваш сокет не закрыт в часы, скорее всего, вы что-то упустили. Попытайтесь использовать низкоуровневое исследование ядра вместо использования верхнего уровня, например netstat или lsof.
Отказ
Я не эксперт OS X, и большинство команд для Linux. Я все еще оставляю его там, если у кого-то будет такая же проблема.
1. Попытайтесь узнать, жив ли сокет (необязательно)
Я могу предложить проверить связь сокетов.
tcpdump -A -s0 port 8080 and tcpdump -A -s0 -ilo port 8080
Если вы видите какие-либо данные, переданные через сокет, вы можете быть уверены, что этот процесс активен. Или может быть одним из его детей. Позже вы можете поймать pid strace
2. Проверьте процесс и его статус
У Linux есть замечательные procfs. Оттуда вы можете получить так много вещей. И обязательно вы увидите все открытые дескрипторы файлов
ls -al /proc/47205/fd
Если вы видите вывод и /proc/ 47205 существует, pid не выдается, тем не менее, ps показывает. Вы увидите все открытые файлы и его fds.It выглядит как
133 → socket: [32242509]
Где 133 - число fd
К сожалению, в ОС X нет файловой системы /proc. Альтернативная команда, которую я нашел.
procexp 47205 fds
Но я не уверен, что он работает на 100%.
3. Закрытие дескриптора файла (сокета) в другом процессе
В linux есть приятная команда
fuser -k -n udp 8080
Это явно закрывает все блокирующие порты процессов. Кажется, OS X может содержать фьюзер
Другим реальным способом хакеров является подключение к процессу с помощью gdb и запуск команд внутри процесса, поскольку номера дескрипторов файлов действительны только в рабочей среде, точно так же, как @Mindaugas Bernatavičius написал:
gdb -p 47205
>call shutdown([fd_number],2)
>call close([fd_number])
Существует третий способ, когда это возможно, вы можете просто перезапустить всю сеть. Plese отметить, вниз и вверх только loopback интерфейс не достаточно. В linux run
systemctl restart network
4.Что делать, чтобы предотвратить застревание сокета в системе
Вы всегда должны быть закрыты, пока ваша программа не выйдет. Я видел много проблем с nodejs, что сокеты остаются открытыми. Вызов Socket.destroy() решит проблему
Возможно, вы потеряете код сокета, прежде чем выйти из приложения:
app.on('close', function (code) {
//Пользователь закрыл приложение. Убейте хост-процесс.
process.exit();
});
Ответ 4
Ваш вопрос выглядит так:
Как вы сказали:
Наконец, я попытался перезапустить интерфейс loopback:
$sudo ifconfig lo down
$sudo ifconfig lo up
Вы пытались перенаправить все доступные сетевые интерфейсы (lan или wlan), а не только loopback)?
Вместо ifconfig
вы можете использовать также собственную служебную утилиту MacOS (от здесь), чтобы выключить питание, затем включить питание самого устройства ( адаптируйте en0
в your device name
):
networksetup -setairportpower en0 off
networksetup -setairportpower en0 on
Наконец, вы также можете попытаться освободить и обновить DHCP с помощью
sudo dhclient -v -r
Привет