我正在尝试连接到Apple推送通知服务,该服务使用通过TLS(或SSL)保护的TCP上的简单二进制协议。该协议表明,当遇到错误(大约有10个定义良好的错误条件)时,APNS将发回错误响应,然后关闭连接。这导致半封闭套接字,因为远程对等体关闭了套接字。我可以看到它正常关闭,因为APNS使用tcpdump发送FIN和RST。
在所有错误条件中,我可以在发送验证之前处理最多。失败的情况是当通知被发送到无效的设备令牌时,由于令牌可能格式不正确而无法轻易处理。令牌是不透明的32字节值,由APNS提供给设备然后向我注册。提交给我的服务时,我无法知道它是否有效。据推测,APNS可以通过某种方式对令牌进行校验,以便他们可以快速快速验证令牌。
反正
我做了我认为正确的事情: -
a. open socket
b. try writing
c. if write failed, read the error response
不幸的是,这似乎不起作用。我认为APNS正在发送错误响应,我没有正确地读它或者我没有正确设置套接字。我尝试了以下技巧: -
我在Windows上使用C#+ .NET 4.5,在Linux上使用Java 1.7。在任何一种情况下,我似乎都没有得到错误响应,套接字表明没有数据可供读取。
这些操作系统和/或框架是否支持半封闭套接字?没有任何东西似乎表明任何一种方式。
我知道我正在设置的方式正常工作,因为如果我使用带有效通知的有效令牌,那么这些令牌会被传递。
在回复其中一条评论时,我使用的是增强型通知格式,因此应从APNS收到回复。
以下是我对C#的代码: -
X509Certificate certificate =
new X509Certificate(@"Foo.cer", "password");
X509CertificateCollection collection = new X509CertificateCollection();
collection.Add(certificate);
Socket socket =
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("gateway.sandbox.push.apple.com", 2195);
NetworkStream stream =
new NetworkStream(socket, System.IO.FileAccess.ReadWrite, false);
stream.ReadTimeout = 1000;
stream.WriteTimeout = 1000;
sslStream =
new SslStream(stream, true,
new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
sslStream.AuthenticateAsClient("gateway.sandbox.push.apple.com", collection,
SslProtocols.Default, false);
sslStream.ReadTimeout = 10000;
sslStream.WriteTimeout = 1000;
// Task rdr = Task.Factory.StartNew(this.reader);
// rdr is used for parallel read of socket sleeping 5ms between each read.
// Not used now but another alternative that was tried.
Random r = new Random(DateTime.Now.Second);
byte[] buffer = new byte[32];
r.NextBytes(buffer);
byte[] resp = new byte[6];
String erroneousToken = toHex(buffer);
TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
int timestamp = (int) t.TotalSeconds;
try
{
for (int i = 0; i < 1000; ++i)
{
// build the notification; format is published in APNS docs.
var not = new ApplicationNotificationBuilder().withToken(buffer).withPayload(
@'{"aps": {"alert":"foo","sound":"default","badge":1}}').withExpiration(
timestamp).withIdentifier(i+1).build();
sslStream.Write(buffer);
sslStream.Flush();
Console.Out.WriteLine("Sent message # " + i);
int rd = sslStream.Read(resp, 0, 6);
if (rd > 0)
{
Console.Out.WriteLine("Found response: " + rd);
break;
}
// doesn't really matter how fast or how slow we send
Thread.Sleep(500);
}
}
catch (Exception ex)
{
Console.Out.WriteLine("Failed to write ...");
int rd = sslStream.Read(resp, 0, 6);
if (rd > 0)
{
Console.Out.WriteLine("Found response: " + rd); ;
}
}
// rdr.Wait(); change to non-infinite timeout to allow error reader to terminate
答案 0 :(得分:0)
我在Java中为APNS实现了服务器端,并且在可靠地读取错误响应时遇到了问题(意思是 - 从不错过任何错误响应),但我确实设法得到错误响应。
你可以看到这个related question,但它没有足够的答案。
如果您从未设法阅读错误响应,则代码必定存在问题。
如果您希望保证读取错误响应有效,则应在每次写入后尝试读取足够的超时。当然,这对于在生产中使用是不实际的(因为它非常慢),但您可以使用它来验证您的读取和解析错误响应的代码是否正确。您还可以使用它来迭代您拥有的所有设备令牌,并找到所有无效的设备令牌,以便清理您的数据库。
您没有发布任何代码,因此我不知道您使用什么二进制格式向APNS发送消息。如果您使用的是简单格式(以0字节开头并且没有消息ID),您将无法获得Apple的任何回复。