我試圖連接到Apple推送通知服務,該服務使用通過TLS(或SSL)保護的TCP的簡單二進制協議。該協議表明,當遇到錯誤時(大約有10個明確定義的錯誤條件),APNS將發回一個錯誤響應,然後關閉連接。這導致半關閉的套接字,因爲遠程對等關閉了套接字。我可以看到它的正常關機,因爲APNS使用tcpdump發送FIN和RST。從半開放套接字讀取
在所有的錯誤條件中,在發送驗證之前,我可以處理大多數錯誤條件。這種情況失敗的原因是,當通知發送給無法處理的無效設備令牌時,因爲令牌的格式可能不正確。令牌是由APNS提供給設備,然後向我註冊的不透明32字節值。提交給我的服務時,我無法確定它是否有效。據推測,APNS會以某種方式對令牌進行校驗,以便快速驗證令牌。
反正
我做了我認爲正確的事情: -
a. open socket
b. try writing
c. if write failed, read the error response
不幸的是,這似乎並沒有工作。我認爲APNS正在發送一個錯誤響應,而我沒有正確讀取它,或者我沒有正確設置套接字。我已經嘗試了以下技術: -
- 每個插槽使用一個單獨的線程嘗試讀取響應(如果有的話每隔5ms左右)。
- 寫入失敗後使用阻止讀取。
- 在遠程斷開連接後使用最終讀取。
我已經嘗試過使用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
發表一些代碼。任何未決的發送數據在FIN之前到達。如果你不能閱讀它,你的代碼中會有一個錯誤。 – EJP