2013-11-14 64 views
2

我試圖連接到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正在發送一個錯誤響應,而我沒有正確讀取它,或者我沒有正確設置套接字。我已經嘗試了以下技術: -

  1. 每個插槽使用一個單獨的線程嘗試讀取響應(如果有的話每隔5ms左右)。
  2. 寫入失敗後使用阻止讀取。
  3. 在遠程斷開連接後使用最終讀取。

我已經嘗試過使用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

發表一些代碼。任何未決的發送數據在FIN之前到達。如果你不能閱讀它,你的代碼中會有一個錯誤。 – EJP

回答

0

我實現了服務器端的APNS在Java和有問題讀取錯誤響應可靠的(意思是 - 永遠不會錯過任何錯誤響應),但我設法得到錯誤迴應。

你可以看到這個related question,雖然它沒有足夠的答案。

如果您從未設法讀取錯誤響應,那麼您的代碼一定有問題。

  1. 使用一個單獨的線程閱讀爲我工作,但不是100%可靠。
  2. 寫入失敗後使用阻止讀取 - 這是蘋果的建議,但它並不總是工作。您可能發送100條消息,並且第一條消息有無效標記,並且只有在第100條消息發生寫入失敗之後。此時,從套接字讀取錯誤響應有時已經太晚了。
  3. 我不確定你的意思。

如果要保證錯誤響應的讀取能夠正常工作,則應該在每次寫入後嘗試讀取,並有足夠的超時時間。這當然不適合在生產環境中使用(因爲它非常慢),但是您可以使用它來驗證您的讀取和解析錯誤響應的代碼是否正確。您也可以使用它遍歷所有設備令牌,並找到所有無效的令牌,以清理數據庫。

你沒有發佈任何代碼,所以我不知道你用什麼二進制格式發送消息到APNS。如果您使用的是簡單格式(以0字節開頭並且沒有消息ID),則Apple不會收到任何響應。

+0

我也是這麼做的。但是,這取決於超時時間的長短,從而殺死吞吐量。另外(至少在Java上)出於某種原因,小的讀取超時似乎也會影響寫入。我發現雖然是從SSLSocket的InputStream幾乎總是返回0 for available()。這似乎與SSLSocket流分層在底層Socket流之上有關。如果不解開和解碼Socket流上可用的任何實際數據,SSL流不能可靠地說出是否有更多可用數據。因此,它總是返回0。 – VJK

+0

因此,我所做的是檢查底層套接字上的可用信息,然後從SSL套接字中讀取底層套接字是否返回數據。此外,Socket getInputStream()方法指出,根據TCP實現,任何待處理的未讀數據可能會在RST上被丟棄。哎喲。這意味着,這可能無法可靠地工作。儘管如此,在這方面,與C#相比,我更喜歡使用C#:在每次寫入之前從流中讀取一個非常短的超時值。 – VJK

+0

@VJK我在Java中找到了一種提高可靠性的方法 - 通過禁用Nagle算法 - 「socket.setTcpNoDelay(true)」 - 但這違背了Apple建議的最佳實踐,並降低了吞吐量。但是,這樣做可以讓您在短暫超時的情況下可靠地獲取錯誤響應。 – Eran