अप्रयुक्त स्थानीय पोर्ट को खोजने का सबसे आसान तरीका क्या है?


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
@ मैं ssh -DSOCKS सर्वर के रूप में उपयोग करने के लिए एक खुला पोर्ट स्वचालित रूप से खोजने का प्रयास कर रहा हूं ।
mybuddymichael

जवाबों:


24

यदि आपका एप्लिकेशन इसका समर्थन करता है, तो आप पोर्ट 0 को एप्लिकेशन में पास करने का प्रयास कर सकते हैं। यदि आपका एप्लिकेशन इसे कर्नेल में भेजता है, तो पोर्ट डायनामिक रूप से अनुरोध समय पर आवंटित किया जाएगा, और गारंटी दी जाती है कि यह उपयोग में न हो (यदि सभी पोर्ट पहले से उपयोग में हैं तो आवंटन विफल हो जाएगा)।

अन्यथा, आप इसे मैन्युअल रूप से कर सकते हैं। आपके उत्तर की स्क्रिप्ट में एक दौड़ की स्थिति है, इससे बचने का एकमात्र तरीका यह है कि इसे खोलने की कोशिश करने के दौरान इसे खोलकर परमाणु जांच की जाए। यदि पोर्ट उपयोग में है, तो प्रोग्राम को पोर्ट को खोलने में विफलता के साथ छोड़ देना चाहिए।

उदाहरण के लिए, मान लें कि आप GNU नेटकैट के साथ सुनने की कोशिश कर रहे हैं।

#!/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: आप यहाँ एक दौड़ की स्थिति कहाँ देखते हैं?
क्रिस डाउन

1
यह पोर्ट पहले उपलब्ध पोर्ट का उपयोग करने की कोशिश करता है। जब आपके पास दो समवर्ती प्रक्रियाएं होती हैं, तो जो पोर्ट अभी चेक किया गया है, उसका पुन: उपयोग किया जा सकता है। अपने उत्तर को फिर से पढ़ना, ऐसा लगता है कि आप सभी पोर्ट समाप्त होने तक किसी उपलब्ध पोर्ट पर बाध्यकारी पुन: प्रयास करने का सुझाव देते हैं। यह मानते हुए कि प्रश्न में कार्यक्रम "पोर्ट इन यूज़" और अन्य त्रुटियों के बीच अंतर कर सकता है, यह ठीक होना चाहिए (हालांकि यादृच्छिकता अभी भी अप्रत्याशितता के लिए इसे बेहतर बनाएगी)।
लेकेनस्टेएन

1
@ Lekensteyn कर्नेल में सफल पोर्ट बाइंडिंग परिणाम EADDRINUSE लौट रहा है यदि आप फिर से कोशिश करते हैं और इसका उपयोग करते हैं, तो यह संभव नहीं है कि "जो पोर्ट अभी चेक किया गया है उसका पुन: उपयोग किया जा सकता है"।
क्रिस डाउन

हां, मैंने गलत तरीके से मान लिया था कि आप लूप से बाहर निकल जाएंगे और $portवास्तविक कार्यक्रम में उपयोग करेंगे while ...; done; program --port $port
लेकेनस्टेएन

मैन पेज से: -p source_port स्रोत पोर्ट nc का उपयोग करना चाहिए, विशेषाधिकार प्रतिबंध और उपलब्धता के अधीन। -L विकल्प के साथ संयोजन में इस विकल्प का उपयोग करना एक त्रुटि है।
भिक्षु

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
यहाँ एक-लाइनर (पायथन 2 और पायथन 3 के साथ मान्य) है:python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'
लेकेनस्टीन

4
मैंने उल्लिखित प्रयोग को चलाया, और सभी परिणाम अद्वितीय नहीं थे। मेरा हिस्टोग्राम था:{ 1: 7006, 2: 1249, 3: 151, 4: 8, 5: 1, 6: 1}
bukzor

2
क्या चेक में जोड़ने का एक आसान तरीका यह है कि पोर्ट को फ़ायरवॉल द्वारा ब्लॉक नहीं किया गया है, या केवल खुले पोर्ट की खोज करता है?
निशान लता

1
@dshepherd मेरा मानना ​​है कि यदि आप पिछले एक को बंद नहीं करते हैं (और अंतिम समय में उन सभी को बंद कर देंगे) तो आपको अलग-अलग पोर्ट मिलेंगे।
फ्रैंकलिन यू

1
रूबी 2.3.1 के लिए एक लाइनर:ruby -e 'puts Addrinfo.tcp("", 0).bind { |s| s.local_address.ip_port }'
फ्रैंकलिन यू

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कहते हैं, को हल करने की कोशिश नहीं करते हैं, - सांख्यिक 22लिए ssh)। हम तब केवल चौथा कॉलम चुनते हैं awk, जिसमें स्थानीय पता और पोर्ट होता है। हम cutपता और पोर्ट को :सीमांकक के साथ विभाजित करने और केवल बाद वाले ( -f2) को रखने के लिए उपयोग करते हैं । ssएक हेडर का भी उत्पादन, कि हम grepसंख्याओं के गैर-खाली अनुक्रमों के लिए पिंग द्वारा छुटकारा पाते हैं जो अब 5 से अधिक नहीं हैं। हम फिर डुप्लिकेट के बिना आईएनजी की commआवश्यकता का अनुपालन करते हैं ।sort-u

अब हमारे पास खुले बंदरगाहों की सूची है, जिसे हम shufपहले "$HOWMANY"वाले हड़पने के लिए उड़ सकते हैं head -n

उदाहरण

निजी श्रेणी में तीन यादृच्छिक खुले बंदरगाहों को पकड़ो (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के साथ -uमें ssबजाय मुक्त UDP पोर्ट मिलता है।
  • की जगह shufके साथ sort -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

जाहिरा तौर पर टीसीपी कनेक्शन को बैश / 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 में बदलते हैं, तो आप अभी भी ईजी 35835 प्राप्त कर सकते हैं। रैंडम [0,3236767] में एक नंबर देता है। अधिकतम से अधिक संख्या से इसे संशोधित करने का कोई प्रभाव नहीं पड़ता है। आप जैसा कुछ चाहते हैं $[$LPORT + ($RANDOM % ($UPORT-$LPORT))]
lxs

अन्यथा बहुत अच्छा है!
lxs

यह \nकिसी भी सुनने के बंदरगाह के लिए भेजता है, :) मैं जोड़ने का सुझाव देंगे -n। यह अभी भी एक कनेक्शन खोलने की कोशिश करेगा लेकिन कुछ भी नहीं भेजेगा, लेकिन तुरंत डिस्कनेक्ट कर देगा।
स्टीफन

4

यहाँ एक क्रॉस-प्लेटफ़ॉर्म, कुशल "ऑनलाइनर" है जो सभी इन-उपयोग पोर्ट को धीमा कर देता है और आपको 3000 में से पहला उपलब्ध उपलब्ध कराता है:

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

यह मैक और लिनक्स दोनों पर काम करता है, यही वजह है कि [:.]रेगेक्स की जरूरत है।


टीसीपी (6) सॉकेट को सिर्फ क्यों -aऔर क्यों नहीं -tदेखें?
स्टीफन

और जब हम उस पर होते हैं, तो आउटपुट ss -Htnlबेहतर हो सकता है (और तेज़! - ऐसा नहीं है कि मैं इस बारे में परवाह करता हूं : पी)।
स्टीफन

@stefanct BSD netstat के पास -tकम से कम एक ऐसा नहीं है जो Apple जहाजों के साथ है, और macOS पर ssभी मौजूद नहीं है। netstat -alnसोलारिस पर भी काम करता है।
wtt

3

लिनक्स पर, आप कुछ ऐसा कर सकते हैं:

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'

हालाँकि, पोर्ट को खोलने और वास्तव में इसका उपयोग करने के प्रयास के बीच एक दौड़ की स्थिति शामिल है।
क्रिस डाउन

@ क्रिस, वास्तव में, लेकिन ssh -D, मैं कोई बेहतर विकल्प नहीं देख सकता। -O forwardका विकल्प sshएक त्रुटि वापस नहीं करता है जब आगे असफल।
स्टीफन चेजलस

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होस्टनाम (-n) निर्धारित करने के लिए समय बर्बाद किए बिना सभी (-a) टीसीपी (-t) और यूडीपी (-u) बंदरगाहों को सूचीबद्ध करता है।


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 के साथ हम रेंज में (-i) से एक यादृच्छिक संख्या / proc / sys / net / ipv4 / ip_local_port_range लेते हैं। shuf को डैश के साथ सिंटैक्स की आवश्यकता होती है, इसलिए हम टैब को डैश में बदलने के लिए tr का उपयोग करते हैं।

अगली wie संख्याओं (-n) में tcp और udp (-u -t) कनेक्शन हम सब को दिखाने के लिए netstat का उपयोग करें, अगर हम इसमें अपना रैंडम पोर्ट $ पोर्ट पाते हैं: (a से शुरू करें और w व्हाट्सएप से अंत करें) \ s) तब हमें एक दूसरे पोर्ट की आवश्यकता होती है और इसलिए जारी रहती है। एल्स (grep -q में एक रिटर्नकोड है> 0 हमने लूप छोड़ दिया और $ पोर्ट सेट है।


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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.