अजगर: बंधन सॉकेट: "पहले से ही उपयोग में पता"


83

मेरे पास टीसीपी / आईपी नेटवर्क पर क्लाइंट सॉकेट से संबंधित प्रश्न है। मान लीजिए कि मैं उपयोग करता हूं

try:

    comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

except socket.error, msg:

    sys.stderr.write("[ERROR] %s\n" % msg[1])
    sys.exit(1)

try:
    comSocket.bind(('', 5555))

    comSocket.connect()

except socket.error, msg:

    sys.stderr.write("[ERROR] %s\n" % msg[1])

    sys.exit(2)

निर्मित सॉकेट 5555 को पोर्ट करने के लिए बाध्य होगा। समस्या यह है कि कनेक्शन समाप्त होने के बाद

comSocket.shutdown(1)
comSocket.close()

वायरशर्क का उपयोग करते हुए, मुझे दोनों तरफ से फिन, एसीके और एसीके के साथ सॉकेट बंद दिखाई देता है, मैं फिर से पोर्ट का उपयोग नहीं कर सकता। मुझे निम्नलिखित त्रुटि मिलती है:

[ERROR] Address already in use

मुझे आश्चर्य है कि मैं पोर्ट को तुरंत कैसे साफ़ कर सकता हूं ताकि अगली बार भी मैं उसी पोर्ट का उपयोग कर सकूं।

comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

setsockopt समस्या को हल करने में सक्षम नहीं लगता है धन्यवाद!


1
क्लाइंट को एक विशिष्ट पोर्ट की आवश्यकता क्यों है?
ए जे।

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

3
आपके नेटवर्क व्यवस्थापकों को यह समझना चाहिए कि गंतव्य पोर्ट द्वारा आउटबाउंड ट्रैफ़िक को नियंत्रित किया जा सकता है ।
ए जे।

7
इसकी पर्याप्त जानकारी है। एक 99% संभावना है कि समस्या TIME_WAITसॉकेट स्थिति के कारण होती है , जिसका उत्तर नीचे दिया गया है :)
lunixbochs

1
आपका ऑपरेटिंग सिस्टम क्या है? आप आमतौर पर एक सॉकेट की स्थिति देखने के लिए
नेटस्टैट का

जवाबों:


124

SO_REUSEADDRसॉकेट को बांधने से पहले सॉकेट विकल्प का उपयोग करने का प्रयास करें ।

comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

संपादित करें: मैं देख रहा हूँ कि आप अभी भी इससे परेशान हैं। वहाँ एक मामला है जहाँ SO_REUSEADDRकाम नहीं करेगा। यदि आप एक सॉकेट को बांधने और उसी गंतव्य (पुन: SO_REUSEADDRसक्षम) के साथ पुन: कनेक्ट करने का प्रयास करते हैं , तो TIME_WAITअभी भी प्रभाव में रहेगा। हालांकि यह आपको एक अलग होस्ट से कनेक्ट करने की अनुमति देगा: पोर्ट।

एक-दो उपाय दिमाग में आते हैं। आप या तो तब तक पुन: प्रयास जारी रख सकते हैं जब तक आप फिर से एक कनेक्शन प्राप्त नहीं कर सकते। या यदि क्लाइंट सॉकेट (सर्वर नहीं) के समापन की शुरुआत करता है, तो उसे जादुई रूप से काम करना चाहिए।


1
फिर भी इसका पुन: उपयोग नहीं कर सकते। उसी पोर्ट को पुनः उपयोग करने में सक्षम होने से पहले मुझे जिस समय का इंतजार करना होगा वह 1 मिनट 30 सेकंड है :(
तू होंग

5
क्या आपने setsockoptपहले फोन किया था bind? पहला सॉकेट किसके साथ बनाया गया था SO_REUSEADDR, या सिर्फ असफल? वेटिंग सॉकेट में SO_REUSEADDRकाम करने के लिए इसके लिए सेट होना चाहिए
lunixbochs

हां, मैंने comSocket.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) को शामिल किया था, लेकिन अभी भी काम नहीं करता है।
तू होंग

1
tcp 0 0 98c5-9-134-71-1: freeciv mobile-166-132-02: 2345 TIME_WAIT
Tu Hoang

1
@jcoffland मैं मानता हूं कि उसे उपयोग नहीं करना चाहिए bind(), लेकिन SO_REUSEADDR न केवल टीसीपी सर्वर सॉकेट बल्कि टीसीपी क्लाइंट सॉकेट और यूडीपी सॉकेट सहित सभी सॉकेट्स के लिए है। यहाँ गलत जानकारी मत डालो।
user207421

26

यहाँ पूरा कोड है जो मैंने परीक्षण किया है और बिल्कुल मुझे "उपयोग में पहले से पता" त्रुटि नहीं देता है। आप इसे एक फ़ाइल में सहेज सकते हैं और उस फ़ाइल को HTML निर्देशिका के आधार निर्देशिका से चला सकते हैं जिसे आप सेवा करना चाहते हैं। इसके अतिरिक्त, आप प्रोग्राम को सर्वर शुरू करने से पहले निर्देशिकाओं को बदल सकते हैं

import socket
import SimpleHTTPServer
import SocketServer
# import os # uncomment if you want to change directories within the program

PORT = 8000

# Absolutely essential!  This ensures that socket resuse is setup BEFORE
# it is bound.  Will avoid the TIME_WAIT issue

class MyTCPServer(SocketServer.TCPServer):
    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler

httpd = MyTCPServer(("", PORT), Handler)

# os.chdir("/My/Webpages/Live/here.html")

httpd.serve_forever()

# httpd.shutdown() # If you want to programmatically shut off the server

Httpd.shutdown () के बाद भी आप पूरी तरह से सभी संसाधनों को जारी करने के लिए httpd.server_close () कॉल करना चाहते हैं।
एंड्रोबिन

इसके अलावा, किसी भी अप्रत्याशित मौजूद के लिए एटैक्सिट मॉड्यूल का उपयोग करने पर विचार करें।
एंड्रोबिन

14

यदि आप समस्या का सामना कर रहे हैं TCPServerया SimpleHTTPServer, ओवरराइड SocketServer.TCPServer.allow_reuse_address(अजगर 2.7.x) या socketserver.TCPServer.allow_reuse_address(अजगर 3.x) विशेषता का सामना कर रहे हैं

class MyServer(SocketServer.TCPServer):
    allow_reuse_address = True

server = MyServer((HOST, PORT), MyHandler)
server.serve_forever()

13

इस लिंक के अनुसार

वास्तव में, SO_REUSEADDR ध्वज बहुत अधिक परिणाम दे सकता है: SO_REUSADDR आपको उस पोर्ट का उपयोग करने की अनुमति देता है जो TIME_WAIT में अटका हुआ है, लेकिन आप अभी भी उस पोर्ट का उपयोग उस अंतिम स्थान से संबंध स्थापित करने के लिए नहीं कर सकते हैं जो इससे जुड़ा है। क्या? मान लीजिए कि मैं स्थानीय पोर्ट 1010 चुनता हूं, और foobar.com पोर्ट 300 से कनेक्ट होता है, और फिर उस पोर्ट को TIME-WAIT में छोड़ देता है। मैं foobar.com पोर्ट 300 को छोड़कर कहीं भी कनेक्ट करने के लिए तुरंत स्थानीय पोर्ट 1010 का पुन: उपयोग कर सकता हूं।

हालाँकि, आप यह सुनिश्चित करके TIME_WAIT स्थिति से पूरी तरह से बच सकते हैं कि दूरस्थ अंत क्लोजर (क्लोज इवेंट) आरंभ करता है। इसलिए सर्वर क्लाइंट को पहले बंद करके समस्याओं से बच सकता है। एप्लिकेशन प्रोटोकॉल को डिज़ाइन किया जाना चाहिए ताकि क्लाइंट को पता हो कि कब बंद करना है। सर्वर क्लाइंट से एक ईओएफ के जवाब में सुरक्षित रूप से बंद कर सकता है, हालांकि इसे उस समय भी सेट करना होगा जब यह ईओएफ की उम्मीद कर रहा हो, जब क्लाइंट ने नेटवर्क को अनजाने में छोड़ दिया हो। कई मामलों में सर्वर बंद होने से पहले बस कुछ सेकंड इंतजार करना पर्याप्त होगा।

मैं आपको नेटवर्किंग और नेटवर्क प्रोग्रामिंग के बारे में अधिक जानने के लिए भी सलाह देता हूं। आपको अब कम से कम कैसे tcp प्रोटोकॉल काम करना चाहिए। प्रोटोकॉल काफी तुच्छ और छोटा है और इसलिए, आपको भविष्य में बहुत समय बचा सकता है।

netstatकमांड के साथ आप आसानी से देख सकते हैं कि कौन से प्रोग्राम ((program_name, pid) tuple) किस पोर्ट से बाइंड किए गए हैं और सॉकेट चालू स्थिति क्या है: TIME_WAIT, CLOSING, FIN_WAIT और इसी तरह।

लिनक्स नेटवर्क कॉन्फ़िगरेशन की वास्तव में अच्छी व्याख्या /server/212093/how-to-reduce-number-of-sockets-in-time-wait पर मिल सकती है ।


इसके अलावा, आपको अपने कोड से सावधान रहना चाहिए। यदि आपका कोड अभी भी विकास पर है और कुछ अपवाद हैं, तो कनेक्शन को विशेष रूप से सर्वर साइड से ठीक से बंद नहीं किया जा सकता है।
रुस्तम के

8
आपको निष्पक्ष होना चाहिए और टॉम के नेटवर्क गाइड से आपके द्वारा कॉपी और पेस्ट किए गए वाक्यों को उद्धृत करना चाहिए । कृपया अपना उत्तर सही करें।
हैलोवर्ल्ड

8

आपको बंधन से पहले allow_reuse_address सेट करने की आवश्यकता है। SimpleHTTPServer के बजाय इस स्निपेट को चलाएं:

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler, bind_and_activate=False)
httpd.allow_reuse_address = True
httpd.server_bind()
httpd.server_activate()
httpd.serve_forever()

इससे पहले कि हमें झंडे सेट करने का मौका मिले, यह सर्वर को बाइंड करने से रोकता है।


TCPServer को उप-वर्ग करना और विशेषता को ओवरराइड करना बहुत आसान लगता है, उदाहरण के लिए मेरा जवाब देखें
आंद्रेई

3

जैसा कि फेलिप क्रूज ने उल्लेख किया है, आपको बाइंडिंग से पहले SO_REUSEADDR सेट करना होगा। मुझे दूसरी साइट पर एक समाधान मिला - अन्य साइट पर समाधान, नीचे पुन: प्रस्तुत किया गया

समस्या यह है कि SO_REUSEADDR सॉकेट विकल्प सेट होना चाहिए इससे पहले कि पता सॉकेट से जुड़ा हो। यह ThreadTCPServer को उप-लिंक करके और सर्वर_बिन्ध विधि को ओवरराइड करके इस प्रकार किया जा सकता है:

आयात सॉकेटसर्वर, सॉकेट

वर्ग MyThreadingTCPServer (सॉकेटसर्वर। TreadTCPServer):
    def server_bind (स्वयं):
        सेल्फ.सेटेकसेटसेट (socket.SOL_SOCKET, socket.SO_REUSEADDR) 1
        self.socket.bind (self.server_address)


3

मुझे इस अपवाद का एक और कारण मिला। स्पाइडर आईडीई से आवेदन चलाते समय (मेरे मामले में यह रास्पियन पर स्पाइडर 3 था) और ^ सी या एक अपवाद द्वारा समाप्त कार्यक्रम, सॉकेट अभी भी सक्रिय था:

sudo netstat -ap | grep 31416
tcp  0  0 0.0.0.0:31416  0.0.0.0:*    LISTEN      13210/python3

कार्यक्रम चलाने पर फिर से "पहले से उपयोग में पता" पाया गया; IDE एक अलग प्रक्रिया के रूप में नए 'रन' को शुरू करने के लिए लगता है जो पिछले 'रन' द्वारा उपयोग किए जाने वाले सॉकेट को ढूंढता है।

socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

कोई सहायता नहीं की।

हत्या प्रक्रिया 13210 में मदद मिली। जैसे कमांड-लाइन से अजगर स्क्रिप्ट शुरू करना

python3 <app-name>.py

हमेशा अच्छा काम किया जब SO_REUSEADDR को सही पर सेट किया गया। नई Thonny IDE या Idle3 IDE में यह समस्या नहीं थी।


1

socket.socket()पहले चलाने के लिए socket.bind()और REUSEADDRकहा के रूप में उपयोग करना चाहिए


उन्होंने प्रश्न की सामग्री को बदल दिया: stackoverflow.com/revisions/6380057/4 - अगली बार से पहले संशोधन की जाँच करें :)
फेलिप क्रूज़

1

मेरे लिए बेहतर समाधान निम्नलिखित था। चूंकि कनेक्शन को बंद करने की पहल सर्वर द्वारा की गई setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)थी , इसलिए कोई प्रभाव नहीं पड़ा और TIME_WAIT उसी पोर्ट पर नए कनेक्शन को त्रुटि से बचा रहा था:

[Errno 10048]: Address already in use. Only one usage of each socket address (protocol/IP address/port) is normally permitted 

मैंने अंत में ओएस को पोर्ट का चयन करने देने के लिए समाधान का उपयोग किया, फिर एक और पोर्ट का उपयोग किया जाता है यदि मिसाल अभी भी TIME_WAIT में है।

मैंने प्रतिस्थापित किया:

self._socket.bind((guest, port))

साथ में:

self._socket.bind((guest, 0))

जैसा कि यह एक tcp पते के अजगर सॉकेट प्रलेखन में इंगित किया गया था :

यदि आपूर्ति की जाती है, तो सोर्स_एड्रेस को सॉकेट के लिए 2-ट्यूपल (होस्ट, पोर्ट) होना चाहिए, जिससे कनेक्ट करने से पहले उसके स्रोत पते के रूप में बाइंड किया जा सके। यदि होस्ट या पोर्ट क्रमशः '' या 0 हैं, तो OS डिफ़ॉल्ट व्यवहार का उपयोग किया जाएगा।


1
आप बस बाँध को छोड़ सकते हैं, लेकिन ओपी कहता है कि उसे पोर्ट 5555 का उपयोग करना चाहिए, और यह उत्तर नहीं देता है।
user207421

1

एक और समाधान, निश्चित रूप से, विकास के माहौल में, उदाहरण के लिए, इसका उपयोग करके प्रक्रिया को मार रहा है

def serve():
    server = HTTPServer(('', PORT_NUMBER), BaseHTTPRequestHandler)
    print 'Started httpserver on port ' , PORT_NUMBER
    server.serve_forever()
try:
    serve()
except Exception, e:
    print "probably port is used. killing processes using given port %d, %s"%(PORT_NUMBER,e)
    os.system("xterm -e 'sudo fuser -kuv %d/tcp'" % PORT_NUMBER)
    serve()
    raise e

हत्या की प्रक्रिया किसी भी तरह से TIME_WAIT को प्रभावित नहीं करती है।
user207421

0

मुझे पता है कि आपने पहले ही एक उत्तर स्वीकार कर लिया है, लेकिन मेरा मानना ​​है कि समस्या को क्लाइंट सॉकेट पर कॉलिंग बाइंड () के साथ करना है। यह ठीक हो सकता है लेकिन बाइंड () और शटडाउन () एक साथ अच्छा नहीं लगता है। इसके अलावा, SO_REUSEADDR का उपयोग आम तौर पर सुनने के सॉकेट के साथ किया जाता है। यानी सर्वर की तरफ।

कनेक्ट करने के लिए आपको पास होना चाहिए और आईपी / पोर्ट होना चाहिए ()। ऐशे ही:

comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.connect(('', 5555))

बाइंड () को कॉल न करें, SO_REUSEADDR सेट न करें।


2
bind()और shutdown()एक दूसरे के साथ करने के लिए वास्तव में कुछ भी नहीं है, और सुझाव है कि 'वे एक साथ अच्छी तरह से खेलने के लिए प्रतीत नहीं होता' निराधार है। आपको वह हिस्सा याद आ गया है जहाँ उसे स्थानीय पोर्ट 5555 का उपयोग करने की आवश्यकता है।
user207421

0

मुझे लगता है कि सबसे अच्छा तरीका है बस प्रक्रिया टर्मिनल में, उस पोर्ट पर मारने के लिए टाइपिंग के द्वारा होता है fuser -k [PORT NUMBER]/tcp, उदाहरण के लिए fuser -k 5001/tcp


1
जब तक कि प्रक्रिया पहले से ही मार दी जाती है और यह केवल ओएस द्वारा साफ किए जाने के लिए बंदरगाह को खुला छोड़ देता है।
क्रिस मर्क

1
हत्या की प्रक्रिया किसी भी तरह से TIME_WAIT को प्रभावित नहीं करती है।
user207421
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.