使用Java服务器应用程序,该应用程序将Apple推送通知发送给用户。
在最近几天工作以解决SSL握手问题,获取" javax.net.ssl.SSLHandshakeException:握手期间远程主机关闭连接"当我的Java服务器应用程序尝试发送APNS消息时
尝试了两个独立的库APNS(https://github.com/notnoop/java-apns_和Java-APNS(https://code.google.com/p/javapns/),但在两种情况下都遇到了同样的问题
这是我的APNS示例代码
@Service(value = "apnsPushService")
public class APNSPushService extends AbstractPushService {
protected String certificate = null;
protected String passphrase = null;
private ApnsService service = null;
@Override
@PostConstruct
public void init() throws Exception {
super.init();
try {
service = buildApnsService();
service.start();
} catch (Exception e) {
logger.error("While building the APNS service", e);
throw e;
}
}
@Override
public boolean pushMessage(String notificationKey, String message) throws Exception {
PayloadBuilder builder = APNS.newPayload().alertBody(alert) // alert dialog, in iOS
.badge(badge) // little badge icon update;
.sound(sound); // sound to be played by app
builder = builder.alertBody(message);
final String apnsMessage = builder.build(); // build the JSON payload for APNs
logger.info("Sending transformed APNs payload: " + apnsMessage);
service.push(notificationKey, apnsMessage, createFutureDateBasedOnTTL(timeToLive));
return true;
}
/**
* Helper method that creates a future {@link Date}, based on the given ttl/time-to-live value. If no TTL was provided, we use the max date from
* the APNs library
*/
private Date createFutureDateBasedOnTTL(int ttl) {
// no TTL was specified on the payload, we use the MAX Default from the
// APNs library:
if (ttl == -1) {
return new Date(System.currentTimeMillis() + EnhancedApnsNotification.MAXIMUM_EXPIRY * 1000L);
} else {
// apply the given TTL to the current time
return new Date(System.currentTimeMillis() + ttl);
}
}
/**
* Build the APNS Service
*
* @return
*/
private ApnsService buildApnsService() throws Exception {
ApnsService apnsService = null;
// this check should not be needed, but you never know:
if (certificate != null && passphrase != null) {
final ApnsServiceBuilder builder = APNS.newService();
try(InputStream stream = new FileInputStream(certificate)) {
builder.withCert(stream, passphrase);
// pick the destination:
if (production) {
builder.withProductionDestination();
} else {
builder.withSandboxDestination();
}
}
// create the service
return builder.build();
}
return apnsService;
}
@Value("${apns.certificate}")
public void setCertificate(String certificate) {
this.certificate = certificate;
}
@Value("${apns.passphrase}")
public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}
@Value("${apns.production}")
public void setProduction(String production) {
this.production = Boolean.parseBoolean(production);
}
@Value("${apns.alert}")
public void setAlert(String alert) {
this.alert = alert;
}
@Value("${apns.sound}")
public void setSound(String sound) {
this.sound = sound;
}
@Value("${apns.badge}")
public void setBadge(String badge) {
this.badge = Integer.parseInt(badge);
}
@Value("${apns.timetolive}")
public void setTimeToLive(String timeToLive) {
this.timeToLive = Integer.parseInt(timeToLive);
}
}
这是我的Java APNS代码 -
public class JavaAPNSPushService extends AbstractPushService {
protected String certificate = null;
protected String passphrase = null;
private PushQueue queue = null;
private int counter = 1;
@Override
@PostConstruct
public void init() throws Exception {
super.init();
queue= Push.queue(certificate, passphrase, production, 10);
queue.start();
}
@Override
public boolean pushMessage(String notificationKey, String message) throws Exception
{
if (counter++ > 1000) {
counter = 1;
queue.clearPushedNotifications();
}
PushNotificationPayload payload = new PushNotificationPayload(alert, badge, sound);
payload.addAlert(message);
payload.setExpiry(timeToLive);
PushQueue tempQueue = queue.add(payload, notificationKey);
List<Exception> expList = tempQueue.getCriticalExceptions();
if (expList.size() > 0) {
expList.get(0).printStackTrace();
}
PushedNotification notification = tempQueue.getPushedNotifications().firstElement();
while(!notification.isTransmissionCompleted()) {
Thread.sleep(100);
}
return notification.isSuccessful();
}
@Value("${apns.certificate}")
public void setCertificate(String certificate) {
this.certificate = certificate;
}
@Value("${apns.passphrase}")
public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}
@Value("${apns.production}")
public void setProduction(String production) {
this.production = Boolean.parseBoolean(production);
}
@Value("${apns.alert}")
public void setAlert(String alert) {
this.alert = alert;
}
@Value("${apns.sound}")
public void setSound(String sound) {
this.sound = sound;
}
@Value("${apns.badge}")
public void setBadge(String badge) {
this.badge = Integer.parseInt(badge);
}
@Value("${apns.timetolive}")
public void setTimeToLive(String timeToLive) {
this.timeToLive = Integer.parseInt(timeToLive);
}
}
这是我的异常堆栈跟踪 -
javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1343)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:728)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123)
at java.io.OutputStream.write(OutputStream.java:75)
at javapns.notification.PushNotificationManager.sendNotification(PushNotificationManager.java:402)
at javapns.notification.PushNotificationManager.sendNotification(PushNotificationManager.java:350)
at javapns.notification.PushNotificationManager.sendNotification(PushNotificationManager.java:320)
at javapns.Push.sendPayload(Push.java:177)
at javapns.Push.test(Push.java:132)
at com.aaplab.konvx.push.apns.JavaAPNSPushService.pushMessage(JavaAPNSPushService.java:69)
at com.aaplab.konvx.push.AbstractPushService.push(AbstractPushService.java:61)
at com.aaplab.konvx.PushTest.JavaAPNSPushTest(PushTest.java:36)
at com.aaplab.konvx.PushTest.main(PushTest.java:23)
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at sun.security.ssl.InputRecord.read(InputRecord.java:505)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
... 13 more
已经遵循了以下各种建议:
从私钥创建.p12证书文件,从Apple重新下载证书。重复多次,但没有运气。
确保认证是开发模式而非生产模式。
即使尝试使用信任所有证书的示例代码,仍然会遇到同样的问题。
虽然我已经成功地远程登录&#34; gateway.sandbox.push.apple.com&#34; / 2195但是当涉及到java代码时,事情无法正常工作。
它肯定不是代理问题,因为我已经从我的Windows机器以及我们的生产服务器测试过它,双方都失败了。
不批量发送邮件,只是尝试发送单个邮件并且邮件失败。