एक टीसीपी लोकल सॉकेट एड्रेस कब तक बंद होने के बाद उपलब्ध नहीं होता है?


13

लिनक्स पर (मेरे लाइव सर्वर RHEL 5.5 पर हैं - नीचे दिए गए LXR लिंक उस कर्नेल संस्करण में हैं), man 7 ipकहते हैं:

जब तक SO_REUSEADDR ध्वज सेट नहीं किया गया है, टीसीपी स्थानीय सॉकेट पता बंद होने के बाद कुछ समय के लिए अनुपलब्ध है।

मैं उपयोग नहीं कर रहा हूं SO_REUSEADDR। "कुछ समय" कब तक है? मैं कैसे पता लगा सकता हूं कि यह कितना लंबा है, और मैं इसे कैसे बदल सकता हूं?

मैं इसके चारों ओर गुगली कर रहा हूं, और कुछ सूचनाओं के बारे में पता चला है, जिनमें से कोई भी वास्तव में एक एप्लिकेशन प्रोग्रामर के दृष्टिकोण से इसकी व्याख्या नहीं करता है। अर्थात:

  • TCP_TIMEWAIT_LEN में net/tcp.h"TIME-WAIT स्थिति को नष्ट करने के लिए कितनी देर तक प्रतीक्षा करें", और "60% पर निर्धारित" है
  • / proc / sys / net / ipv4 / tcp_fin_timeout "स्टेट फिन- वॉइट -2 में सॉकेट रखने का समय है, अगर यह हमारी तरफ से बंद था", और "डिफ़ॉल्ट मान 60sec है"

जहां मैं ठोकर मारता हूं वह टीसीपी जीवन चक्र के कर्नेल मॉडल के बीच के अंतर को पाटने में है, और बंदरगाहों के प्रोग्रामर का मॉडल अनुपलब्ध है, अर्थात यह समझने में कि ये राज्य "कुछ समय" से कैसे संबंधित हैं।


@ कालेब: टैग के बारे में, बाइंड एक सिस्टम कॉल भी है! man 2 bindअगर आपको मुझ पर विश्वास नहीं है तो कोशिश करें । बेशक, यह शायद पहली बात नहीं है कि यूनिक्स के लोग सोचते हैं कि जब कोई "बाइंड" कहता है, तो यह काफी उचित है।
टॉम एंडरसन

मुझे इसके उपयोग के बारे में अच्छी तरह से पता था bind, लेकिन यहाँ टैग विशेष रूप से DNS सर्वर पर लागू होता है। हमारे पास हर संभव सिस्टम कॉल के लिए टैग नहीं हैं।
कालेब

जवाबों:


14

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

मैंने C में एक छोटा प्रोग्राम हैक किया है जिसे आप संकलित कर सकते हैं और यह देखने के लिए उपयोग कर सकते हैं कि टाइमआउट कितना लंबा है:

#include <stdio.h>        /* fprintf() */
#include <string.h>       /* strerror() */
#include <errno.h>        /* errno */
#include <stdlib.h>       /* strtol() */
#include <signal.h>       /* signal() */
#include <sys/time.h>     /* struct timeval */
#include <unistd.h>       /* read(), write(), close(), gettimeofday() */
#include <sys/types.h>    /* socket() */
#include <sys/socket.h>   /* socket-related stuff */
#include <netinet/in.h>
#include <arpa/inet.h>    /* inet_ntoa() */
float elapsed_time(struct timeval before, struct timeval after);
int
main(int ac, char **av)
{
        int opt;
        int listen_fd = -1;
        unsigned short port = 0;
        struct sockaddr_in  serv_addr;
        struct timeval before_bind;
        struct timeval after_bind;

        while (-1 != (opt = getopt(ac, av, "p:"))) {
                switch (opt) {
                case 'p':
                        port = (unsigned short)atoi(optarg);
                        break;
                }
        }

        if (0 == port) {
                fprintf(stderr, "Need a port to listen on\n");
                return 2;
        }

        if (0 > (listen_fd = socket(AF_INET, SOCK_STREAM, 0))) {
                fprintf(stderr, "Opening socket: %s\n", strerror(errno));
                return 1;
        }

        memset(&serv_addr, '\0', sizeof(serv_addr));
        serv_addr.sin_family      = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port        = htons(port);

        gettimeofday(&before_bind, NULL);
        while (0 > bind(listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
                fprintf(stderr, "binding socket to port %d: %s\n",
                        ntohs(serv_addr.sin_port),
                        strerror(errno));

                sleep(1);
        }
        gettimeofday(&after_bind, NULL);
        printf("bind took %.5f seconds\n", elapsed_time(before_bind, after_bind));

        printf("# Listening on port %d\n", ntohs(serv_addr.sin_port));
        if (0 > listen(listen_fd, 100)) {
                fprintf(stderr, "listen() on fd %d: %s\n",
                        listen_fd,
                        strerror(errno));
                return 1;
        }

        {
                struct sockaddr_in  cli_addr;
                struct timeval before;
                int newfd;
                socklen_t clilen;

                clilen = sizeof(cli_addr);

                if (0 > (newfd = accept(listen_fd, (struct sockaddr *)&cli_addr, &clilen))) {
                        fprintf(stderr, "accept() on fd %d: %s\n", listen_fd, strerror(errno));
                        exit(2);
                }
                gettimeofday(&before, NULL);
                printf("At %ld.%06ld\tconnected to: %s\n",
                        before.tv_sec, before.tv_usec,
                        inet_ntoa(cli_addr.sin_addr)
                );
                fflush(stdout);

                while (close(newfd) == EINTR) ;
        }

        if (0 > close(listen_fd))
                fprintf(stderr, "Closing socket: %s\n", strerror(errno));

        return 0;
}
float
elapsed_time(struct timeval before, struct timeval after)
{
        float r = 0.0;

        if (before.tv_usec > after.tv_usec) {
                after.tv_usec += 1000000;
                --after.tv_sec;
        }

        r = (float)(after.tv_sec - before.tv_sec)
                + (1.0E-6)*(float)(after.tv_usec - before.tv_usec);

        return r;
}

मैंने 3 अलग-अलग मशीनों पर इस कार्यक्रम की कोशिश की, और मुझे एक चर समय मिलता है, 55 और 59 सेकंड के बीच, जब कर्नेल एक गैर-रूट उपयोगकर्ता को सॉकेट को फिर से खोलने की अनुमति देने से इनकार करता है। मैंने उपरोक्त कोड को "ओपनर" नामक एक निष्पादन योग्य के लिए संकलित किया, और इसे इस तरह से चलाया:

./opener -p 7896; ./opener -p 7896

मैंने एक और खिड़की खोली और यह किया:

telnet otherhost 7896

किसी कनेक्शन को स्वीकार करने के लिए "ओपनर" का पहला उदाहरण है, फिर इसे बंद करें। "ओपनर" का दूसरा उदाहरण bind(2)टीसीपी पोर्ट 7896 के लिए हर सेकंड की कोशिश करता है । "ओपनर" 55 से 59 सेकंड देरी से रिपोर्ट करता है।

चारों ओर घूमते हुए, मुझे पता चलता है कि लोग ऐसा करने की सलाह देते हैं:

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

उस अंतराल को कम करने के लिए। यह मेरे लिए काम नहीं किया। जिन 4 लिनक्स मशीनों तक मेरी पहुंच थी उनमें से दो में 30 और दो के पास 60 थे। मैंने भी उस मूल्य को 10 से कम बताया। "ओपनर" कार्यक्रम से कोई फर्क नहीं पड़ा।

यह कर रहा हूं:

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

चीजों को बदल दिया। दूसरे "ओपनर" को अपना नया सॉकेट प्राप्त करने में केवल 3 सेकंड का समय लगा।


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

आपके अनुमान दिलचस्प थे! बस उन्हें हटाने के बजाय एक शीर्षक के साथ उन्हें ध्वजांकित करें, यह कारण की खोज करने के तरीके देता है!
फिलिप गाचौड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.