2016-04-22 28 views
6

yılında iki yönlü SSL soket oluşturabilir, ama aynı zamanda kullanıcıların bir yöntemi çağırarak veri göndermek için izin verir.nasıl bir sunucuya bağlanır ve veri bekler bir istemci Ruby kütüphane oluşturma çabası Ruby

kullandığım mekanizma bir soket çifti başlatır bir sınıf var şöyle şudur:

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

Ben geliştiriciler sunucuya veri göndermek için arama yapılmasına izin yöntem aşağıdaki gibidir:

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

ben sunucuya bağlı bir socket arasından seçim ayrı bir iş parçacığı, bir döngü var ve @pipe_r den:

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 

Bu, güzel bir şekilde ancak yalnızca örnek olarak normal TCPSocket ile çalışır. Ben ancak the IO.select docs uyarınca, bunun yerine bir OpenSSL::SSL::SSLSocket kullanmak gerekir:

iyi yolu IO.select vb read_nonblock, write_nonblock gibi beklememe yöntemlerle sonra yürütmesini Kullanmak

[...]

Özellikle nonblocking yöntem ve IO.select gibi OpenSSL :: SSL :: SSLSocket gibi nesneler gibi IO için tercih edilir bir kombinasyonu.

Buna göre ne zaman döngü içinde ben bu yüzden 2 farklı IO nesneler arasından seçim yapabilirsiniz önce kullanın ederken, Nonblocking yöntemlerle sonra IO.selectçağırmanız gerekir.

bir SSL soketi ile IO.select nasıl kullanılacağı konusunda verilen örnektir:

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

Ancak bu IO.select bir tek IO nesnesi ile kullanıldığında durumunda çalışır.

Sorum şu: nasıl bir SSL soketi ile benim önceki örnek çalışması yapabilir, ben de @pipe_r ve socket nesnelerden seçmek gerektiğini verilen?

DÜZENLEME: Ben ancak boşuna ne @ steffen-ullrich önerdi denedim. Aşağıdaki benim testler kullanılarak geçmesi yapmak başardı:

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 

Bu kadar kötü görünmüyor, ancak herhangi bir giriş açığız.

cevap

2

Ben Yakut kendisi aşina değilim, ancak SSL tabanlı soketli seçin kullanarak sorunları ile. SSL soketleri SSL Veri çerçeveleri olarak aktarılır ve bir veri akışı olarak, çünkü, TCP soketlerine farklı davranır, fakat yine de anlam yuva ara-yüzü uygulanır aktarın.

ilk basit bir TCP bağlantısı kullanarak, bir örnekle açıklayalım:

  • sunucu tek bir yazma 1000 bayt gönderir.
  • Veriler müşteriye teslim edilir ve çekirdek soket arabelleğine konur. Böylelikle seçim, bu verinin kullanılabilir olmasını sağlayacaktır.
  • İstemci uygulaması önce yalnızca 100 bayt okuyor.
  • Diğer 900 bayt daha sonra çekirdek içi soket arabelleğinde tutulur. Bir sonraki seçim çağrısı, seçili veriyi kullanılabilir duruma getirir, çünkü seçim, çekirdek içi soket arabelleğine bakar.

SSL ile bu farklıdır:

  • sunucu tek bir yazma 1000 bayt gönderir. SSL yığını bu 1000 baytı tek bir SSL çerçevesine şifreler ve bu çerçeveyi istemciye gönderir.
  • Bu SSL çerçevesi, istemciye teslim edilir ve çekirdek soket arabelleğine konur. Böylelikle seçim, bu verinin kullanılabilir olmasını sağlayacaktır.
  • Artık istemci uygulaması yalnızca 100 bayt okuyor. SSL çerçevesi 1000 bayt içerdiğinden ve verilerin şifresini çözmek için tam çerçeveye ihtiyaç duyduğundan, SSL yığını yuvadaki tüm çerçeveyi okur ve çekirdek çekirdek soketinde hiçbir şey kalmaz. Ardından çerçevenin şifresini çözer ve istenen 100 baytı uygulamaya döndürür. Şifrelenmiş 900 baytın geri kalanı kullanıcı alanında SSL yığınında tutulacak.
  • İç çekirdek soket arabelleği boş olduğu için sonraki seçme çağrısı bu veriyi döndürmez. Böylece, eğer uygulama sadece seçimle ilgileniyorsa, hala okunmamış veriler, yani SSL yığında tutulan 900 bayt olduğu anlamına gelmeyecektir.

nasıl bu farkın başa:

  • bir yolu her zaman en az 32768 veri okumak için çalışmaktır, bu bir SSL çerçevesinin maksimum boyut çünkü. Bu şekilde, SSL yığında hiçbir verinin hala tutulmadığından emin olabilirsiniz (SSL okuma, SSL çerçeve sınırları üzerinden okunmaz). Başka bir yöntem, seçilmeden önce, önceden şifrelenmiş veri için SSL yığınını kontrol etmektir. SSL yığında hiçbir veri tutulmuyorsa, seçim yapılmalıdır. "Bekleyen verileri" kontrol etmek için the pending method kullanın.
  • Daha fazla veri yok olana kadar engelleme olmayan soketten daha fazla veri okumaya çalışın. Bu şekilde, SSL yığında hala bir veri beklemediğinden emin olabilirsiniz. Ancak bunun altta yatan TCP soketinde de okuyacağına ve dolayısıyla diğer soketlere (yalnızca başarılı seçimlerden sonra okunan) ilişkin verilere kıyasla SSL soketindeki verileri tercih edebileceğine dikkat edin. Bahsettiğin öneri bu, ama diğerlerini tercih ederim.
+0

Teşekkür ederim @ steffen-ullrich, bu çok açık bir cevaptır. Maalesef, select seçeneğini kullanmadan önce bekleyen yöntemi kullanmayı denedim ve her zaman 0 bayt olduğunu öğreniyorum. Ayrıca nonblock_read (32768) denedim ve bu da hile değil. Biraz daha deneyeceğim. – ostinelli

+0

Bu cevabı kabul edeceğim, çünkü bu sorunları çözmek için doğru yolu açıklığa kavuşturuyor. Tekrar teşekkürler! – ostinelli