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...不幸的是我现在无法使用它。
对套接字编程有更好了解的人可以指出我正确的方向吗?
I'm trying to implement support for Apple's enhanced Push Notification message format in my Rails app, and am having some frustrating problems. I clearly don't understand sockets as much as I thought I did.
My main problem is that if I send all messages correctly, my code hangs, because socket.read will block until I receive a message. Apple doesn't return anything if your messages looked OK, so my program locks up.
Here is some pseudocode for how I have this working:
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
Obviously, there are a number of problems with this:
- If I'm sending messages to a large number of devices, and the failure message is sent half way through processing, then I'm not going to actually see the error until I've already tried to send to all devices.
- As mentioned earlier, if all messages were acceptable to Apple, my app will hang on the socket read call.
One way I've tried to solve this is by to reading from the socket in a separate thread:
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
This doesn't seem to work. Data never seems to be read from the socket. This is probably a misunderstanding I have about how sockets are supposed to work.
The other solution I have thought of is having a non-blocking read call... but it doesn't seem like Ruby has a non blocking read call on SSLSocket until 1.9... which I unfortunately cannot use right now.
Could someone with a better understanding of socket programming please point me in the right direction?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
cam 是正确的:处理这种情况的传统方法是使用 IO.select
这将检查 ssl 的“可读性”5 秒并返回 ssl > 如果可读,否则
nil
。cam is correct: the traditional way to handle this situation is with
IO.select
This will check
ssl
for "readability" for 5 seconds and returnssl
if it's readable ornil
otherwise.你能使用
IO.select
吗?它允许您指定超时,因此您可以限制阻止的时间量。有关详细信息,请参阅规范: http://github.com/ rubyspec/rubyspec/blob/master/core/io/select_spec.rbCan you use
IO.select
? It lets you specify a timeout, so you could at limit the amount of time you block. See the spec for details: http://github.com/rubyspec/rubyspec/blob/master/core/io/select_spec.rb我对此也很感兴趣,这是另一种方法,不幸的是它有自己的缺陷。
这似乎有效,并且由于我保持持久连接,因此我可以毫无问题地在
rescue
代码中重新连接并重新开始。但也存在一些问题。我想要解决的主要问题是由于发送不正确的设备令牌(例如来自开发版本)而导致的断开连接。如果我有 100 个设备令牌要向其发送消息,并且中间某处有一个不正确的令牌,我的代码会让我知道它是哪一个(假设我提供了良好的标识符)。然后,我可以删除有问题的令牌,并将消息发送到有问题的令牌之后出现的所有设备(因为消息没有发送给它们)。但是,如果不正确的令牌位于 100 末尾的某个位置,则直到我下次发送消息时才会发生
救援
。问题似乎在于代码并不是真正实时的。如果我使用此代码向 10 个不正确的令牌发送 10 条消息,一切都会很好,循环将完成并且不会报告任何问题。看起来
write()
不会等待一切都清除,并且在连接终止之前循环运行。下次运行循环时,write()
命令会失败(因为自上次以来我们实际上已断开连接),并且会收到错误消息。如果有其他方法来响应失败的连接,则可以解决问题。
I'm interested in this too, this is another approach, unfortunately with it's own flaws.
This seems to work, and since I'm keeping a persistent connection, I can without problems reconnect in the
rescue
code and start over again.There are some issues though. The main problem I'm looking to solve is disconnects caused by sending in incorrect device tokens (for example from development builds). If I have 100 device tokens that I send a message to, and somewhere in the middle there is an incorrect token, my code lets me know which one it was (assuming I supplied good identifiers). I can then remove the faulty token, and send the message to all devices that appeared after the faulty one (since the message didn't get sent to them). But if the incorrect token is somewhere in the end of the 100, the
rescue
doesn't happen until the next time I send messages.The problem seams to be that the code isn't really in real time. If I were to send in, say, 10 messages to 10 incorrect tokens with this code, everything would be just fine, the loop will go through and no problems will be reported. It seems that
write()
doesn't wait for everything to clear up, and the loops runs through before the connection is terminated. The next time the loop will be run, thewrite()
command fails (since we've actually been disconnected since the last time) and we would get the error.If there is an alternative way to respond to the failed connection, this could solve the problem.
有一个简单的方法。写完消息后,尝试以非阻塞模式阅读:
我将其与 MRI 2.1 一起使用,但它也应该适用于早期版本。
There is a simple way. After you write your messages, try reading in nonblocking mode:
I use it with MRI 2.1 but it should work with earlier versions too.