2016-04-22 7 views
6

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

तंत्र मैं उपयोग करते हैं, एक वर्ग है कि एक सॉकेट जोड़ी initializes है तो तरह है:

def initialize 
    @pipe_r, @pipe_w = Socket.pair(:UNIX, :STREAM, 0) 
end 

विधि है कि मैं डेवलपर्स सर्वर से डेटा भेजने के लिए कॉल करने की अनुमति इस तरह दिखता है:

def send(data) 
    @pipe_w.write(data) 
    @pipe_w.flush 
end 

तो मैं एक अलग थ्रेड, जहां मैं एक socket सर्वर से जुड़ा से चयन में एक पाश है और @pipe_r से:

def socket_loop 
    Thread.new do 
    socket = TCPSocket.new(host, port) 

    loop do 
     ready = IO.select([socket, @pipe_r]) 

     if ready[0].include?(@pipe_r) 
     data_to_send = @pipe_r.read_nonblock(1024) 
     socket.write(data_to_send) 
     end 

     if ready[0].include?(socket) 
     data_received = socket.read_nonblock(1024) 
     h2 << data_received 
     break if socket.nil? || socket.closed? || socket.eof? 
     end 
    end 
    end 
end 

यह उदाहरण के अनुसार, लेकिन केवल सामान्य TCPSocket के साथ खूबसूरती से काम करता है। मैं तथापि के रूप में प्रति the IO.select docs, बजाय एक OpenSSL::SSL::SSLSocket उपयोग करने की आवश्यकता: IO.select ऐसे read_nonblock, write_nonblock, आदि के रूप nonblocking तरीकों के बाद यह लागू है उपयोग करने के लिए

सबसे अच्छा तरीका है

[...]

विशेष रूप से, ओपनएसएसएल :: एसएसएल :: एसएसएलएसकेट जैसी वस्तुओं जैसे आईओ के लिए गैर-ब्लॉकिंग विधियों और IO.select का संयोजन पसंद किया जाता है।

इस के अनुसार, मैं nonblocking तरीकों के बाद IO.selectकॉल करने के लिए, जबकि मेरी पाश में मैं इसका इस्तेमाल से पहले तो मैं 2 अलग आईओ वस्तुओं से चुन सकते हैं की जरूरत है।

कैसे एक SSL सॉकेट के साथ IO.select उपयोग करने के लिए दिए गए उदाहरण है:

begin 
    result = socket.read_nonblock(1024) 
rescue IO::WaitReadable 
    IO.select([socket]) 
    retry 
rescue IO::WaitWritable 
    IO.select(nil, [socket]) 
    retry 
end 

हालांकि इस केवल तभी कारगर साबित IO.select एक एकल आईओ वस्तु के साथ प्रयोग किया जाता है।

मेरा प्रश्न है: मैं अपने पिछले उदाहरण को एसएसएल सॉकेट के साथ कैसे काम कर सकता हूं, यह देखते हुए कि मुझे @pipe_r और socket ऑब्जेक्ट्स दोनों से चयन करने की आवश्यकता है?

संपादित करें: मैंने कोशिश की है कि @ स्टीफन-औलिच ने सुझाव दिया है, हालांकि इसका कोई फायदा नहीं हुआ। मैं निम्नलिखित मेरी परीक्षण का उपयोग कर पारित कराने में सफल रहा:

loop do 

    begin 
    data_to_send = @pipe_r.read_nonblock(1024) 
    socket.write(data_to_send) 
    rescue IO::WaitReadable, IO::WaitWritable 
    end 

    begin 
    data_received = socket.read_nonblock(1024) 
    h2 << data_received 
    break if socket.nil? || socket.closed? || socket.eof? 

    rescue IO::WaitReadable 
    IO.select([socket, @pipe_r]) 

    rescue IO::WaitWritable 
    IO.select([@pipe_r], [socket]) 

    end 
end 

यह इतना बुरा नहीं लगती है, लेकिन किसी भी इनपुट का स्वागत है।

उत्तर

2

मैं रूबी से परिचित नहीं हूं, लेकिन एसएसएल आधारित सॉकेट के साथ चयन करने की समस्याओं के साथ। एसएसएल सॉकेट टीसीपी सॉकेट के लिए अलग-अलग व्यवहार करते हैं क्योंकि एसएसएल डेटा फ्रेम में स्थानांतरित होते हैं, न कि डेटा स्ट्रीम के रूप में, लेकिन फिर भी स्ट्रीम अर्थशास्त्र सॉकेट इंटरफ़ेस पर लागू होते हैं।

के एक उदाहरण के साथ यह समझाने के लिए, पहले एक साधारण TCP कनेक्शन का उपयोग कर दो:

  • सर्वर एक भी लिखने में 1000 बाइट्स भेजता है।
  • डेटा क्लाइंट को दिया जाएगा और इन-कर्नेल सॉकेट बफर में डाल दिया जाएगा। इस प्रकार चयन करें कि डेटा उपलब्ध होगा।
  • क्लाइंट एप्लिकेशन पहले केवल 100 बाइट पढ़ता है।
  • अन्य 900 बाइट्स को इन-कर्नेल सॉकेट बफर में रखा जाएगा। चयन का अगला कॉल फिर से लौटाएगा कि डेटा उपलब्ध है, क्योंकि इन-कर्नेल सॉकेट बफर में चयन दिखता है।

SSL के साथ इस तरह से अलग है:

  • सर्वर एक भी लिखने में 1000 बाइट्स भेजता है। एसएसएल स्टैक तब इन 1000 बाइट्स को एक एसएसएल फ्रेम में एन्क्रिप्ट करेगा और इस फ्रेम को क्लाइंट को भेज देगा।
  • यह SSL फ्रेम क्लाइंट को वितरित किया जाएगा और इन-कर्नेल सॉकेट बफर में डाल दिया जाएगा। इस प्रकार चयन करें कि डेटा उपलब्ध होगा।
  • अब क्लाइंट एप्लिकेशन केवल 100 बाइट पढ़ता है। चूंकि एसएसएल फ्रेम में 1000 बाइट हैं और चूंकि इसे डेटा को डिक्रिप्ट करने के लिए पूर्ण फ्रेम की आवश्यकता है, इसलिए एसएसएल स्टैक सॉकेट से पूर्ण फ्रेम पढ़ेगा, जिसमें कर्नेल सॉकेट बफर में कुछ भी नहीं छोड़ा जाएगा। फिर यह फ्रेम को डिक्रिप्ट करेगा और आवेदन के लिए अनुरोधित 100 बाइट वापस कर देगा। शेष डिक्रिप्ट किए गए 900 बाइट उपयोगकर्ता स्थान में एसएसएल स्टैक में रखे जाएंगे।
  • चूंकि इन-कर्नेल सॉकेट बफर खाली है, तो चयन का अगला कॉल उस डेटा को वापस नहीं लौटाएगा। इस प्रकार यदि एप्लिकेशन केवल चयन के साथ सौदा करता है तो अब यह नहीं होगा कि अभी भी अपठित डेटा हैं, यानी एसएसएल स्टैक में रखे 900 बाइट्स।

इस अंतर को कैसे प्रबंधित करने के लिए:

  • एक तरह से हमेशा कम से कम 32768 डेटा पढ़ने की कोशिश करने के लिए है, क्योंकि यह एक SSL फ्रेम का अधिकतम आकार है। इस तरह से कोई यह सुनिश्चित कर सकता है कि एसएसएल स्टैक में कोई डेटा अभी भी नहीं रखा गया है (एसएसएल पढ़ना एसएसएल फ्रेम सीमाओं पर नहीं पढ़ेगा)।
  • एक और तरीका चयन करने से पहले पहले से डिक्रिप्ट किए गए डेटा के लिए एसएसएल स्टैक की जांच करना है। केवल अगर एसएसएल स्टैक में कोई डेटा नहीं रखा जाता है तो चयन किया जाना चाहिए। ऐसे "लंबित डेटा" का उपयोग करने के लिए the pending method का उपयोग करें।
  • गैर-अवरुद्ध सॉकेट से अधिक डेटा पढ़ने का प्रयास करें जब तक कि कोई और डेटा उपलब्ध न हो। इस तरह आप सुनिश्चित कर सकते हैं कि एसएसएल स्टैक में अभी भी कोई डेटा लंबित नहीं है। लेकिन ध्यान दें कि यह अंतर्निहित टीसीपी सॉकेट पर भी पढ़ेगा और इस प्रकार अन्य सॉकेट पर डेटा की तुलना में एसएसएल सॉकेट पर डेटा पसंद कर सकता है (जो केवल सफल चयन के बाद पढ़ा जाता है)। यह वह सिफारिश है जिसे आपने उद्धृत किया है, लेकिन मैं दूसरों को पसंद करूंगा।
+0

धन्यवाद @ स्टीफन-औलिच, यह एक बहुत ही स्पष्ट उत्तर है। दुर्भाग्यवश मैंने चयन का उपयोग करने से पहले लंबित विधि का उपयोग करने का प्रयास किया है और मुझे हमेशा यह मिलता है कि 0 बाइट उपलब्ध हैं। मैंने nonblock_read (32768) भी कोशिश की है और यह चाल भी नहीं है। कुछ और कोशिश करेंगे। – ostinelli

+0

इस उत्तर को स्वीकार करेगा क्योंकि यह इन मुद्दों को हल करने में सही तरीके से स्पष्ट करता है। फिर से धन्यवाद! – ostinelli