Ruby,SSLSockets和Apple的增强型APN消息格式

我正试图在我的Rails应用程序中实现对Apple增强的推送通知消息格式的支持,并且遇到了一些令人沮丧的问题。我显然不像我想的那样理解插座。 我的主要问题是,如果我正确发送所有消息,我的代码会挂起,因为socket.read将阻塞,直到我收到消息。如果您的消息看起来没问题,Apple不会返回任何内容,因此我的程序会锁定。 这是我如何工作的一些伪代码:
cert = File.read(options[:cert])
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = OpenSSL::PKey::RSA.new(cert, options[:passphrase])
ctx.cert = OpenSSL::X509::Certificate.new(cert)

sock = TCPSocket.new(options[:host], options[:port])
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.sync = true
ssl.connect

messages.each do |message|
  ssl.write(message.to_apn)
end

if read_buffer = ssl.read(6)
  process_error_response(read_buffer)
end
显然,这有很多问题: 如果我正在向大量设备发送消息,并且在处理过程中途发送了失败消息,那么在我尝试发送到所有设备之前,我不会真正看到错误。 如前所述,如果所有消息都被Apple接受,我的应用程序将挂起套接字读取调用。 我尝试解决此问题的一种方法是在单独的线程中从套接字读取:
Thread.new() {
  while data = ssl.read(6)
    process_error_response(data)
  end
}

messages.each do |message|
  ssl.write(message.to_apn)
end

ssl.close
sock.close
这似乎不起作用。似乎永远不会从套接字读取数据。这可能是我对套接字应该如何工作的误解。 我想到的另一个解决方案是进行非阻塞读取调用...但是看起来Ruby似乎并没有在SSLSocket上进行非阻塞读取调用,直到1.9 ...我很遗憾现在无法使用它。 能够更好地理解套接字编程的人能指出我正确的方向吗?     
已邀请:
凸轮是正确的:处理这种情况的传统方式是
IO.select
if IO.select([ssl], nil, nil, 5)
  read_buffer = ssl.read(6)
  process_error_response(read_buffer)
end
这将检查
ssl
的“可读性”为5秒,如果可读则返回
ssl
,否则返回
nil
。     
你能用
IO.select
吗?它允许您指定超时,因此您可以限制阻止的时间。有关详细信息,请参阅规范:http://github.com/rubyspec/rubyspec/blob/master/core/io/select_spec.rb     
我也对此感兴趣,这是另一种方法,遗憾的是它有自己的缺陷。
messages.each do |message|
  begin
    // Write message to APNS
    ssl.write(message.to_apn)
  rescue
    // Write failed (disconnected), read response
    response = ssl.read(6)
    // Unpack the binary response and print it out
    command, errorCode, identifier = response.unpack('CCN');
    puts "Command: #{command} Code: #{errorCode} Identifier: #{identifier}"
    // Before reconnecting, the problem (assuming incorrect token) must be solved
    break
  end
end
这似乎有效,因为我保持持久的连接,我可以毫无问题地重新连接
rescue
代码并重新开始。 但是有一些问题。我想要解决的主要问题是发送不正确的设备令牌(例如来自开发版本)导致的断开连接。如果我有100个设备令牌,我发送消息,中间某处有一个不正确的令牌,我的代码让我知道它是哪一个(假设我提供了良好的标识符)。然后我可以删除有故障的令牌,并将消息发送到出现故障之后出现的所有设备(因为消息没有发送给他们)。但如果不正确的令牌位于100的末尾,则直到下次发送消息时才会发生
rescue
。 问题是代码实际上不是实时的。如果我使用此代码向10个不正确的令牌发送10封消息,一切都会好的,循环将会通过,并且不会报告任何问题。似乎
write()
不等待清理所有内容,并且在连接终止之前循环运行。下次循环运行时,
write()
命令失败(因为我们自上次以来实际上已经断开连接),我们会得到错误。 如果有另一种方法来响应失败的连接,这可以解决问题。     
有一个简单的方法。编写邮件后,尝试以非阻止模式阅读:
ssl.connect
ssl.sync = true # then ssl.write() flushes immediately
ssl.write(your_packed_frame)
sleep(0.5)      # so APN have time to answer
begin
  error_packet = ssl.read_nonblock(6) # Read one packet: 6 bytes
  # If we are here, there IS an error_packet which we need to process
rescue  IO::WaitReadable
  # There is no (yet) 6 bytes from APN, probably everything is fine
end
我在MRI 2.1中使用它,但它也适用于早期版本。     

要回复问题请先登录注册