2011-06-17 15 views
52

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

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() 

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

[ERROR] Address already in use 

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

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

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

+0

ग्राहक को एक विशिष्ट पोर्ट की आवश्यकता क्यों है? –

+1

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

+0

मेरा सुझाव है कि आप वास्तविक कोड कॉपी और पेस्ट करें। आपने जो लिखा है, उसमें एक स्पष्ट त्रुटि है जो आपको उस व्यवहार को देखने से रोकती है जिसे आपने देखा है। इससे पाठकों को कोई जानकारी नहीं होती है कि आपने हमें यह बताने के लिए और क्या उपेक्षा की है कि आपकी समस्या हो सकती है। –

उत्तर

82

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

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

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

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

+0

अभी भी इसका पुन: उपयोग नहीं कर सकता एक ही बंदरगाह का पुन: उपयोग करने में सक्षम होने से पहले मुझे इंतजार करना है कि समय 1 मिनट 30 सेकंड है :( –

+4

क्या आपने 'बाइंड' से पहले 'सेटॉकॉप' को कॉल किया था? 'SO_REUSEADDR' के साथ बनाई गई पहली सॉकेट थी, या केवल असफल? प्रतीक्षा सॉकेट में – lunixbochs

+0

काम करने के लिए 'SO_REUSEADDR' सेट होना चाहिए हां, मैंने comSocket.setsockopt (सॉकेट.SOL_SOCKET, सॉकेट.SO_REUSEADDR, 1) शामिल किया था लेकिन अभी भी काम नहीं करता है। –

2

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

2

के रूप में फेलिप क्रूज उल्लेख किया जाना चाहिए, आप बंधन से पहले SO_REUSEADDR सेट करना होगा। solution on other site, reproduced below

The problem is that the SO_REUSEADDR socket option must be set before the address is bound to the socket. This can be done by subclassing ThreadingTCPServer and overriding the server_bind method as follows:

 
import SocketServer, socket 

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

1

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

आपको पास करने के लिए गुजरना और आईपी/पोर्ट होना चाहिए()। इस तरह:

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

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

2

मेरे लिए बेहतर समाधान निम्न था। के बाद से कनेक्शन बंद करने की पहल सर्वर द्वारा किया गया था, 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)) 

के रूप में यह एक टीसीपी पते के python socket documentation में संकेत दिया गया था:

If supplied, source_address must be a 2-tuple (host, port) for the socket to bind to as its source address before connecting. If host or port are ‘’ or 0 respectively the OS default behavior will be used.

21

यहाँ पूरा कोड है कि मैं है मैंने परीक्षण किया है और बिल्कुल मुझे "उपयोग में पहले से ही पता" त्रुटि नहीं देता है। आप इसे फ़ाइल में सहेज सकते हैं और फ़ाइल को उन 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 
+0

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

+0

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

10

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

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

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

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

लिनक्स नेटवर्क कॉन्फ़िगरेशन का वास्तव में अच्छा स्पष्टीकरण https://serverfault.com/questions/212093/how-to-reduce-number-of-sockets-in-time-wait पाया जा सकता है।

+0

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

+5

आपको [टॉम्स] (http://hea-www.harvard.edu/~fine/Tech/addrinuse.html) नेटवर्क गाइड से कॉपी और पेस्ट किए गए वाक्यों को निष्पक्ष और उद्धृत करना चाहिए। कृपया अपना उत्तर सही करें। – HelloWorld

0

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

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 
6

आप बंधन से पहले allow_reuse_address निर्धारित करने की आवश्यकता के लिए।

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() 

यह बाध्यकारी इससे पहले कि हम झंडे स्थापित करने के लिए एक मौका मिला से सर्वर से बचाता है: SimpleHTTPServer के बजाय इस स्निपेट चलाते हैं।

+0

टीसीपीएस सर्वर को उप-वर्गीकृत करना और विशेषता को ओवरराइड करना बहुत आसान लगता है, [मेरा उत्तर] देखें (http://stackoverflow.com/questions/6380057/python-binding-socket-address-already-in-use/35363839#35363839) के लिए उदाहरण – Andrei

0

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

+0

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

संबंधित मुद्दे