paint-brush
टीसीपी रीट्रांसमिशन को नियंत्रित करें: डेटा हानि को रोकने के लिए प्रारंभिक समस्या का पता लगानाद्वारा@koilas
1,256 रीडिंग
1,256 रीडिंग

टीसीपी रीट्रांसमिशन को नियंत्रित करें: डेटा हानि को रोकने के लिए प्रारंभिक समस्या का पता लगाना

द्वारा Oleg Tolmashov14m2024/01/23
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

इस लेख में, मैं टीसीपी संचार के एक महत्वपूर्ण पहलू पर चर्चा करूंगा: उन परिदृश्यों को प्रभावी ढंग से प्रबंधित करना जहां सर्वर प्रतिक्रिया देने में विफल रहता है। मैं एक विशिष्ट परिदृश्य पर ध्यान केंद्रित करता हूं जहां एप्लिकेशन सर्वर से कोई एप्लिकेशन-स्तरीय प्रतिक्रिया प्राप्त किए बिना केवल टीसीपी पर डेटा भेजता है। यह अन्वेषण एप्लिकेशन के परिप्रेक्ष्य से टीसीपी संचार को कवर करता है, एप्लिकेशन परत और अंतर्निहित ओएस संचालन दोनों पर प्रकाश डालता है। आप सीखेंगे कि अनुत्तरदायी सर्वर इंस्टेंस के दौरान डेटा हानि से बचने के लिए प्रभावी टाइमआउट कैसे सेट करें।
featured image - टीसीपी रीट्रांसमिशन को नियंत्रित करें: डेटा हानि को रोकने के लिए प्रारंभिक समस्या का पता लगाना
Oleg Tolmashov HackerNoon profile picture
0-item
1-item

परिचय

इस लेख में, मैं टीसीपी संचार के एक महत्वपूर्ण पहलू पर चर्चा करूंगा: उन परिदृश्यों को प्रभावी ढंग से प्रबंधित करना जहां सर्वर प्रतिक्रिया देने में विफल रहता है। मैं एक विशिष्ट परिदृश्य पर ध्यान केंद्रित करता हूं जहां एप्लिकेशन सर्वर से कोई एप्लिकेशन-स्तरीय प्रतिक्रिया प्राप्त किए बिना केवल टीसीपी पर डेटा भेजता है।


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

साइलेंट टीसीपी सर्वर की चुनौती

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


इसे बेहतर ढंग से समझने के लिए, आइए रूबी का उपयोग करके एक नमूना टीसीपी सर्वर और क्लाइंट बनाएं। यह हमें संचार प्रक्रिया को क्रियान्वित करने का निरीक्षण करने की अनुमति देगा।


server.rb :

 # server.rb require 'socket' require 'time' $stdout.sync = true puts 'starting tcp server...' server = TCPServer.new(1234) puts 'started tcp server on port 1234' loop do Thread.start(server.accept) do |client| puts 'new client' while (message = client.gets) puts "#{Time.now}]: #{message.chomp}" end client.close end end


और client.rb :

 require 'socket' require 'time' $stdout.sync = true socket = Socket.tcp('server', 1234) loop do puts "sending message to the socket at #{Time.now}" socket.write "Hello from client\n" sleep 1 end


और आइए इस Dockerfile का उपयोग करके इस सेटअप को कंटेनरों में समाहित करें:

 FROM ruby:2.7 RUN apt-get update && apt-get install -y tcpdump # Set the working directory in the container WORKDIR /usr/src/app # Copy the current directory contents into the container at /usr/src/app COPY . .


और docker-compose.yml :

 version: '3' services: server: build: context: . dockerfile: Dockerfile command: ruby server.rb volumes: - .:/usr/src/app ports: - "1234:1234" healthcheck: test: ["CMD", "sh", "-c", "nc -z localhost 1234"] interval: 1s timeout: 1s retries: 2 networks: - net client: build: context: . dockerfile: Dockerfile command: ruby client.rb volumes: - .:/usr/src/app - ./data:/data depends_on: - server networks: - net networks: net:


अब, हम इसे docker compose up के साथ आसानी से चला सकते हैं और लॉग में देख सकते हैं कि क्लाइंट कैसे संदेश भेजता है और सर्वर इसे कैसे प्राप्त करता है:


 $ docker compose up [+] Running 2/0 ⠿ Container tcp_tests-server-1 Created 0.0s ⠿ Container tcp_tests-client-1 Created 0.0s Attaching to tcp_tests-client-1, tcp_tests-server-1 tcp_tests-server-1 | starting tcp server... tcp_tests-server-1 | started tcp server on port 1234 tcp_tests-client-1 | sending message to the socket at 2024-01-14 08:59:08 +0000 tcp_tests-server-1 | new client tcp_tests-server-1 | 2024-01-14 08:59:08 +0000]: Hello from client tcp_tests-server-1 | 2024-01-14 08:59:09 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-14 08:59:09 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 08:59:10 +0000 tcp_tests-server-1 | 2024-01-14 08:59:10 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-14 08:59:11 +0000 tcp_tests-server-1 | 2024-01-14 08:59:11 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-14 08:59:12 +0000 tcp_tests-server-1 | 2024-01-14 08:59:12 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-14 08:59:13 +0000

अब तक बहुत आसान है, हुह?


हालाँकि, स्थिति तब और दिलचस्प हो जाती है जब हम एक सक्रिय कनेक्शन के लिए सर्वर विफलता का अनुकरण करते हैं।


हम इसे docker compose stop server उपयोग करके करते हैं:


 tcp_tests-server-1 | 2024-01-14 09:04:23 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:24 +0000 tcp_tests-server-1 | 2024-01-14 09:04:24 +0000]: Hello from client tcp_tests-server-1 exited with code 1 tcp_tests-server-1 exited with code 0 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:25 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:26 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:27 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:28 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:29 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:30 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:31 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:04:32 +0000


हमने देखा कि सर्वर अब ऑफ़लाइन है, फिर भी क्लाइंट ऐसा व्यवहार करता है जैसे कि कनेक्शन अभी भी सक्रिय है, बिना किसी हिचकिचाहट के सॉकेट में डेटा भेजना जारी रखता है।


इससे मुझे सवाल उठता है कि ऐसा क्यों होता है। तार्किक रूप से, क्लाइंट को कम समय, संभवतः कुछ सेकंड के भीतर सर्वर के डाउनटाइम का पता लगाना चाहिए, क्योंकि टीसीपी अपने पैकेट के लिए पावती प्राप्त करने में विफल रहता है, जिससे कनेक्शन बंद हो जाता है।


हालाँकि, वास्तविक परिणाम इस अपेक्षा से भिन्न था:

 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:11 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:12 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:13 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:14 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:15 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:16 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:17 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:18 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:19 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:20 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:21 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:22 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-14 09:20:23 +0000 tcp_tests-client-1 | client.rb:11:in `write': No route to host (Errno::EHOSTUNREACH) tcp_tests-client-1 | from client.rb:11:in `block in <main>' tcp_tests-client-1 | from client.rb:9:in `loop' tcp_tests-client-1 | from client.rb:9:in `<main>' tcp_tests-client-1 exited with code 1


वास्तव में, ग्राहक 15 मिनट तक बाधित कनेक्शन से अनजान रह सकता है!


पता लगाने में इस देरी का क्या कारण है? आइए कारणों को समझने के लिए गहराई से जानें।

गहराई में: टीसीपी संचार यांत्रिकी

इस मामले को पूरी तरह से कवर करने के लिए, आइए पहले बुनियादी सिद्धांतों पर दोबारा गौर करें, इसके बाद यह जांच करें कि क्लाइंट टीसीपी पर डेटा कैसे प्रसारित करता है।

टीसीपी मूल बातें

टीसीपी प्रवाह को दर्शाने वाला मूल चित्र यहां दिया गया है:

टीसीपी प्रवाह

एक बार कनेक्शन स्थापित हो जाने पर, प्रत्येक संदेश के प्रसारण में आम तौर पर दो प्रमुख चरण शामिल होते हैं:


  1. क्लाइंट पीएसएच (पुश) ध्वज के साथ चिह्नित संदेश भेजता है।


  2. सर्वर एक ACK (पावती) प्रतिक्रिया भेजकर रसीद की पुष्टि करता है।

एप्लिकेशन और सॉकेट के बीच संचार

नीचे एक सरलीकृत अनुक्रम आरेख है जो एक एप्लिकेशन द्वारा टीसीपी सॉकेट के उद्घाटन और उसके बाद के डेटा ट्रांसमिशन को दर्शाता है:


टीसीपी सॉकेट के साथ संचार


एप्लिकेशन दो ऑपरेशन करता है:

  1. एक टीसीपी सॉकेट खोलना


  2. खुले सॉकेट पर डेटा भेजना


उदाहरण के लिए, टीसीपी सॉकेट खोलते समय, जैसा कि रूबी के Socket.tcp(host, port) कमांड के साथ किया जाता है, सिस्टम सिंक्रोनाइज़ रूप से socket(2) सिस्टम कॉल का उपयोग करके एक सॉकेट बनाता है और फिर connect(2) सिस्टम कॉल के माध्यम से एक कनेक्शन स्थापित करता है। .


जहां तक डेटा भेजने की बात है, किसी एप्लिकेशन में socket.write('foo') जैसे कमांड का उपयोग मुख्य रूप से संदेश को सॉकेट के सेंड बफर में रखता है। इसके बाद यह उन बाइट्स की गिनती लौटाता है जिन्हें सफलतापूर्वक कतारबद्ध किया गया था। नेटवर्क पर गंतव्य होस्ट तक इस डेटा का वास्तविक प्रसारण टीसीपी/आईपी स्टैक द्वारा अतुल्यकालिक रूप से प्रबंधित किया जाता है।


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

क्या होता है जब टीसीपी सर्वर डाउन हो जाता है?

चूँकि सर्वर ACK फ़्लैग के साथ प्रतिक्रिया नहीं करता है, हमारा TCP स्टैक अंतिम अस्वीकृत पैकेट का पुनः प्रसारण आरंभ करेगा:


जब सर्वर डाउन हो जाता है तो क्या होता है


यहां दिलचस्प बात यह है कि डिफ़ॉल्ट रूप से टीसीपी घातीय बैकऑफ़ के साथ 15 पुन: प्रसारण करता है जिसके परिणामस्वरूप लगभग 15 मिनट का पुन: प्रयास होता है!


आप जांच सकते हैं कि आपके होस्ट पर कितने पुनः प्रयास सेट हैं:

 $ sysctl net.ipv4.tcp_retries2 net.ipv4.tcp_retries2 = 15


दस्तावेज़ों में गोता लगाने के बाद, यह स्पष्ट हो रहा है; ip-sysctl.txt दस्तावेज़ कहता है:


15 का डिफ़ॉल्ट मान 924.6 सेकंड का एक काल्पनिक टाइमआउट उत्पन्न करता है और प्रभावी टाइमआउट के लिए निचली सीमा है। टीसीपी प्रभावी रूप से पहले आरटीओ पर टाइम आउट करेगा जो काल्पनिक टाइमआउट से अधिक है।


इस अवधि के दौरान, स्थानीय टीसीपी सॉकेट सक्रिय है और डेटा स्वीकार करता है। जब सभी पुनः प्रयास किए जाते हैं, तो सॉकेट बंद हो जाता है, और एप्लिकेशन को सॉकेट पर कुछ भी भेजने के प्रयास में एक त्रुटि प्राप्त होती है।

यह आमतौर पर कोई मुद्दा क्यों नहीं है?

ऐसा परिदृश्य जहां टीसीपी सर्वर अप्रत्याशित रूप से फिन या आरएसटी टीसीपी झंडे भेजे बिना बंद हो जाता है, या जब कनेक्टिविटी समस्याएं होती हैं, तो यह काफी सामान्य है। तो फिर ऐसी स्थितियों पर अक्सर ध्यान क्यों नहीं जाता?


क्योंकि, ज्यादातर मामलों में, सर्वर एप्लिकेशन स्तर पर कुछ प्रतिक्रिया देता है। उदाहरण के लिए, HTTP प्रोटोकॉल के लिए सर्वर को हर अनुरोध का जवाब देने की आवश्यकता होती है। मूल रूप से, जब आपके पास connection.get जैसा कोड होता है, तो यह दो मुख्य ऑपरेशन करता है:

  1. आपके पेलोड को टीसीपी सॉकेट के सेंड बफ़र पर लिखता है।

    इस बिंदु से आगे, ऑपरेटिंग सिस्टम का टीसीपी स्टैक इन पैकेटों को टीसीपी गारंटी के साथ दूरस्थ सर्वर तक विश्वसनीय रूप से पहुंचाने की जिम्मेदारी लेता है।


  2. दूरस्थ सर्वर से टीसीपी प्राप्त बफ़र में प्रतिक्रिया की प्रतीक्षा की जा रही है


    आमतौर पर, एप्लिकेशन उसी टीसीपी सॉकेट के प्राप्तकर्ता बफर से नॉनब्लॉकिंग रीड्स का उपयोग करते हैं।


यह दृष्टिकोण मामलों को काफी सरल बनाता है क्योंकि, ऐसे मामलों में, हम आसानी से एप्लिकेशन स्तर पर एक टाइमआउट सेट कर सकते हैं और यदि निर्धारित समय सीमा के भीतर सर्वर से कोई प्रतिक्रिया नहीं मिलती है तो सॉकेट बंद कर सकते हैं।


हालाँकि, जब हम सर्वर से किसी भी प्रतिक्रिया की उम्मीद नहीं करते हैं (टीसीपी स्वीकृतियों को छोड़कर), तो एप्लिकेशन स्तर से कनेक्शन की स्थिति निर्धारित करना कम सरल हो जाता है

लंबे टीसीपी रिट्रांसमिशन का प्रभाव

अब तक, हमने निम्नलिखित स्थापित किया है:

  1. एप्लिकेशन एक टीसीपी सॉकेट खोलता है और नियमित रूप से उसमें डेटा लिखता है।


  2. कुछ बिंदु पर, टीसीपी सर्वर आरएसटी पैकेट भेजे बिना ही बंद हो जाता है, और प्रेषक का टीसीपी स्टैक अंतिम अज्ञात पैकेट को फिर से भेजना शुरू कर देता है।


  3. उस सॉकेट पर लिखे गए अन्य सभी पैकेट सॉकेट के सेंड बफ़र में पंक्तिबद्ध हैं।


  4. डिफ़ॉल्ट रूप से, टीसीपी स्टैक घातीय बैकऑफ़ को नियोजित करते हुए, 15 बार अनजाने पैकेट को पुनः प्रेषित करने का प्रयास करता है, जिसके परिणामस्वरूप लगभग 924.6 सेकंड (लगभग 15 मिनट) की अवधि होती है।


इस 15 मिनट की अवधि के दौरान, स्थानीय टीसीपी सॉकेट खुला रहता है, और एप्लिकेशन तब तक डेटा लिखना जारी रखता है जब तक कि सेंड बफर पूरा न हो जाए (जिसमें आमतौर पर सीमित क्षमता होती है, अक्सर केवल कुछ मेगाबाइट)। जब सभी पुन: प्रसारण के बाद सॉकेट को अंततः बंद के रूप में चिह्नित किया जाता है, तो सेंड बफर में सभी डेटा खो जाता है


ऐसा इसलिए है क्योंकि सेंड बफ़र पर लिखने के बाद एप्लिकेशन इसके लिए ज़िम्मेदार नहीं है, और ऑपरेटिंग सिस्टम बस इस डेटा को हटा देता है।


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


हालाँकि, इस पता लगाने की विधि की प्रभावशीलता भेजे जा रहे डेटा के आकार पर निर्भर करती है।


उदाहरण के लिए, यदि एप्लिकेशन मेट्रिक्स जैसे केवल कुछ बाइट्स भेजता है, तो यह 15 मिनट की समय सीमा के भीतर सेंड बफर को पूरी तरह से नहीं भर सकता है।


तो, इस अवधि के दौरान 15 मिनट के पुन: प्रसारण और डेटा खोने से बचने के लिए टीसीपी सर्वर डाउन होने पर स्पष्ट रूप से निर्धारित टाइमआउट के साथ कनेक्शन बंद करने के लिए कोई तंत्र कैसे कार्यान्वित किया जा सकता है?

सॉकेट विकल्प का उपयोग करके टीसीपी रीट्रांसमिशन टाइमआउट

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


एक अधिक सटीक दृष्टिकोण TCP_USER_TIMEOUT सॉकेट विकल्प का उपयोग करके विशेष रूप से हमारे सॉकेट के लिए रीट्रांसमिशन टाइमआउट सेट करना है। इस विकल्प को नियोजित करके, टीसीपी स्टैक स्वचालित रूप से सॉकेट को बंद कर देगा यदि पुन: प्रसारण निर्दिष्ट समय-सीमा के भीतर सफल नहीं होता है, चाहे विश्व स्तर पर निर्धारित टीसीपी पुन: प्रसारण की अधिकतम संख्या कुछ भी हो।


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


आइए इस सॉकेट विकल्प को client.rb में सेट करें:

 require 'socket' require 'time' $stdout.sync = true socket = Socket.tcp('server', 1234) # set 5 seconds restransmissions timeout socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_USER_TIMEOUT, 5000) loop do puts "sending message to the socket at #{Time.now}" socket.write "Hello from client\n" sleep 1 end


साथ ही, मेरी टिप्पणियों से, TCP_USER_TIMEOUT सॉकेट विकल्प macOS पर उपलब्ध नहीं है।


अब, docket compose up के साथ सब कुछ फिर से शुरू करें, और कुछ बिंदु पर, आइए docker compose stop server के साथ सर्वर को फिर से रोकें:

 $ docker compose up [+] Running 2/0 ⠿ Container tcp_tests-server-1 Created 0.0s ⠿ Container tcp_tests-client-1 Created 0.0s Attaching to tcp_tests-client-1, tcp_tests-server-1 tcp_tests-server-1 | starting tcp server... tcp_tests-server-1 | started tcp server on port 1234 tcp_tests-server-1 | new client tcp_tests-server-1 | 2024-01-20 12:37:38 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:38 +0000 tcp_tests-server-1 | 2024-01-20 12:37:39 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:39 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:40 +0000 tcp_tests-server-1 | 2024-01-20 12:37:40 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:41 +0000 tcp_tests-server-1 | 2024-01-20 12:37:41 +0000]: Hello from client tcp_tests-server-1 | 2024-01-20 12:37:42 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:42 +0000 tcp_tests-server-1 | 2024-01-20 12:37:43 +0000]: Hello from client tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:43 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:44 +0000 tcp_tests-server-1 | 2024-01-20 12:37:44 +0000]: Hello from client tcp_tests-server-1 exited with code 1 tcp_tests-server-1 exited with code 0 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:45 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:46 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:47 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:48 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:49 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:50 +0000 tcp_tests-client-1 | sending message to the socket at 2024-01-20 12:37:51 +0000 tcp_tests-client-1 | client.rb:11:in `write': Connection timed out (Errno::ETIMEDOUT) tcp_tests-client-1 | from client.rb:11:in `block in <main>' tcp_tests-client-1 | from client.rb:9:in `loop' tcp_tests-client-1 | from client.rb:9:in `<main>' tcp_tests-client-1 exited with code 1


~12:37:45 पर, मैंने सर्वर बंद कर दिया, और हमने देखा कि क्लाइंट को लगभग 5 सेकंड में ही Errno::ETIMEDOUT मिल गया, बढ़िया।


आइए docker exec -it tcp_tests-client-1 tcpdump -i any tcp port 1234 के साथ एक tcpdump कैप्चर करें:


tcpdump


यह ध्यान देने योग्य है कि टाइमआउट वास्तव में 5 सेकंड से कुछ अधिक समय में होता है। ऐसा इसलिए है क्योंकि TCP_USER_TIMEOUT से अधिक की जांच अगले पुनः प्रयास पर होती है। जब टीसीपी/आईपी स्टैक को पता चलता है कि टाइमआउट पार हो गया है, तो यह सॉकेट को बंद के रूप में चिह्नित करता है, और हमारे एप्लिकेशन को Errno::ETIMEDOUT त्रुटि प्राप्त होती है


इसके अलावा, यदि आप टीसीपी कीपलाइव्स का उपयोग कर रहे हैं, तो मैं क्लाउडफ्लेयर के इस लेख को देखने की सलाह देता हूं। इसमें TCP_USER_TIMEOUT को TCP Keepalives के साथ संयोजन में उपयोग करने की बारीकियों को शामिल किया गया है।