Закрытие порта вручную из командной строки

112

Я хочу закрыть открытый порт, который находится в режиме прослушивания между моим клиентским и серверным приложением.

Есть ли какая-либо опция командной строки в Linux, чтобы закрыть порт?

ПРИМЕЧАНИЕ. Я узнал, что «только приложение, которому принадлежит подключенный сокет, должно закрывать его, что произойдет, когда приложение завершится».

Я не понимаю, почему это возможно только с помощью приложения, которое его открывает ... Но я все еще хочу знать, есть ли другой способ сделать это.

Глорфиндел
источник
5
Нет, открытые порты принадлежат процессу, который их открыл, контроль извне невозможен. Что хорошо, иначе все приложения должны будут ожидать, что их открытые порты (и файлы) будут испорчены. Однако вы можете заблокировать трафик на порт с помощью брандмауэра (iptables), но это не закроет и не даст порт для другого использования.
Юрген Штробель
10
Многие респонденты не поняли смысл этого вопроса. Бессмысленно заявлять, что только приложение, которому принадлежит порт, может отключить его. Я могу отключить его, подойдя к коробке и вытащив кабель Ethernet из розетки, или убив приложение на другом конце соединения! Приложение должно быть написано, чтобы справиться с этим. Итак --- как вы проверяете, чтобы убедиться, что приложение написано правильно, не требуя физического вмешательства и / или контроля над другим компьютером?
Дейл Уилсон
"... нет никакого контроля извне." Это важное замечание, которое подтолкнуло меня к следующему вопросу, как я могу быть частью процесса извне? GDB.
Данко Давид
@ JürgenStrobel Действительно, контроль извне возможен - и tcpkill, и ss могут делать именно то, что просят. Потому что открытые порты на самом деле не принадлежат процессу; они являются ресурсами ядра, с некоторыми правами, назначенными процессу, но все еще существующими только на усмотрение ядра.
Том Андерсон
@ tom-anderson DaleW: tcpkill - это инструмент межсетевого экрана, и я упомянул эту опцию. Вы можете предотвратить трафик на порт, который отличается от закрытия порта (сокета).
Юрген Штробель

Ответы:

134

У меня была такая же проблема, процесс должен остаться в живых, но сокет должен закрыться. Закрытие сокета в работающем процессе не является невозможным, но трудным:

  1. найдите процесс:

    netstat -np
    

    Вы получаете source/destination ip:port portstate pid/processnameкарту

  2. найдите дескриптор файла сокета в процессе

    lsof -np $pid
    

    Вы получаете список: имя процесса, pid, пользователь, fileDescriptor, ... строка подключения.

    Найдите соответствующий номер fileDescriptor для соединения. Это будет что-то вроде «97u», что означает «97».

  3. Теперь подключите процесс:

    gdb -p $pid
    
  4. Теперь закройте сокет:

    call close($fileDescriptor) //does not need ; at end.
    

    пример:

    call close(97)
    

    Затем отсоедините GDB:

    quit
    

    И розетка закрыта.

Данко Давид
источник
1
sudo lsof -np $pidдает мне около 200 строк, и я не понимаю, как найти нужный FD. В моем случае это вкладка Chrome, и я пытаюсь закрыть открытые веб-сокеты ...
SET
2
Строка , как правило , выглядит следующим образом : FireFox 14812 szupervigyor 97u IPv4 32814564 0t0 TCP 192.168.2.4:40385->173.194.39.65:https (ESTABLISHED) , как: process_name PID пользователя Fd [opened_for] Протокол устройства инф.узлов protocol_data_toString
Данко Dávid
2
* Строка, как правило, выглядит следующим образом: firefox 14812 szupervigyor 97u IPv4 32814564 0t0 TCP 192.168.2.4:40385->173.194.39.65:https (ESTABLISHED) как: process_name pid user fd [open_for] протокол устройства inode protocol_data_toString Нужно знать адрес и найти на послед. В моем примере 97 является FileDescriptor. Поиск затруднен, если вы открыли несколько подключений к целевому хосту.
Данко Давид
7
это блестящее решение
marcorossi
8
Если вы хотите , чтобы имитировать сокет закрывается на удаленном конце (например, равный выход) , то лучше использовать shutdown: call shutdown($fileDescriptor, 0).
Ecatmur
75

Вы вроде задаете неправильный вопрос здесь. На самом деле просто невозможно «закрыть порт» извне приложения, которое открыло сокет, прослушивая его. Единственный способ сделать это - полностью убить процесс, которому принадлежит порт. Затем, через минуту или две, порт снова станет доступным для использования. Вот что происходит (если вам все равно, перейдите к концу, где я покажу вам, как убить процесс, владеющий определенным портом):

Порты - это ресурсы, выделяемые ОС различным процессам. Это похоже на запрос ОС к указателю файла. Однако, в отличие от файловых указателей, порт может иметь только ОДИН процесс за раз. Через интерфейс сокета BSD процессы могут сделать запрос на прослушивание порта, который ОС затем предоставит. ОС также будет следить за тем, чтобы другие процессы не получали такой же порт. В любой момент процесс может освободить порт, закрыв сокет. Затем ОС восстановит порт. В качестве альтернативы, если процесс завершится без освобождения порта, ОС в конечном итоге восстановит порт (хотя это произойдет не сразу: это займет несколько минут).

Теперь то, что вы хотите сделать (просто закрыть порт из командной строки), невозможно по двум причинам. Во-первых, если бы это было возможно, это означало бы, что один процесс мог просто украсть ресурс другого процесса (порт). Это было бы плохой политикой, если не ограничено привилегированными процессами. Вторая причина заключается в том, что неясно, что произойдет с процессом, которому принадлежит порт, если мы позволим ему продолжить работу. Код процесса написан в предположении, что он владеет этим ресурсом. Если бы мы просто убрали его, он сам бы закончился сбоем, поэтому ОС не позволяет вам этого делать, даже если вы являетесь привилегированным процессом. Вместо этого вы должны просто убить их.

В любом случае, вот как убить процесс, которому принадлежит определенный порт:

sudo netstat -ap | grep :<port_number>

Это выведет строку, соответствующую порту удержания процесса, например:

tcp  0  0 *:8000   *:* LISTEN  4683/procHoldingPort

В этом случае procHoldingPort - это имя процесса, открывшего порт, 4683 - его pid, а 8000 (обратите внимание, что это TCP) - номер порта, который он содержит.

Затем посмотрите в последнем столбце, вы увидите /. Затем выполните это:

kill  <pid>

Если это не сработает (вы можете проверить, повторно выполнив команду netstat). Сделай это:

kill -9 <pid>

В общем, лучше не отправлять SIGKILL, если можете. Вот почему я говорю вам попробовать killраньше kill -9. Просто использование killпосылает мягче SIGTERM.

Как я уже сказал, для повторного открытия порта все равно потребуется несколько минут, если вы сделаете это. Я не знаю способ ускорить это. Если кто-то другой, я бы хотел это услышать.

Гай Авраам
источник
@smehmood - Спасибо за подробное объяснение .. Небольшое сомнение .. Как ядро ​​восстанавливает открытый порт внезапно убитым процессом ?? ... решение, которое вы предоставили, похоже, убивает процесс, удерживающий порт ...
3
@codingfreak Ядро знает, что процесс завершен. Он знает, что может восстановить порт. На самом деле существуют правила по срокам закрытия порта, чтобы убедиться, что в сети нет плавающих пакетов. Откуда оно знает, что у него есть эти ресурсы? это то, что делает ядро, отслеживает вещи.
Богатая гомолка
Пока кто-то не отправит что-нибудь разумное, как unix.tools.port.close(<my port number>)я бы использовал init 6.
Snowcrash
Это отличный ответ на другой вопрос. Этот вопрос касается присоединения отладчика и изменения запущенной программы, чтобы она вызывала функцию в дескрипторе файла.
Артур Ульфельдт
19

Фьюзер также может быть использован

fuser -k -n *protocol portno*

Здесь протокол tcp / udp, а portno - номер, который вы хотите закрыть. Например

fuser -k -n tcp 37

Больше информации на странице man fuser

RedBaron
источник
Простое убийство собственного процесса не сработало для меня, но фьюзер сработало. Спасибо!
webwurst
Я получил смешанные результаты с ним, даже после использования fuser, я получил «сокет уже используется» при попытке повторно использовать порт из убитого приложения, даже делая это как root. С другой стороны, время для освобождения розетки оказалось короче, чем раньше, так что в любом случае спасибо.
Ян Влчинский
@JanVlcinsky Может быть, есть процесс-хранитель, который перезапускает убитый процесс через несколько секунд после fuserзапуска?
RedBaron
@RedBaron: согласно комментарию superuser.com/a/415236/153413 моя проблема возникает из-за плохо написанного приложения, которое не очищает / закрывает сокет при завершении. Так что fuserнаходит процесс, использующий порт, и убивает его, но не устраняет тот факт, что сокет не закрыт. 60 секунд и ядро ​​делает это для меня.
Ян Влчинский
6

В качестве альтернативы вы можете использовать iptables:

iptables -I INPUT -p tcp --dport 80 -j DROP

Это в основном выполняет то, что вы хотите. Это отбросит весь трафик TCP на порт 80.

supercheetah
источник
4
Нет, это будет держать сокет открытым, пока все таймауты не закроют их. (Он будет скрывать весь трафик от процесса-владельца, который затем не имеет средств знать, что он должен его закрыть.) Вы можете использовать -j REJECTдля возврата флаг сброса TCP, который затем будет виден моему процессу-владельцу (но только тогда, когда другая сторона попытается это сделать). что-то отправить).
Marki555
3
netstat -anp | grep 80

Он должен сказать вам, если вы используете apache, «httpd» (это только пример, используйте порт, который ваше приложение использует вместо 80)

pkill -9 httpd 

или же

killall -9 httpd
Бен
источник
4
попробуйте обычное убийство, прежде чем прибегнуть к -9
Тило
@omfgroflmao - Но это убьет процесс, который открыл порт ??
@codingfreak Процесс, который удерживает порт, да, он убьет его.
2

Вы могли бы просто узнать, какой процесс открыл сокет, с которым связан порт, и убить этот процесс.

Но вы должны понимать, что если у этого процесса нет обработчика, который деинициализирует все, что он использовал (открытые файлы, сокеты, вилки, вещи, которые могут задерживаться, если не будут закрыты должным образом после завершения), вы бы создали перетащите на производительность системы. Кроме того, сокет будет оставаться открытым, пока ядро ​​не поймет, что процесс был убит. Обычно это занимает около минуты.

Я полагаю, что лучшим вопросом будет: какой порт (принадлежащий к какому процессу) вы хотите остановить?

Если вы пытаетесь положить конец обнаруженному бэкдору или вирусу, то вам следует, по крайней мере, узнать, какие данные передаются взад-вперед, прежде чем уничтожить их. (wireshark хорош для этого) (И имя исполняемого файла процесса, чтобы вы могли удалить его и предотвратить его повторный запуск при перезагрузке) или, если это что-то, что вы установили (например, HTTPD или FTPD или что-то еще), то у вас уже должен быть доступ к сам процесс.

Обычно это будет управляющая программа (HTTPD stop | start или что-то в этом роде). Или, если это системная вещь, вы, вероятно, не должны связываться с ней. Во всяком случае, я подумал, что, поскольку все остальные дают вам угол «как», я должен дать вам предостережения.

jackenheimer
источник
Очень хороший комментарий. У меня есть одна программа, которая при закрытии не закрывает сокет, что приводит к описанному поведению - сокет не работает в течение примерно 60 секунд. Когда я останавливаюсь и запускаю этот процесс, он примерно минуту жалуется, что адрес и порт уже используются. Лучшее решение - исправить некорректно закрывающийся процесс, чтобы закрыть его, но иногда это не вариант. Есть ли способ попросить ядро ​​проверить этот заблокированный сокет раньше, чем через 60 секунд?
Ян Влчинский
2

Сначала я искал процессы монго и ноды, затем сделал следующее:

ps -A | grep node

10418 pts/23   00:00:05 node

10551 pts/23   00:00:00 node

ps -A | grep mongo

10490 pts/23   00:00:00 mongod

После идентификации просто уничтожьте процессы с помощью команды kill.

kill -9 10418
kill -9 10490

Наконец, введите meteorи он должен снова работать.

abautista
источник
1

Вы можете написать скрипт, который модифицирует iptables и перезапускает их. Один скрипт для добавления правила отбрасывания всех пакетов на порт, другой скрипт для удаления указанного правила.

Другие ответы показали вам, как убить процесс, связанный с портом - это может быть не то, что вы хотите. Если вы хотите, чтобы сервер продолжал работать, но для предотвращения соединений с клиентами, то вы хотите заблокировать порт, а не останавливать процесс.

Майкл Шимминс
источник
@Michael Shimmins ... хм, звучит интересно, так как мы можем заблокировать порт на стороне сервера, чтобы клиент не отправлял никаких сообщений.
Ну, клиент может отправлять сообщения все, что они хотят, мы только что закрыли дверь, чтобы они не могли войти.
1

Еще одна проблема: иногда ядру принадлежат сами порты. Я знаю, что NAT-маршрутизация держит некоторые порты открытыми для использования NAT. Вы не можете убить процесс для этого, это ядро, и требуется перенастройка и перезагрузка.

Рич Гомолка
источник
1

Если вы хотите, чтобы ваш порт был освобожден раньше, вам нужно установить следующее значение:

echo 1 > /proc/sys/net/ipv4/tcp_fin_timeout

установить от 60 секунд (по умолчанию) до 1 секунды

Даниил
источник
1

Вы можете использовать команду с именем killcx, закрывая соединение без какого-либо процесса, который нужно уничтожить.

  • синтаксис:
killcx [dest_ip:dest_port] {interface}

  dest_ip              : remote IP
  dest_port            : remote port
  interface (optional) : network interface (eth0, lo etc).
  • пример:
killcx 120.121.122.123:1234
killcx 120.121.122.123:1234 eth0
shuaiming
источник
// У большинства машин Linux есть killcx? Ни у одного из тех, кого я пробовал, эта команда killcx не была доступна без установки пакетов, недоступных для них с их текущей конфигурацией репозитория.
Натан Басанезе
нет, вы можете найти инструмент здесь: killcx.sourceforge.net
shuaiming
// я знаю, что могу найти инструмент, просто установить его на тысячи серверов
Натан Басанезе
Используйте куклу @NathanBasanese :)
SHOUBHIK BOSE
1

Я знаю, что этот ответ, собственно говоря, не отвечает самому вопросу, но, читая его, это может быть важной информацией:

Поведение по умолчанию привязки сокета к порту (и адресу) таково, что когда сокет закрывается из-за внезапного завершения процесса, сокет некоторое время будет оставаться в TIME_WAIT. Это будет означать, что вы не можете немедленно повторно привязаться к этому адресу / порту. Если вы разрабатываете саму систему через стандартный интерфейс сокетов BSD, вы можете (по крайней мере, до некоторой степени) управлять этим поведением с помощью опции сокета SO_REUSEADDR. Это в основном позволит вам снова подключиться к тому же адресу / порту, если сокет находится в состоянии TIME_WAIT. Тем не менее, один сокет на порт!

Однако эту информацию следует использовать только в качестве вспомогательного средства для разработки, поскольку существует причина существования TIME_WAIT в первую очередь, уже объясненная в других ответах.

Томми Туура
источник
// , Правильно. Конечно, уничтожение процессов - не лучший способ освободить порты. Я заметил, что когда я убиваю процесс Vagrant, если он зависает, я не могу некоторое время снова бродить. Могу поспорить, что эта вещь TIME_WAIT как-то связана с этим.
Натан Басанезе
0

Если вам не нужен трафик через сокет, но вы хотите сохранить процесс: tcpkill .

tcpkill -i eth0 host xxx.xxx.xxx.xxx and port yyyy

Запустит демон прослушивания, который перехватывает и перехватывает весь трафик на этом порту. Очень удобно для тестирования ваших приложений на сетевые расколы.

RickyA
источник
0

Вы можете закрыть сокет прослушивания, используя ss :

sudo ss --kill state listening src :1234

Где 1234 - номер вашего порта.

ss является частью пакета iproute2, поэтому есть серьезные изменения, которые уже установлены в современном Linux.

Я узнал об этом из ответа на связанный вопрос .

Том Андерсон
источник