2012-03-24 14 views
17

के माध्यम से रूबी में सॉकेट टाइमआउट सेट करें, मैं एसओ_आरसीवीटीएमई सॉकेट विकल्प के माध्यम से रूबी में सॉकेट टाइमआउट बनाने की कोशिश कर रहा हूं, हालांकि ऐसा लगता है कि यह किसी भी हालिया * निक्स ऑपरेटिंग सिस्टम पर कोई प्रभाव नहीं पड़ता है।रूबी में SO_RCVTIMEO सॉकेट विकल्प

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

मुझे लगता है कि एक अनुरोध भेजा समय की राशि प्राप्त होगा इंतजार एक TCP सर्वर का एक सरल उदाहरण बनाया विषय here पर और runnable कोड में से एक फाइल करने के लिए समस्या को कम करने के प्रयास में माइक Perham उत्तम पोस्ट पढ़ा है अनुरोध में और फिर कनेक्शन बंद करें।

क्लाइंट सॉकेट बनाता है, प्राप्त समय समाप्ति 1 सेकंड होने के लिए सेट करता है, और उसके बाद सर्वर से कनेक्ट होता है। क्लाइंट सर्वर को 5 सेकंड के बाद सत्र बंद करने के लिए कहता है तो डेटा के लिए इंतजार कर रहा है।

ग्राहक एक दूसरे लेकिन इसके बजाय सफलतापूर्वक के बाद 5.

#!/usr/bin/env ruby 
require 'socket' 

def timeout 
    sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) 

    # Timeout set to 1 second 
    timeval = [1, 0].pack("l_2") 
    sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval 

    # Connect and tell the server to wait 5 seconds 
    sock.connect(Socket.pack_sockaddr_in(1234, '127.0.0.1')) 
    sock.write("5\n") 

    # Wait for data to be sent back 
    begin 
    result = sock.recvfrom(1024) 
    puts "session closed" 
    rescue Errno::EAGAIN 
    puts "timed out!" 
    end 
end 

Thread.new do 
    server = TCPServer.new(nil, 1234) 
    while (session = server.accept) 
    request = session.gets 
    sleep request.to_i 
    session.close 
    end 
end 

timeout 

मैं भी (जो स्वचालित रूप से जोड़ता है) एक TCPSocket साथ ही बात कर की कोशिश की है कनेक्शन बंद कर देता है और देखा है समान कोड के बाद का समय-समाप्त करना चाहिए redis और अन्य परियोजनाओं में।

साथ ही, इस बात की पुष्टि कर सकते हैं getsockopt इस तरह फोन करके उस विकल्प को निर्धारित किया गया है:

sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).inspect 

की स्थापना करता है इस सॉकेट विकल्प वास्तव में किसी के लिए भी काम करते हैं?

+0

प्रश्न इस प्रकार का कर दिया गया है पहले पोस्ट किया गया था, और ऐसा लगता है कि 'आरईवीवी कॉल' के आस-पास रुबी की 'टाइमआउट' लाइब्रेरी का उपयोग करना सबसे अच्छा जवाब था। – Linuxios

+0

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

उत्तर

22

आप यह कुशलता से कर सकते हैं select रूबी के आईओ कक्षा से।

IO::select 4 पैरामीटर लेता है। पहले तीन मॉनिटर करने के लिए सॉकेट के सरणी हैं और अंतिम एक टाइमआउट (सेकंड में निर्दिष्ट) है।

जिस तरह से चुनिंदा काम यह है कि यह अवरुद्ध करके किसी दिए गए ऑपरेशन के लिए तैयार आईओ ऑब्जेक्ट्स की सूचियां बनाता है जब तक कि उनमें से कम से कम एक को पढ़ने, लिखे जाने या त्रुटि को उठाना नहीं चाहता है।

इसलिए पहले तीन तर्क, निगरानी के लिए विभिन्न प्रकार के राज्यों से मेल खाते हैं।

  • तैयार
  • लिखने के लिए पढ़ने
  • तैयार के लिए अपवाद

चौथे लंबित है टाइमआउट आप स्थापित करने के लिए (यदि हो तो) चाहते हैं। हम इस पैरामीटर का लाभ उठाने जा रहे हैं।

आईओ ऑब्जेक्ट्स (इस मामले में सॉकेट) के सरणी वाले एक सरणी का चयन करें जिसे विशेष कार्रवाई की निगरानी के लिए ऑपरेटिंग सिस्टम द्वारा तैयार समझा जाता है।

तो चयन के रिटर्न मान इस तरह दिखेगा:

[ 
    [sockets ready for reading], 
    [sockets ready for writing], 
    [sockets raising errors] 
] 

हालांकि, चुनिंदा रिटर्न nil अगर वैकल्पिक टाइमआउट मान दिया जाता है और कोई आईओ वस्तु टाइमआउट सेकंड के भीतर तैयार है।

इसलिए, यदि आप performant आईओ रूबी में टाइमआउट करते हैं और समय समाप्त मॉड्यूल का उपयोग करने के लिए होने से बचने के लिए चाहते हैं, आप कर सकते हैं निम्नलिखित:

एक उदाहरण है जहाँ हम socket पर पढ़ने के लिए timeout सेकंड प्रतीक्षा निर्माण करते हैं:

ready = IO.select([socket], nil, nil, timeout) 

if ready 
    # do the read 
else 
    # raise something that indicates a timeout 
end 

यह प्रत्येक समय समाप्ति के लिए एक नया धागा ऊपर कताई नहीं (समय समाप्ति मॉड्यूल के रूप में) का लाभ मिलता है और कई समय समाप्ति बहुत रूबी में तेजी के साथ मल्टी-थ्रेडेड अनुप्रयोगों कर देगा।

+0

है तो मान लें कि पढ़ना या तो रीडपार्टियल या read_nonblock होना चाहिए – nhed

+0

मुझे पता है कि यह इस बिंदु पर एक पुराना धागा है, लेकिन यह एसएसएल पर होने वाले टाइमआउट के साथ कैसे काम कर सकता है। कनेक्ट()? – Justin

+0

मुझे ईमेल करें और मुझे सहायता करने में खुशी होगी। –

6

मुझे लगता है कि आप मूल रूप से भाग्य से बाहर हैं। जब मैं strace के साथ अपने उदाहरण चलाने (केवल एक बाहरी सर्वर का उपयोग कर उत्पादन को साफ रखने के), यह है कि setsockopt वास्तव में बुलाया जा रहा है की जाँच करना आसान है:

$ strace -f ruby foo.rb 2>&1 | grep setsockopt 
[pid 5833] setsockopt(5, SOL_SOCKET, SO_RCVTIMEO, "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) = 0 

strace भी पता चलता है कि कार्यक्रम अवरोधित कर रही है।

[pid 5958] ppoll([{fd=5, events=POLLIN}], 1, NULL, NULL, 8 

इसका मतलब है कि कार्यक्रम ppoll को यह फोन पर ब्लॉक कर रहा है, recvfrom के लिए एक कॉल पर नहीं: यह पंक्ति मैं स्क्रीन पर देख सर्वर का समय समाप्त होने से पहले है। आदमी पेज सॉकेट विकल्पों को सूचीबद्ध करता है (socket(7)) कहा गया है कि:

टाइमआउट का चयन करें (2), सर्वेक्षण (2), epoll_wait (2), आदि

तो समय समाप्ति के लिए कोई प्रभाव नहीं सेट किया जा रहा है लेकिन इसका कोई प्रभाव नहीं है। मुझे आशा है कि मैं यहां गलत हूं, लेकिन ऐसा लगता है कि रुबी में इस व्यवहार को बदलने का कोई तरीका नहीं है। मैंने कार्यान्वयन पर एक त्वरित नज़र डाली और मुझे एक स्पष्ट तरीका नहीं मिला। दोबारा, मुझे उम्मीद है कि मैं गलत हूं - ऐसा लगता है कि यह कुछ बुनियादी है, यह कैसे नहीं है?

एक (बहुत बदसूरत) वर्कअराउंड dl का उपयोग करके read या recvfrom पर कॉल करने के लिए सीधे है। वे कॉल आपके द्वारा सेट किए गए टाइमआउट से प्रभावित होते हैं।उदाहरण के लिए:

require 'socket' 
require 'dl' 
require 'dl/import' 

module LibC 
    extend DL::Importer 
    dlload 'libc.so.6' 
    extern 'long read(int, void *, long)' 
end 

sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) 
timeval = [3, 0].pack("l_l_") 
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval 
sock.connect(Socket.pack_sockaddr_in(1234, '127.0.0.1')) 

buf = "\0" * 1024 
count = LibC.read(sock.fileno, buf, 1024) 
if count == -1 
    puts 'Timeout' 
end 

यह कोड यहां काम करता है। बेशक: यह एक बदसूरत समाधान है, जो कई प्लेटफॉर्म पर काम नहीं करेगा, आदि। हालांकि यह एक तरीका हो सकता है।

कृपया ध्यान दें कि यह पहली बार है जब मैं रूबी में कुछ ऐसा करता हूं, इसलिए मुझे उन सभी समस्याओं के बारे में पता नहीं है जिन्हें मैं अनदेखा कर सकता हूं - विशेष रूप से, मुझे 'long read(int, void *, long)' में निर्दिष्ट प्रकारों के बारे में संदेह है और जिस तरह से मैं पढ़ने के लिए एक बफर गुजर रहा हूँ।

+0

उपरोक्त, उत्तर देने के लिए धन्यवाद। मुझे उम्मीद है कि यह सच नहीं है। ऐसे कई रूबी कार्यक्रम हैं जो इस बी पर निर्भर करते हैं व्यवहार जो सही ढंग से काम नहीं करना चाहिए यदि यह –

6

मेरी परीक्षण, और "टीसीपी सॉकेट के साथ कार्य करना" (रूबी में) पर जेसी Storimer उत्तम ebook के आधार पर, समय समाप्ति सॉकेट विकल्प रूबी 1.9 में काम नहीं है (और, मैं 2.0 और 2.1 अनुमान)। जेसी का कहना है:

आपका ऑपरेटिंग सिस्टम भी देशी सॉकेट समय समाप्ति SNDTIMEO और RCVTIMEO सॉकेट विकल्पों के माध्यम से स्थापित किया जा सकता है कि प्रदान करता है। लेकिन, रूबी 1 के रूप में।9, यह सुविधा अब कार्यात्मक है। "

वाह। मुझे लगता है कि कहानी का नैतिक इन विकल्पों के बारे में भूल और IO.select या टोनी Arcieri के NIO पुस्तकालय का प्रयोग है।