http://autumnrain-zgq.iteye.com/blog/1743279

蘋果平臺開發的應用程序,不支持后臺運行程序,所以蘋果有一個推送服務在軟件的一些信息推送給用戶。 
JAVA中,有一個開源軟件,JavaPNS實現了Java平臺中連接蘋果服務器與推送消息的服務。但是在使用的過程中,有兩點需要使用者注意一下,希望后續使用的同志們能避免我走過的覆轍。 
1、一是向蘋果的服務推送消息時,如果遇到無效的deviceToken,蘋果會斷開網絡連接,而JavaPNS不會進行重連。蘋果原文: 
    If you send a notification and APNs finds the notification malformed or otherwise unintelligible, it returns an error-response packet prior to disconnecting. (If there is no error, APNs doesn’t return anything.) Figure 5-3 depicts the format of the error-response packet. 
2、JavaPNS是一條條發送通知的,但是對于大規模發送的生產環境,顯然是不可以的,建議使用批量發送。蘋果原文: 
The binary interface employs a plain TCP socket for binary content that is streaming in nature. For optimum performance, you should batch multiple notifications in a single transmission over the interface, either explicitly or using a TCP/IP Nagle algorithm. 

對于此,我對JavaAPNS的代碼進行了改動,使其支持批量發送和蘋果斷開重連,但是有一個問題需要大家注意一下,在正常發送的情況下,蘋果是不會向Socket中寫任何數據的,需要等待其讀超時,this.socket.getInputStream().read(),確訂推送結果的正常。通過持續的向Socket中寫數據,實現批量發送,調用flush方法時,完成一次批量發送。在PushNotificationManager增加如下方法: 
Java代碼  收藏代碼
  1. public List<ResponsePacket> sendNotification(List<PushedNotification> pnl) {  
  2.         logger.info(psn + "RR批量推送時消息體的大小為:" + pnl.size());  
  3.         List<ResponsePacket> failList = new ArrayList<ResponsePacket>();  
  4.         if (pnl.size() == 0) {  
  5.             return failList;  
  6.         }  
  7.         Set<Integer> sendSet = new HashSet<Integer>();  
  8.         int counter = 0;  
  9.         while (counter < pnl.size()) {  
  10.             try {  
  11.                 this.socket.setSoTimeout(3000);  
  12.                 this.socket.setSendBufferSize(25600);  
  13.                 this.socket.setReceiveBufferSize(600);  
  14.                 for (; counter < pnl.size(); counter++) {  
  15.                     PushedNotification push = pnl.get(counter);  
  16.                     if (sendSet.contains(push.getIdentifier())) {  
  17.                         logger.warn("信息[" + push.getIdentifier() + "]已經被推送");  
  18.                         continue;  
  19.                     }  
  20.                     byte[] bytes = getMessage(push.getDevice().getToken(), push.getPayload(), push.getIdentifier(), push);  
  21.                     this.socket.getOutputStream().write(bytes);  
  22.                     // 考慮到重發的問題  
  23.                     // sendSet.add(push.getIdentifier());  
  24.                 }  
  25.                 this.socket.getOutputStream().flush();  
  26.                 // 等待回饋數據,比單個發送時延時長一點,否則將無法獲取到回饋數據  
  27.                 this.socket.setSoTimeout(1000);  
  28.   
  29.                 StringBuffer allResult = new StringBuffer();  
  30.                 ResponsePacket rp = new ResponsePacket();  
  31.                 int readCounter = 0;  
  32.                 // 處理讀回寫數據的異常  
  33.                 try {  
  34.                     logger.info(psn + "檢查流數據是否可用:" + this.socket.getInputStream().available());  
  35.                     byte[] sid = new byte[4];// 發送標記  
  36.                     while (true) {  
  37.                         int value = this.socket.getInputStream().read();  
  38.                         if (value < 0) {  
  39.                             break;  
  40.                         }  
  41.                         readCounter++;  
  42.                         if (readCounter == 1) {  
  43.                             rp.setCommand(value);  
  44.                         }  
  45.                         if (readCounter == 2) {  
  46.                             rp.setStatus(value);  
  47.                         }  
  48.                         if (readCounter >= 3 && readCounter <= 6) {  
  49.                             sid[readCounter - 3] = (byte) value;  
  50.                             if (readCounter == 6) {  
  51.                                 rp.setIdentifier(ByteBuffer.wrap(sid).getInt());  
  52.                                 if (failList.contains(rp)) {  
  53.                                     logger.error("錯誤返饋數據中已經包含當前數據," + rp.getIdentifier());  
  54.                                 }  
  55.                                 failList.add(rp);  
  56.                             }  
  57.                         }  
  58.                         allResult.append(value + "_");  
  59.                     }  
  60.                     this.socket.getInputStream().close();  
  61.                 } catch (SocketTimeoutException ste) {  
  62.                     logger.debug(psn + "消息推送成功,無任何返回!", ste);  
  63.                 } catch (IOException e) {  
  64.                     logger.debug(psn + "消息推送成功,關閉連接流時出錯!", e);  
  65.                 }  
  66.                 logger.info("蘋果返回的數據為:" + allResult.toString());  
  67.                 if (readCounter >= 6) {  
  68.                     // 找到出錯的地方  
  69.                     for (int i = 0; i < pnl.size(); i++) {  
  70.                         PushedNotification push = pnl.get(i);  
  71.                         if (push.getIdentifier() == rp.getIdentifier()) {  
  72.                             counter = i + 1;  
  73.                             break;  
  74.                             // 從出錯的地方再次發送,  
  75.                         }  
  76.                     }  
  77.                     try {  
  78.                         this.createNewSocket();  
  79.                     } catch (Exception e) {  
  80.                         logger.warn("連接時出錯,", e);  
  81.                     }  
  82.                 }  
  83.             } catch (SSLHandshakeException she) {  
  84.                 // 握手出錯,標記不加  
  85.                 logger.warn("SHE消息推送時出錯,", she);  
  86.                 try {  
  87.                     this.createNewSocket();  
  88.                 } catch (Exception e) {  
  89.                     logger.warn("連接時出錯,", e);  
  90.                 }  
  91.             } catch (SocketException se) {  
  92.                 logger.warn("SE消息推送時出錯", se);  
  93.                 counter++;  
  94.                 try {  
  95.                     this.createNewSocket();  
  96.                 } catch (Exception e) {  
  97.                     logger.warn("連接時出錯,", e);  
  98.                 }  
  99.             } catch (IOException e) {  
  100.                 logger.warn("IE消息推送時出錯", e);  
  101.                 counter++;  
  102.             } catch (Exception e) {  
  103.                 logger.warn("E消息推送時出錯", e);  
  104.                 counter++;  
  105.             }  
  106.   
  107.             if (counter >= pnl.size()) {  
  108.                 break;  
  109.             }  
  110.         }  
  111.         return failList;  
  112.     }  
分享到:  
評論
16 樓 lixiangblue 2014-01-07  
在javapns2.2 中,重發確實存在bug。但是我發現。在注釋重發后,notifications的返回結果會變得非常不準確(雖然不注釋也不準確),所以根據系統的需求,如果需要比較準確的返回結果,還是不注銷重發比較好,如果系統偏重與推送功能,對推送結果沒有很高的要求,注釋掉重發代碼可以修復重復推送的bug。期待在以后的javapns版本中修復重復推送的bug。
15 樓 zhanghua_1199 2013-07-22  
在正常發送的情況下,蘋果是不會向Socket中寫任何數據的,需要等待其讀超時。想問一下樓主,這個蘋果讀取超時是在哪看到的,能否給個鏈接?
14 樓 zhanghua_1199 2013-07-22  
lz,,,,你的
持續寫入數據,遇到無效的deviceToken蘋果會斷開連接,并返回相應的消息編號,這時需要重新連接,再次發送數據
博文體現在哪里?是從這里開始嗎:
if (readCounter >= 6) {  
                    // 找到出錯的地方  



如果是,lz可以去看看javapns2.2版本的
PushNotificationManager.stopConnection()這個方法。這里面實現了重發。望回復。
13 樓 shanjh2000 2013-07-09  
請問,你的這個方法的輸入參數: List<PushedNotification> pnl   怎么創建?
12 樓 autumnrain_zgq 2013-05-17  
lsqwind 寫道
rock 寫道
PushQueue支持重連,
源碼:
Java代碼  收藏代碼
  1. private void sendNotification(PushedNotification notification, boolean closeAfter) throws CommunicationException {  
  2.         try {  
  3.             Device device = notification.getDevice();  
  4.             Payload payload = notification.getPayload();  
  5.             try {  
  6.                 payload.verifyPayloadIsNotEmpty();  
  7.             } catch (IllegalArgumentException e) {  
  8.                 throw new PayloadIsEmptyException();  
  9.             } catch (Exception e) {  
  10.             }  
  11.   
  12.             if (notification.getIdentifier() <= 0) notification.setIdentifier(newMessageIdentifier());  
  13.             if (!pushedNotifications.containsKey(notification.getIdentifier())) pushedNotifications.put(notification.getIdentifier(), notification);  
  14.             int identifier = notification.getIdentifier();  
  15.   
  16.             String token = device.getToken();  
  17.             // even though the BasicDevice constructor validates the token, we revalidate it in case we were passed another implementation of Device  
  18.             BasicDevice.validateTokenFormat(token);  
  19.             //      PushedNotification pushedNotification = new PushedNotification(device, payload);  
  20.             byte[] bytes = getMessage(token, payload, identifier, notification);  
  21.             //      pushedNotifications.put(pushedNotification.getIdentifier(), pushedNotification);  
  22.   
  23.             /* Special simulation mode to skip actual streaming of message */  
  24.             boolean simulationMode = payload.getExpiry() == 919191;  
  25.   
  26.             boolean success = false;  
  27.   
  28.             BufferedReader in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));  
  29.             int socketTimeout = getSslSocketTimeout();  
  30.             if (socketTimeout > 0this.socket.setSoTimeout(socketTimeout);  
  31.             notification.setTransmissionAttempts(0);  
  32.             // Keep trying until we have a success  
  33.             while (!success) {  
  34.                 try {  
  35.                     logger.debug("Attempting to send notification: " + payload.toString() + "");  
  36.                     logger.debug("  to device: " + token + "");  
  37.                     notification.addTransmissionAttempt();  
  38.                     boolean streamConfirmed = false;  
  39.                     try {  
  40.                         if (!simulationMode) {  
  41.                             this.socket.getOutputStream().write(bytes);  
  42.                             streamConfirmed = true;  
  43.                         } else {  
  44.                             logger.debug("* Simulation only: would have streamed " + bytes.length + "-bytes message now..");  
  45.                         }  
  46.                     } catch (Exception e) {  
  47.                         if (e != null) {  
  48.                             if (e.toString().contains("certificate_unknown")) {  
  49.                                 throw new InvalidCertificateChainException(e.getMessage());  
  50.                             }  
  51.                         }  
  52.                         throw e;  
  53.                     }  
  54.                     logger.debug("Flushing");  
  55.                     this.socket.getOutputStream().flush();  
  56.                     if (streamConfirmed) logger.debug("At this point, the entire " + bytes.length + "-bytes message has been streamed out successfully through the SSL connection");  
  57.   
  58.                     success = true;  
  59.                     logger.debug("Notification sent on " + notification.getLatestTransmissionAttempt());  
  60.                     notification.setTransmissionCompleted(true);  
  61.   
  62.                 } catch (IOException e) {  
  63.                     // throw exception if we surpassed the valid number of retry attempts  
  64.                     if (notification.getTransmissionAttempts() >= retryAttempts) {  
  65.                         logger.error("Attempt to send Notification failed and beyond the maximum number of attempts permitted");  
  66.                         notification.setTransmissionCompleted(false);  
  67.                         notification.setException(e);  
  68.                         logger.error("Delivery error", e);  
  69.                         throw e;  
  70.   
  71.                     } else {  
  72.                         logger.info("Attempt failed (" + e.getMessage() + ")... trying again");  
  73.                         //Try again  
  74.                         try {  
  75.                             this.socket.close();  
  76.                         } catch (Exception e2) {  
  77.                             // do nothing  
  78.                         }  
  79.                         this.socket = connectionToAppleServer.getSSLSocket();  
  80.                         if (socketTimeout > 0this.socket.setSoTimeout(socketTimeout);  
  81.                     }  
  82.                 }  
  83.             }  
  84.         } catch (CommunicationException e) {  
  85.             throw e;  
  86.         } catch (Exception ex) {  
  87.   
  88.             notification.setException(ex);  
  89.             logger.error("Delivery error: " + ex);  
  90.             try {  
  91.                 if (closeAfter) {  
  92.                     logger.error("Closing connection after error");  
  93.                     stopConnection();  
  94.                 }  
  95.             } catch (Exception e) {  
  96.             }  
  97.         }  
  98.     }  

我也看到這段代碼了。確實是有重發功能,默認重發次數是3次。請教下樓主@ autumnrain_zgq 跟你寫的這個方法比有哪些需要注意的地方?

這個代碼正常情況下沒有問題,遇到無效的deviceToken就無法重新發送信息了,還有,他不是批量提交信息的,
11 樓 lsqwind 2013-05-15  
rock 寫道
PushQueue支持重連,
源碼:
Java代碼  收藏代碼
  1. private void sendNotification(PushedNotification notification, boolean closeAfter) throws CommunicationException {  
  2.         try {  
  3.             Device device = notification.getDevice();  
  4.             Payload payload = notification.getPayload();  
  5.             try {  
  6.                 payload.verifyPayloadIsNotEmpty();  
  7.             } catch (IllegalArgumentException e) {  
  8.                 throw new PayloadIsEmptyException();  
  9.             } catch (Exception e) {  
  10.             }  
  11.   
  12.             if (notification.getIdentifier() <= 0) notification.setIdentifier(newMessageIdentifier());  
  13.             if (!pushedNotifications.containsKey(notification.getIdentifier())) pushedNotifications.put(notification.getIdentifier(), notification);  
  14.             int identifier = notification.getIdentifier();  
  15.   
  16.             String token = device.getToken();  
  17.             // even though the BasicDevice constructor validates the token, we revalidate it in case we were passed another implementation of Device  
  18.             BasicDevice.validateTokenFormat(token);  
  19.             //      PushedNotification pushedNotification = new PushedNotification(device, payload);  
  20.             byte[] bytes = getMessage(token, payload, identifier, notification);  
  21.             //      pushedNotifications.put(pushedNotification.getIdentifier(), pushedNotification);  
  22.   
  23.             /* Special simulation mode to skip actual streaming of message */  
  24.             boolean simulationMode = payload.getExpiry() == 919191;  
  25.   
  26.             boolean success = false;  
  27.   
  28.             BufferedReader in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));  
  29.             int socketTimeout = getSslSocketTimeout();  
  30.             if (socketTimeout > 0this.socket.setSoTimeout(socketTimeout);  
  31.             notification.setTransmissionAttempts(0);  
  32.             // Keep trying until we have a success  
  33.             while (!success) {  
  34.                 try {  
  35.                     logger.debug("Attempting to send notification: " + payload.toString() + "");  
  36.                     logger.debug("  to device: " + token + "");  
  37.                     notification.addTransmissionAttempt();  
  38.                     boolean streamConfirmed = false;  
  39.                     try {  
  40.                         if (!simulationMode) {  
  41.                             this.socket.getOutputStream().write(bytes);  
  42.                             streamConfirmed = true;  
  43.                         } else {  
  44.                             logger.debug("* Simulation only: would have streamed " + bytes.length + "-bytes message now..");  
  45.                         }  
  46.                     } catch (Exception e) {  
  47.                         if (e != null) {  
  48.                             if (e.toString().contains("certificate_unknown")) {  
  49.                                 throw new InvalidCertificateChainException(e.getMessage());  
  50.                             }  
  51.                         }  
  52.                         throw e;  
  53.                     }  
  54.                     logger.debug("Flushing");  
  55.                     this.socket.getOutputStream().flush();  
  56.                     if (streamConfirmed) logger.debug("At this point, the entire " + bytes.length + "-bytes message has been streamed out successfully through the SSL connection");  
  57.   
  58.                     success = true;  
  59.                     logger.debug("Notification sent on " + notification.getLatestTransmissionAttempt());  
  60.                     notification.setTransmissionCompleted(true);  
  61.   
  62.                 } catch (IOException e) {  
  63.                     // throw exception if we surpassed the valid number of retry attempts  
  64.                     if (notification.getTransmissionAttempts() >= retryAttempts) {  
  65.                         logger.error("Attempt to send Notification failed and beyond the maximum number of attempts permitted");  
  66.                         notification.setTransmissionCompleted(false);  
  67.                         notification.setException(e);  
  68.                         logger.error("Delivery error", e);  
  69.                         throw e;  
  70.   
  71.                     } else {  
  72.                         logger.info("Attempt failed (" + e.getMessage() + ")... trying again");  
  73.                         //Try again  
  74.                         try {  
  75.                             this.socket.close();  
  76.                         } catch (Exception e2) {  
  77.                             // do nothing  
  78.                         }  
  79.                         this.socket = connectionToAppleServer.getSSLSocket();  
  80.                         if (socketTimeout > 0this.socket.setSoTimeout(socketTimeout);  
  81.                     }  
  82.                 }  
  83.             }  
  84.         } catch (CommunicationException e) {  
  85.             throw e;  
  86.         } catch (Exception ex) {  
  87.   
  88.             notification.setException(ex);  
  89.             logger.error("Delivery error: " + ex);  
  90.             try {  
  91.                 if (closeAfter) {  
  92.                     logger.error("Closing connection after error");  
  93.                     stopConnection();  
  94.                 }  
  95.             } catch (Exception e) {  
  96.             }  
  97.         }  
  98.     }  

我也看到這段代碼了。確實是有重發功能,默認重發次數是3次。請教下樓主@ autumnrain_zgq 跟你寫的這個方法比有哪些需要注意的地方?
10 樓 autumnrain_zgq 2013-05-09  
sorehead 寫道
autumnrain_zgq 寫道
sorehead 寫道
樓主可以貼一份完整的代碼嗎,我也在糾結這個問題。

下載下來JavaAPNS源代碼項目,在PushNotificationManager這個類中增加我文章中增加的那個方法就可以了。其它地方我也沒有改的,

 this.createNewSocket();  都做了些什么事情?只是重新建立一個socket連接嗎?方便貼出這個方法嗎?
                

Java代碼  收藏代碼
  1. public void createNewSocket() throws SocketException, KeystoreException, CommunicationException {  
  2.     logger.info(psn + "創建新的Socke的連接");  
  3.     if (this.socket != null) {  
  4.         try {  
  5.             socket.close();  
  6.         } catch (Exception e) {  
  7.             logger.info("關閉Socket連接時出錯...", e);  
  8.         }  
  9.     }  
  10.     this.socket = connectionToAppleServer.getSSLSocket();  
  11.     this.socket.setSoTimeout(3000);  
  12.     this.socket.setKeepAlive(true);  
  13. }  
9 樓 sorehead 2013-05-08  
autumnrain_zgq 寫道
sorehead 寫道
樓主可以貼一份完整的代碼嗎,我也在糾結這個問題。

下載下來JavaAPNS源代碼項目,在PushNotificationManager這個類中增加我文章中增加的那個方法就可以了。其它地方我也沒有改的,

 this.createNewSocket();  都做了些什么事情?只是重新建立一個socket連接嗎?方便貼出這個方法嗎?
                
8 樓 autumnrain_zgq 2013-05-08  
sorehead 寫道
樓主可以貼一份完整的代碼嗎,我也在糾結這個問題。

下載下來JavaAPNS源代碼項目,在PushNotificationManager這個類中增加我文章中增加的那個方法就可以了。其它地方我也沒有改的,
7 樓 sorehead 2013-05-08  
樓主可以貼一份完整的代碼嗎,我也在糾結這個問題。
6 樓 rock 2013-04-24  
PushQueue支持重連,
源碼:
Java代碼  收藏代碼
  1. private void sendNotification(PushedNotification notification, boolean closeAfter) throws CommunicationException {  
  2.         try {  
  3.             Device device = notification.getDevice();  
  4.             Payload payload = notification.getPayload();  
  5.             try {  
  6.                 payload.verifyPayloadIsNotEmpty();  
  7.             } catch (IllegalArgumentException e) {  
  8.                 throw new PayloadIsEmptyException();  
  9.             } catch (Exception e) {  
  10.             }  
  11.   
  12.             if (notification.getIdentifier() <= 0) notification.setIdentifier(newMessageIdentifier());  
  13.             if (!pushedNotifications.containsKey(notification.getIdentifier())) pushedNotifications.put(notification.getIdentifier(), notification);  
  14.             int identifier = notification.getIdentifier();  
  15.   
  16.             String token = device.getToken();  
  17.             // even though the BasicDevice constructor validates the token, we revalidate it in case we were passed another implementation of Device  
  18.             BasicDevice.validateTokenFormat(token);  
  19.             //      PushedNotification pushedNotification = new PushedNotification(device, payload);  
  20.             byte[] bytes = getMessage(token, payload, identifier, notification);  
  21.             //      pushedNotifications.put(pushedNotification.getIdentifier(), pushedNotification);  
  22.   
  23.             /* Special simulation mode to skip actual streaming of message */  
  24.             boolean simulationMode = payload.getExpiry() == 919191;  
  25.   
  26.             boolean success = false;  
  27.   
  28.             BufferedReader in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));  
  29.             int socketTimeout = getSslSocketTimeout();  
  30.             if (socketTimeout > 0this.socket.setSoTimeout(socketTimeout);  
  31.             notification.setTransmissionAttempts(0);  
  32.             // Keep trying until we have a success  
  33.             while (!success) {  
  34.                 try {  
  35.                     logger.debug("Attempting to send notification: " + payload.toString() + "");  
  36.                     logger.debug("  to device: " + token + "");  
  37.                     notification.addTransmissionAttempt();  
  38.                     boolean streamConfirmed = false;  
  39.                     try {  
  40.                         if (!simulationMode) {  
  41.                             this.socket.getOutputStream().write(bytes);  
  42.                             streamConfirmed = true;  
  43.                         } else {  
  44.                             logger.debug("* Simulation only: would have streamed " + bytes.length + "-bytes message now..");  
  45.                         }  
  46.                     } catch (Exception e) {  
  47.                         if (e != null) {  
  48.                             if (e.toString().contains("certificate_unknown")) {  
  49.                                 throw new InvalidCertificateChainException(e.getMessage());  
  50.                             }  
  51.                         }  
  52.                         throw e;  
  53.                     }  
  54.                     logger.debug("Flushing");  
  55.                     this.socket.getOutputStream().flush();  
  56.                     if (streamConfirmed) logger.debug("At this point, the entire " + bytes.length + "-bytes message has been streamed out successfully through the SSL connection");  
  57.   
  58.                     success = true;  
  59.                     logger.debug("Notification sent on " + notification.getLatestTransmissionAttempt());  
  60.                     notification.setTransmissionCompleted(true);  
  61.   
  62.                 } catch (IOException e) {  
  63.                     // throw exception if we surpassed the valid number of retry attempts  
  64.                     if (notification.getTransmissionAttempts() >= retryAttempts) {  
  65.                         logger.error("Attempt to send Notification failed and beyond the maximum number of attempts permitted");  
  66.                         notification.setTransmissionCompleted(false);  
  67.                         notification.setException(e);  
  68.                         logger.error("Delivery error", e);  
  69.                         throw e;  
  70.   
  71.                     } else {  
  72.                         logger.info("Attempt failed (" + e.getMessage() + ")... trying again");  
  73.                         //Try again  
  74.                         try {  
  75.                             this.socket.close();  
  76.                         } catch (Exception e2) {  
  77.                             // do nothing  
  78.                         }  
  79.                         this.socket = connectionToAppleServer.getSSLSocket();  
  80.                         if (socketTimeout > 0this.socket.setSoTimeout(socketTimeout);  
  81.                     }  
  82.                 }  
  83.             }  
  84.         } catch (CommunicationException e) {  
  85.             throw e;  
  86.         } catch (Exception ex) {  
  87.   
  88.             notification.setException(ex);  
  89.             logger.error("Delivery error: " + ex);  
  90.             try {  
  91.                 if (closeAfter) {  
  92.                     logger.error("Closing connection after error");  
  93.                     stopConnection();  
  94.                 }  
  95.             } catch (Exception e) {  
  96.             }  
  97.         }  
  98.     }  
5 樓 autumnrain_zgq 2013-04-03  
鐜嬫旦 寫道
鐜嬫旦 寫道
pns 2.2版本不是支持群發嘛?

樓主,看到盡快回復下,,最近也在整這個

你好,是不支持群發的,群發的情況下,是在一個連接打開的情況下,持續寫入數據,遇到無效的deviceToken蘋果會斷開連接,并返回相應的消息編號,這時需要重新連接,再次發送數據。博文的代碼中這是這樣實現的,
4 樓 鐜嬫旦 2013-04-02  
鐜嬫旦 寫道
pns 2.2版本不是支持群發嘛?

樓主,看到盡快回復下,,最近也在整這個
3 樓 鐜嬫旦 2013-04-02  
pns 2.2版本不是支持群發嘛?
2 樓 autumnrain_zgq 2013-03-27  
linbaoji 寫道
請問你的 JavaPNS 版本是什么 啊!??
謝謝1

你好,我查看一下,版本是:2.2
1 樓 linbaoji 2013-03-20  
請問你的 JavaPNS 版本是什么 啊!??
謝謝1