এই নিবন্ধে, আমি TCP যোগাযোগের একটি গুরুত্বপূর্ণ দিক সম্বোধন করব: কার্যকরভাবে এমন পরিস্থিতি পরিচালনা করা যেখানে সার্ভার প্রতিক্রিয়া জানাতে ব্যর্থ হয়। আমি একটি নির্দিষ্ট পরিস্থিতিতে ফোকাস করি যেখানে অ্যাপ্লিকেশনটি সার্ভার থেকে কোনো অ্যাপ্লিকেশন-স্তরের প্রতিক্রিয়া না পেয়ে শুধুমাত্র TCP-এর মাধ্যমে ডেটা পাঠায়।
এই অন্বেষণটি অ্যাপ্লিকেশনের দৃষ্টিকোণ থেকে TCP যোগাযোগকে কভার করে, অ্যাপ্লিকেশন স্তর এবং অন্তর্নিহিত OS অপারেশন উভয়কেই হাইলাইট করে। প্রতিক্রিয়াশীল সার্ভার দৃষ্টান্তের সময় ডেটা ক্ষতি এড়াতে আপনি কীভাবে কার্যকর টাইমআউট সেট করবেন তা শিখবেন। আমি রুবিতে কোড উদাহরণ প্রদান করব, তবে ধারণাটি যেকোনো ভাষার জন্য একই থাকে।
কল্পনা করুন যে আপনি একটি অ্যাপ্লিকেশনের সাথে কাজ করছেন যা ধারাবাহিকভাবে একটি TCP সকেটের মাধ্যমে ডেটা প্রেরণ করে। যদিও TCP সংজ্ঞায়িত TCP স্ট্যাক কনফিগারেশনের মধ্যে ট্রান্সপোর্ট লেভেলে প্যাকেট ডেলিভারি নিশ্চিত করার জন্য ডিজাইন করা হয়েছে, এটি অ্যাপ্লিকেশন লেভেলে কী বোঝায় তা বিবেচনা করা আকর্ষণীয়।
এটি আরও ভালভাবে বোঝার জন্য, আসুন রুবি ব্যবহার করে একটি নমুনা টিসিপি সার্ভার এবং ক্লায়েন্ট তৈরি করি। এটি আমাদের কর্মে যোগাযোগ প্রক্রিয়া পর্যবেক্ষণ করার অনুমতি দেবে।
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 তার প্যাকেটগুলির জন্য স্বীকৃতি পেতে ব্যর্থ হয়, যা সংযোগ বন্ধ করার অনুরোধ করে।
যাইহোক, প্রকৃত ফলাফল এই প্রত্যাশা থেকে ভিন্ন:
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 মিনিট পর্যন্ত বিঘ্নিত সংযোগ সম্পর্কে অবগত থাকতে পারে!
সনাক্তকরণে এই বিলম্বের কারণ কী? এর কারণগুলি বুঝতে আরও গভীরে যাওয়া যাক।
এই কেসটি সম্পূর্ণভাবে কভার করার জন্য, আসুন প্রথমে প্রাথমিক নীতিগুলি পর্যালোচনা করি, তারপরে ক্লায়েন্ট কীভাবে TCP-এর মাধ্যমে ডেটা প্রেরণ করে তা পরীক্ষা করে দেখা যাক।
এখানে টিসিপি প্রবাহ চিত্রিত করার প্রাথমিক চিত্রটি রয়েছে:
একবার একটি সংযোগ স্থাপন করা হলে, প্রতিটি বার্তার ট্রান্সমিশনে সাধারণত দুটি মূল পদক্ষেপ জড়িত থাকে:
ক্লায়েন্ট বার্তা পাঠায়, PSH (পুশ) পতাকা দিয়ে চিহ্নিত।
সার্ভার একটি ACK (স্বীকৃতি) প্রতিক্রিয়া ফেরত পাঠিয়ে প্রাপ্তি স্বীকার করে।
নীচে একটি সরলীকৃত সিকোয়েন্স ডায়াগ্রাম রয়েছে যা একটি অ্যাপ্লিকেশন দ্বারা একটি TCP সকেট খোলার এবং এর মাধ্যমে পরবর্তী ডেটা ট্রান্সমিশনকে চিত্রিত করে:
অ্যাপ্লিকেশন দুটি অপারেশন করে:
একটি TCP সকেট খোলা হচ্ছে
খোলা সকেটে ডেটা পাঠানো হচ্ছে
উদাহরণস্বরূপ, রুবির Socket.tcp(host, port)
কমান্ডের সাহায্যে একটি TCP সকেট খোলার সময়, সিস্টেমটি socket(2)
সিস্টেম কল ব্যবহার করে সিঙ্ক্রোনাসভাবে একটি সকেট তৈরি করে এবং তারপর connect(2)
সিস্টেম কলের মাধ্যমে একটি সংযোগ স্থাপন করে। .
ডেটা পাঠানোর ক্ষেত্রে, একটি অ্যাপ্লিকেশনে socket.write('foo')
এর মতো একটি কমান্ড ব্যবহার করে প্রাথমিকভাবে সকেটের পাঠান বাফারে বার্তাটি রাখে। এটি তারপর সফলভাবে সারিবদ্ধ বাইটের সংখ্যা ফেরত দেয়। নেটওয়ার্কের মাধ্যমে গন্তব্য হোস্টে এই ডেটার প্রকৃত ট্রান্সমিশন টিসিপি/আইপি স্ট্যাক দ্বারা অসিঙ্ক্রোনাসভাবে পরিচালিত হয়।
এর মানে হল যে যখন একটি অ্যাপ্লিকেশন সকেটে লেখে, এটি সরাসরি নেটওয়ার্ক ক্রিয়াকলাপের সাথে জড়িত নয় এবং সংযোগটি এখনও সক্রিয় আছে কিনা তা রিয়েল-টাইমে নাও হতে পারে। এটি প্রাপ্ত একমাত্র নিশ্চিতকরণ হল যে বার্তাটি সফলভাবে TCP সেন্ড বাফারে যোগ করা হয়েছে।
যেহেতু সার্ভার একটি ACK পতাকার সাথে সাড়া দেয় না, আমাদের TCP স্ট্যাক শেষ অস্বীকৃত প্যাকেটের একটি পুনঃপ্রচার শুরু করবে:
এখানে মজার বিষয় হল যে ডিফল্টভাবে TCP 15টি রিট্রান্সমিশন করে এক্সপোনেনশিয়াল ব্যাকঅফ যার ফলে প্রায় 15 মিনিটের পুনঃপ্রচার হয়!
আপনার হোস্টে কতগুলি পুনঃপ্রচার সেট করা হয়েছে তা আপনি পরীক্ষা করতে পারেন:
$ sysctl net.ipv4.tcp_retries2 net.ipv4.tcp_retries2 = 15
ডক্সে ডুব দেওয়ার পরে, এটি স্পষ্ট হয়ে উঠছে; ip-sysctl.txt ডকুমেন্টেশন বলে:
15-এর ডিফল্ট মান 924.6 সেকেন্ডের একটি অনুমানমূলক টাইমআউট প্রদান করে এবং কার্যকর টাইমআউটের জন্য একটি নিম্ন সীমা। TCP কার্যকরভাবে প্রথম RTO-তে সময় শেষ করবে যা অনুমানমূলক সময়সীমা অতিক্রম করে।
এই সময়ের মধ্যে, স্থানীয় TCP সকেট জীবিত থাকে এবং ডেটা গ্রহণ করে। যখন সমস্ত পুনঃপ্রচার করা হয়, তখন সকেটটি বন্ধ হয়ে যায়, এবং অ্যাপ্লিকেশনটি সকেটে কিছু পাঠানোর প্রচেষ্টায় একটি ত্রুটি পায়।
FIN বা RST TCP ফ্ল্যাগ না পাঠিয়েই TCP সার্ভার অপ্রত্যাশিতভাবে ডাউন হয়ে যায় বা কানেক্টিভিটি সমস্যা দেখা দিলে তা খুবই সাধারণ। তাহলে কেন এই ধরনের পরিস্থিতি প্রায়ই অলক্ষিত হয়?
কারণ, বেশিরভাগ ক্ষেত্রে, সার্ভার অ্যাপ্লিকেশন স্তরে কিছু প্রতিক্রিয়া দিয়ে প্রতিক্রিয়া জানায়। উদাহরণস্বরূপ, এইচটিটিপি প্রোটোকলের জন্য সার্ভারকে প্রতিটি অনুরোধে সাড়া দিতে হবে। মূলত, যখন আপনার কাছে connection.get
এর মতো কোড থাকে, তখন এটি দুটি প্রধান কাজ করে:
আপনার পেলোড টিসিপি সকেটের পাঠান বাফারে লেখে।
এই মুহূর্ত থেকে, অপারেটিং সিস্টেমের TCP স্ট্যাক এই প্যাকেটগুলিকে TCP গ্যারান্টি সহ রিমোট সার্ভারে নির্ভরযোগ্যভাবে সরবরাহ করার দায়িত্ব নেয়।
দূরবর্তী সার্ভার থেকে একটি TCP গ্রহণকারী বাফারে একটি প্রতিক্রিয়ার জন্য অপেক্ষা করা হচ্ছে৷
সাধারণত, অ্যাপ্লিকেশনগুলি একই TCP সকেটের রিসিভিং বাফার থেকে ননব্লকিং রিড ব্যবহার করে।
এই পদ্ধতিটি বিষয়গুলিকে যথেষ্ট সহজ করে তোলে কারণ, এই ধরনের ক্ষেত্রে, আমরা সহজেই অ্যাপ্লিকেশন স্তরে একটি টাইমআউট সেট করতে পারি এবং একটি নির্দিষ্ট সময়সীমার মধ্যে সার্ভার থেকে কোনও প্রতিক্রিয়া না থাকলে সকেটটি বন্ধ করতে পারি।
যাইহোক, যখন আমরা সার্ভার থেকে কোন প্রতিক্রিয়া আশা করি না (টিসিপি স্বীকৃতি ব্যতীত), তখন অ্যাপ্লিকেশন স্তর থেকে সংযোগের স্থিতি নির্ধারণ করা কম সোজা হয়ে যায়।
এখনও অবধি, আমরা নিম্নলিখিতগুলি স্থাপন করেছি:
অ্যাপ্লিকেশনটি একটি TCP সকেট খোলে এবং নিয়মিত এটিতে ডেটা লেখে।
কিছু সময়ে, TCP সার্ভার এমনকি একটি RST প্যাকেট না পাঠিয়েও ডাউন হয়ে যায়, এবং প্রেরকের TCP স্ট্যাক শেষ অস্বীকৃত প্যাকেটটি পুনরায় প্রেরণ করা শুরু করে।
সেই সকেটে লেখা অন্যান্য সমস্ত প্যাকেট সকেটের পাঠান বাফারে সারিবদ্ধ।
ডিফল্টরূপে, TCP স্ট্যাক 15 বার অস্বীকৃত প্যাকেটটি পুনরায় প্রেরণ করার চেষ্টা করে, সূচকীয় ব্যাকঅফ নিয়োগ করে, যার ফলে প্রায় 924.6 সেকেন্ড (প্রায় 15 মিনিট) সময়কাল হয়।
এই 15-মিনিটের সময়কালে, স্থানীয় TCP সকেট খোলা থাকে, এবং প্রেরণ বাফার পূর্ণ না হওয়া পর্যন্ত অ্যাপ্লিকেশন এটিতে ডেটা লিখতে থাকে (যা সাধারণত সীমিত ক্ষমতা থাকে, প্রায়শই মাত্র কয়েক মেগাবাইট)। যখন সমস্ত পুনঃপ্রচারের পরে সকেটটি শেষ পর্যন্ত বন্ধ হিসাবে চিহ্নিত করা হয়, সেন্ড বাফারের সমস্ত ডেটা হারিয়ে যায় ।
এর কারণ হল সেন্ড বাফারে লেখার পরে অ্যাপ্লিকেশনটি আর এর জন্য দায়ী নয় এবং অপারেটিং সিস্টেম কেবল এই ডেটা বাতিল করে দেয়।
অ্যাপ্লিকেশনটি শুধুমাত্র সনাক্ত করতে পারে যে সংযোগটি ভেঙে গেছে যখন TCP সকেটের পাঠান বাফারটি পূর্ণ হয়ে যায়। এই ধরনের ক্ষেত্রে, সকেটে লেখার চেষ্টা করা অ্যাপ্লিকেশনটির মূল থ্রেডকে অবরুদ্ধ করবে, এটি পরিস্থিতি পরিচালনা করার অনুমতি দেবে।
যাইহোক, এই সনাক্তকরণ পদ্ধতির কার্যকারিতা পাঠানো ডেটার আকারের উপর নির্ভর করে।
উদাহরণস্বরূপ, যদি অ্যাপ্লিকেশনটি শুধুমাত্র কয়েকটি বাইট পাঠায়, যেমন মেট্রিক্স, এটি এই 15-মিনিটের সময়সীমার মধ্যে পাঠানোর বাফারটি সম্পূর্ণরূপে পূরণ করতে পারে না।
সুতরাং, এই সময়ের মধ্যে 15 মিনিটের রিট্রান্সমিশন এবং ডেটা হারানো এড়াতে যখন TCP সার্ভার ডাউন থাকে তখন একটি সুস্পষ্টভাবে সেট করা সময়সীমার সাথে সংযোগ বন্ধ করার জন্য কীভাবে কেউ একটি প্রক্রিয়া প্রয়োগ করতে পারে?
ব্যক্তিগত নেটওয়ার্কগুলিতে, বিস্তৃত পুনঃপ্রচারগুলি সাধারণত প্রয়োজনীয় নয়, এবং শুধুমাত্র সীমিত সংখ্যক পুনঃপ্রয়াসের চেষ্টা করার জন্য TCP স্ট্যাক কনফিগার করা সম্ভব। যাইহোক, এই কনফিগারেশন সমগ্র নোডের জন্য বিশ্বব্যাপী প্রযোজ্য। যেহেতু একাধিক অ্যাপ্লিকেশন প্রায়ই একই নোডে চলে, তাই এই ডিফল্ট মান পরিবর্তন করলে অপ্রত্যাশিত পার্শ্বপ্রতিক্রিয়া হতে পারে।
আরও সুনির্দিষ্ট পদ্ধতি হল TCP_USER_TIMEOUT সকেট বিকল্প ব্যবহার করে আমাদের সকেটের জন্য একচেটিয়াভাবে একটি পুনঃপ্রচারের সময়সীমা সেট করা। এই বিকল্পটি ব্যবহার করার মাধ্যমে, বিশ্বব্যাপী TCP পুনঃপ্রচারের সর্বোচ্চ সংখ্যা নির্বিশেষে নির্দিষ্ট সময়ের মধ্যে পুনঃপ্রচার সফল না হলে TCP স্ট্যাক স্বয়ংক্রিয়ভাবে সকেট বন্ধ করে দেবে।
অ্যাপ্লিকেশন স্তরে, এর ফলে একটি বদ্ধ সকেটে ডেটা লেখার চেষ্টা করার সময় একটি ত্রুটি পাওয়া যায়, যা সঠিক ডেটা ক্ষতি প্রতিরোধ পরিচালনার জন্য অনুমতি দেয়।
আসুন 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 এ, আমি সার্ভার বন্ধ করেছিলাম, এবং আমরা দেখেছি যে ক্লায়েন্টটি Errno::ETIMEDOUT
প্রায় 5 সেকেন্ডের মধ্যে পেয়েছে, দুর্দান্ত।
docker exec -it tcp_tests-client-1 tcpdump -i any tcp port 1234
সাহায্যে একটি tcpdump ক্যাপচার করা যাক:
এটা লক্ষণীয় যে টাইমআউট আসলে 5 সেকেন্ডের একটু বেশি সময়ে ঘটে। এর কারণ হল TCP_USER_TIMEOUT অতিক্রম করার চেক পরবর্তী পুনঃপ্রচেষ্টাতে ঘটে। যখন TCP/IP স্ট্যাক সনাক্ত করে যে সময়সীমা অতিক্রম করা হয়েছে, তখন এটি সকেটটিকে বন্ধ হিসাবে চিহ্নিত করে এবং আমাদের অ্যাপ্লিকেশনটি Errno::ETIMEDOUT
ত্রুটি পায়
এছাড়াও, আপনি যদি TCP Keepalives ব্যবহার করেন, আমি Cloudflare থেকে এই নিবন্ধটি চেক করার পরামর্শ দিচ্ছি। এটি টিসিপি কিপলাইভের সাথে একত্রে TCP_USER_TIMEOUT ব্যবহার করার সূক্ষ্মতা কভার করে।