사용하지 않는 로컬 포트를 찾는 가장 쉬운 방법은 무엇입니까?


52

사용하지 않는 로컬 포트를 찾는 가장 쉬운 방법은 무엇입니까?

현재 나는 이와 비슷한 것을 사용하고 있습니다 :

port=$RANDOM
quit=0

while [ "$quit" -ne 1 ]; do
  netstat -a | grep $port >> /dev/null
  if [ $? -gt 0 ]; then
    quit=1
  else
    port=`expr $port + 1`
  fi
done

로터리가 끔찍하기 때문에 내가 놓친 내장과 같은 더 간단한 경로가 있는지 궁금합니다.


2
왜 그렇게 하시겠습니까? 그것은 본질적으로 불쾌합니다 (그리고 비효율적이며 -nnetstat와 더 선택적인 grep에 가장 적습니다 ). 이를 수행하는 방법은 필요한 모드로 포트를 열려고 시도하고 사용할 수없는 경우 다른 포트를 시도하는 것입니다.
Mat

1
@ Mat 나는 ssh -DSOCKS 서버로 사용할 열린 포트를 자동으로 찾으려고 합니다.
mybuddymichael

답변:


24

응용 프로그램에서 지원하는 경우 포트 0을 응용 프로그램으로 전달할 수 있습니다. 응용 프로그램이이를 커널에 전달하면 포트는 요청시 동적으로 할당되며 사용 중이 아님을 보장합니다 (모든 포트가 이미 사용중인 경우 할당이 실패 함).

그렇지 않으면 수동으로 수행 할 수 있습니다. 귀하의 답변에있는 스크립트에는 경쟁 조건이 있으며, 피할 수있는 유일한 방법은 열려고 시도하여 원자 적으로 열려 있는지 확인하는 것입니다. 포트가 사용중인 경우 포트를 열지 못하면 프로그램이 종료됩니다.

예를 들어, GNU netcat을 들으려고한다고 가정하십시오.

#!/bin/bash
read lower_port upper_port < /proc/sys/net/ipv4/ip_local_port_range
while :; do
    for (( port = lower_port ; port <= upper_port ; port++ )); do
        nc -l -p "$port" 2>/dev/null && break 2
    done
done

1
@Lekensteyn : 여기서 경합 상태는 어디입니까?
Chris Down

1
이 포트는 사용 가능한 첫 번째 포트를 사용하려고합니다. 두 개의 동시 프로세스가있는 경우 방금 확인한 포트가 재사용 될 수 있습니다. 답을 다시 읽으면 모든 포트가 소진 될 때까지 사용 가능한 포트에서 바인딩을 다시 시도하는 것이 좋습니다. 문제의 프로그램이 "사용중인 포트"와 다른 오류를 구별 할 수 있다고 가정하면, 무작위 화는 여전히 예측할 수없는 상태로 만들지 만 괜찮습니다.
Lekensteyn

1
@Lekensteyn 포트 바인딩에 성공하면 커널이 EADDRINUSE를 반환하고 다시 사용하면 "방금 확인한 포트를 다시 사용할 수 없습니다"가 불가능합니다.
Chris Down

예, 루프를 끝내고 $port실제 프로그램에서 와 같이 사용한다고 잘못 가정 했습니다 while ...; done; program --port $port.
Lekensteyn의

매뉴얼 페이지에서 : -p source_port 권한 제한 및 가용성에 따라 nc가 사용해야하는 소스 포트를 지정합니다. 이 옵션을 -l 옵션과 함께 사용하면 오류가 발생합니다.
monksy

54

내 솔루션은 포트 0에 바인딩하는 것입니다.이 포트는 커널이 ip_local_port_range에서 포트를 할당하도록 요청합니다. 그런 다음 소켓을 닫고 구성에 해당 포트 번호를 사용하십시오.

커널은 포트 번호를 절대로 다시 사용할 때까지 포트 번호를 재사용하지 않는 것 같습니다. 포트 0에 대한 후속 바인드는 다른 포트 번호를 할당합니다. 파이썬 코드 :

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 0))
addr = s.getsockname()
print addr[1]
s.close()

이것은 단지 몇 개의 포트를 제공합니다. 60123.

이 프로그램을 10 000 번 실행하십시오 (동시에 실행해야합니다). 10 000 개의 다른 포트 번호를 얻게됩니다. 따라서 포트를 사용하는 것이 안전하다고 생각합니다.


20
여기에 하나의 라이너가 있습니다 (Python 2 및 Python 3에 유효) :python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'
Lekensteyn

4
언급 된 실험을 실행했으며 모든 결과가 독특하지는 않았습니다. 내 막대 그래프는 :{ 1: 7006, 2: 1249, 3: 151, 4: 8, 5: 1, 6: 1}
bukzor

2
포트가 방화벽에 의해 차단되지 않았는지 또는 열린 포트만 검색하는지 확인하는 쉬운 방법이 있습니까?
Mark Lakata

1
@dshepherd 이전 포트를 닫지 않으면 마지막으로 한 번에 모두 닫으면 다른 포트가 표시됩니다.
Franklin Yu

1
루비 2.3.1에 대한 하나의 라이너 :ruby -e 'puts Addrinfo.tcp("", 0).bind { |s| s.local_address.ip_port }'
Franklin Yu

12

짧막 한 농담

나는 목적을 신속하게 제공하는 멋진 원 라이너를 구성하여 임의의 범위에서 임의의 수의 포트를 잡을 수 있습니다 (여기에서는 가독성을 위해 4 줄로 나뉩니다).

comm -23 \
<(seq "$FROM" "$TO" | sort) \
<(ss -tan | awk '{print $4}' | cut -d':' -f2 | grep '[0-9]\{1,5\}' | sort -u) \
| shuf | head -n "$HOWMANY"

한 줄씩

comm알파벳순으로 정렬되어야하는 두 파일의 행을 비교하는 유틸리티입니다. 첫 번째 파일에만 나타나는 행, 두 번째 열에 만 나타나는 행 및 공통 행의 세 가지 열을 출력합니다. 지정 -23하면 후자의 열을 억제하고 첫 번째 열만 유지합니다. 이를 사용하여 일련의 텍스트 줄로 표현 된 두 세트의 차이를 얻을 수 있습니다. 나는 comm 여기 에 대해 배웠다 .

첫 번째 파일은 선택할 수있는 포트 범위입니다. seq에서 $FROM까지 정렬 된 숫자 시퀀스를 생성합니다 $TO. 결과는 알파벳순 (숫자 대신)으로 정렬되고 프로세스 대체를comm 사용하여 첫 번째 파일로 파이프됩니다 .

두 번째 파일은 우리가 호출하여받는 것이, 포트의 정렬 된 목록입니다 ss명령 (와 -t, 의미 TCP 포트 -a- 설립 듣기 - 모든 의미하는 -n숫자 - 말하자면, 해결하려고하지 않습니다 22ssh). 그런 다음로 awk로컬 주소와 포트가 포함 된 네 번째 열만 선택합니다 . 우리 cut:구분자와 함께 주소와 포트를 분할 하고 후자를 유지하는 데 사용합니다 ( -f2). ss또한 grep5를 넘지 않는 비어 있지 않은 숫자 시퀀스를 핑하여 제거하는 헤더를 출력합니다 . 그런 다음 중복없이 ing comm하여 요구 사항 을 준수 sort합니다 -u.

이제 우리는 우리가 할 수있는, 열린 포트의 정렬 된 목록이 shuf다음 첫 번째 잡아 FLE "$HOWMANY"을 가진 사람을 head -n.

개인 범위에서 3 개의 임의 개방 포트를 가져옵니다 (49152-65535)

comm -23 <(seq 49152 65535 | sort) <(ss -tan | awk '{print $4}' | cut -d':' -f2 | grep "[0-9]\{1,5\}" | sort -u) | shuf | head -n 3

예를 들어 반환 할 수 있습니다

54930
57937
51399

노트

  • 스위치 -t-uss대신 무료 UDP 포트를 얻을 수 있습니다.
  • 교체 shufsort -n사용 가능한 포트를 수치 적으로 분류하는 대신 무작위로 얻을 선호하는 경우

11
#!/bin/bash
read LOWERPORT UPPERPORT < /proc/sys/net/ipv4/ip_local_port_range
while :
do
        PORT="`shuf -i $LOWERPORT-$UPPERPORT -n 1`"
        ss -lpn | grep -q ":$PORT " || break
done
echo $PORT

크리스 다운 크레딧


6

분명히 TCP 연결은 bash / zsh에서 리눅스에서 파일 디스크립터로 사용될 수 있습니다 . 다음 함수는이 기술을 사용하며 netcat / telnet을 호출하는 것보다 빠릅니다.

function EPHEMERAL_PORT() {
    LOW_BOUND=49152
    RANGE=16384
    while true; do
        CANDIDATE=$[$LOW_BOUND + ($RANDOM % $RANGE)]
        (echo "" >/dev/tcp/127.0.0.1/${CANDIDATE}) >/dev/null 2>&1
        if [ $? -ne 0 ]; then
            echo $CANDIDATE
            break
        fi
    done
}

사용법 지시 사항 : 출력을 변수에 바인딩하고 스크립트에서 사용하십시오. 우분투 16.04에서 테스트

root@ubuntu:~> EPHEMERAL_PORT
59453
root@ubuntu:~> PORT=$(EPHEMERAL_PORT)

함께 작동 ksh93합니다.
fpmurphy

UPORT를 32768로 변경하면 EG 35835를 계속 얻을 수 있습니다. RANDOM은 [0,32767]의 숫자를 반환합니다. 최대 값보다 큰 숫자로이를 수정해도 효과가 없습니다. 당신은 같은 것을 원합니다 $[$LPORT + ($RANDOM % ($UPORT-$LPORT))].
lxs

그렇지 않으면 꽤 시원합니다!
lxs

이것은 \n모든 청취 포트로 보냅니다 :) 추가를 제안 -n합니다. 그래도 연결을 열려고하지만 아무것도 보내지 않고 즉시 연결을 끊습니다.
stefanct

4

다음은 사용중인 모든 포트를 정리하고 3000 이상에서 사용할 수있는 첫 번째 포트를 제공하는 크로스 플랫폼, 효율적인 "oneliner"입니다.

netstat -aln | awk '
  $6 == "LISTEN" {
    if ($4 ~ "[.:][0-9]+$") {
      split($4, a, /[:.]/);
      port = a[length(a)];
      p[port] = 1
    }
  }
  END {
    for (i = 3000; i < 65000 && p[i]; i++){};
    if (i == 65000) {exit 1};
    print i
  }
'

모든 줄을 연결하여 한 줄로 만들 수 있습니다. 다른 포트 번호에서 첫 번째를 사용 i하려면 for루프 에서 할당을 로 변경하십시오 .

Mac과 Linux 모두에서 작동하므로 [:.]정규식이 필요합니다.


-a이 아니라 -t단지 (6) 소켓 TCP 보는?
stefanct

우리의 출력을 구문 분석, 그것은에있는 동안 그리고 ss -Htnl더 좋을 수도 (! - 빨리하지 않는 것이 내가 : P 그것에 대해 신경).
stefanct

@stefanct BSD netstat에는 -tApple과 함께 제공되는 ss것이 없으며 macOS에는 없습니다. netstat -aln심지어 솔라리스에서도 작동합니다.
w00t

3

Linux에서는 다음과 같은 작업을 수행 할 수 있습니다.

ss -tln | 
  awk 'NR > 1{gsub(/.*:/,"",$4); print $4}' |
  sort -un |
  awk -v n=1080 '$0 < n {next}; $0 == n {n++; next}; {exit}; END {print n}'

1080 이상의 첫 번째 사용 가능한 포트를 찾으려면 ssh -D루프백 인터페이스에서 바인드되므로 이론적으로 소켓이 다른 주소에 바인드 된 경우 포트 1080을 재사용 할 수 있습니다. 또 다른 방법은 실제로 시도하고 바인딩하는 것입니다.

perl -MSocket -le 'socket S, PF_INET, SOCK_STREAM,getprotobyname("tcp");
  $port = 1080;
  ++$port until bind S, sockaddr_in($port,inet_aton("127.1"));
  print $port'

그러나 여기에는 포트를 열려고 시도하는 것과 실제로 사용하는 것 사이의 경쟁 조건이 포함됩니다.
Chris Down

@ChrisDown, 실제로,하지만 ssh -D더 나은 옵션을 볼 수 없습니다. -O forward의 옵션은 ssh전방이 실패 할 때 오류를 반환하지 않습니다.
Stéphane Chazelas

3

이것은 내가 사용하는 버전입니다.

while
  port=$(shuf -n 1 -i 49152-65535)
  netstat -atun | grep -q "$port"
do
  continue
done

echo "$port"

이 명령 shuf -n 1 -i 49152-65535은 동적 범위의 "무작위"포트를 제공합니다. 이미 사용중인 경우 해당 범위의 다른 포트가 시도됩니다.

이 명령 netstat -atun은 호스트 이름을 결정하는 데 시간을 낭비하지 않고 모든 (-a) TCP (-t) 및 UDP (-u) 포트를 나열합니다 (-n).


1

이것은 내 .bashrc에있는 기능의 일부로 SSH 터널을 동적으로 생성하고 범위 내의 모든 포트를 사용하려고합니다.

   lps=( 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 )
   lp=null

   # find a free listening port
   for port in ${lps[@]}; do
      lsof -i -n -P |grep LISTEN |grep -q ":${port}"
      [ $? -eq 1 ] && { lp=$port; break; }
   done
   [ "$lp" = "null" ] && { echo "no free local ports available"; return 2; }
   return $port

YMMV


1

이 오래된 취미 말에서 또 다른 달리기 :

function random_free_tcp_port {
  local ports="${1:-1}" interim="${2:-2048}" spacing=32
  local free_ports=( )
  local taken_ports=( $( netstat -aln | egrep ^tcp | fgrep LISTEN |
                         awk '{print $4}' | egrep -o '[0-9]+$' |
                         sort -n | uniq ) )
  interim=$(( interim + (RANDOM % spacing) ))

  for taken in "${taken_ports[@]}" 65535
  do
    while [[ $interim -lt $taken && ${#free_ports[@]} -lt $ports ]]
    do
      free_ports+=( $interim )
      interim=$(( interim + spacing + (RANDOM % spacing) ))
    done
    interim=$(( interim > taken + spacing
                ? interim
                : taken + spacing + (RANDOM % spacing) ))
  done

  [[ ${#free_ports[@]} -ge $ports ]] || return 2

  printf '%d\n' "${free_ports[@]}"
}

이 코드의 순수 휴대용 사용한다 netstat, egrep, awk, 등을. 처음에는 가져온 포트 목록을 얻기 위해 외부 명령에 대해서만 호출이 실행됩니다. 하나 이상의 사용 가능한 포트를 요청할 수 있습니다.

:;  random_free_tcp_port
2070
:;  random_free_tcp_port 2
2073
2114

임의의 포트에서 시작하십시오.

:;  random_free_tcp_port 2 10240
10245
10293

1
while port=$(shuf -n1 -i $(cat /proc/sys/net/ipv4/ip_local_port_range | tr '\011' '-'))
netstat -atun | grep -q ":$port\s" ; do
    continue
done
echo $port

위의 다른 답변에서 내 조합. 그것을 얻으십시오 :

shuf -n1을 사용하면 / proc / sys / net / ipv4 / ip_local_port_range의 범위 (-i)에서 임의의 숫자 하나를 가져옵니다. shuf에는 대시 구문이 필요하므로 tr을 사용하여 탭을 대시로 변경하십시오.

다음으로 netstat를 사용하여 임의의 포트 $ port를 발견하면 (-a) tcp 및 udp (-u -t) 연결을 숫자 (-n)로 표시합니다 (:로 시작하고 w 공백으로 끝남) \ s) 그러면 다른 포트가 필요하므로 계속 진행하십시오 (그렇지 않으면 grep -q의 리턴 코드가 0보다 크면 while 루프를 떠나고 $ port가 설정됩니다).


1

파이썬이 주위에 있다면, 나는 이것을 할 것입니다 :

port="$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1])')";
echo "Unused Port: $port"


0

내 생각에 ...이 기능은 n연속 사용 가능한 포트 를 찾으려고합니다 .

#!/bin/bash

RANDOM=$$

# Based on 
# https://unix.stackexchange.com/a/55918/41065
# https://unix.stackexchange.com/a/248319/41065
find_n_ports () {
    local n=$1
    RANDOM=$$
    read lower_port upper_port < /proc/sys/net/ipv4/ip_local_port_range
    local tries=10
    while [ $((tries--)) -gt 0 ]; do
        # Sleep for 100 to 499 ms
        sleep "0.$((100+$RANDOM%399))"
        local start="`shuf -i $lower_port-$(($upper_port-$n-1)) -n 1`"
        local end=$(($start+$n-1))
        # create ss filter for $n consecutive ports starting with $start
        local filter=""
        local ports=$(seq $start $end)
        for p in $ports ; do
            filter="$filter or sport = $p"
        done
        # if none of the ports is in use return them
        if [ "$(ss -tHn "${filter# or}" | wc -l)" = "0" ] ; then
            echo "$ports"
            return 0
        fi
    done
    echo "Could not find $n consecutive ports">&2
    return 1
}

ports=$(find_n_ports 3)
[ $? -ne 0 ] && exit 1
exit 0
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.