iOS 台设备已发送但未收到 APNs 消息
APNs messages are delivered but not received on iOS device
我已经设置了一个转发/调度服务,该服务保持与 APNs 的开放连接,如 Apple 所述/推荐的那样,以防止 DoS 检测并且证书正在运行...
连接成功,消息发送成功,但是iOS设备收不到消息(iPhone)...(?!?)发送的字节数相等到(字节数)fwrite returns.
最后,消息可能包含特定国家/地区的字符。 APNs 是如何处理的?以及这些字符应该如何发送到 APNs 服务器?
我尝试过使用和不使用转发服务,但都没有成功。 :-(
在代码片段中删除了所有对日志的调用以避免混淆。这意味着粘贴的日志消息不能直接映射到代码片段中,但应该可以 "guess" 来自日志消息的来源。
请帮我解决这些问题。
提前致谢
连接是这样的:
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $certPath);
stream_context_set_option($ctx, 'ssl', 'passphrase', $passPhrase);
$this->apn_link = stream_socket_client($url, $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx);
/* ... */
在转发服务上侦听套接字的循环内:
foreach ($changed as $changed_socket) {
while(isset($changed_socket) && socket_recv($changed_socket, $buf, 1024, 0) >= 1)
{
$buf = trim($buf);
if ($buf != '') {
$this->msg2apn($buf);
}
}
}
/* ... */
另外我是这样发消息的:
public function msg2apn($msg) {
if (isset($this->apn_link)) {
while(!$result = fwrite($this->apn_link, $msg, strlen($msg))) {
usleep(400000); //400 msec
}
if ($result) {
fflush($this->apn_link);
return True;
}
}
}
发送到转发服务的消息:
private function send_pn_iosproxy($msg) {
$address = IOS_APNPROXY_HOST;
$port = IOS_APNPROXY_PORT;
$fp = fsockopen($address,$port);
if (!$fp) {
elog("Connection to [".$address.":".$port."] could not be established");
return False;
}
$result = fwrite($fp, $msg, strlen($msg));
if (isset($fp)) fflush($fp);
if (isset($fp)) fclose($fp);
return True;
}
通过以下方式为每个设备令牌生成一条消息:
$payload = '{"aps":{"alert":"'.$message['message'].'","badge" : "1","sound":"default"}}';
foreach($deviceTokens as $deviceToken) {
// Build the binary notification
$msg = chr(0) . pack('n', 32) . base64_decode($deviceToken) . pack('n', strlen($payload)) . $payload;
// see function above
$result = $this->send_pn_iosproxy($msg);
if ($result) {
ilog("Message delivered");
dlog("DeviceToken[$deviceToken], Payload[$payload]");
} else {
wlog("Message not properly delivered");
dlog("Msg[$msg]");
dlog("DeviceToken[$deviceToken], Payload[$payload]");
}
}
logdisp的日志信息来自转发服务,而log的日志信息来自app-/web服务器。 Web 服务器使用 127.0.0.1/localhost 连接到转发服务,因此 Web 服务器和转发服务器托管在同一台物理计算机上。
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_ios() [] []
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_iosproxy() [] []
[2015-08-27 11:19:03] log.INFO: Message delivered [] []
[2015-08-27 11:19:03] logdisp.DEBUG: Client [127.0.0.1] connected [] []
[2015-08-27 11:19:03] log.DEBUG: DeviceToken[djYBbJepT8NOPKgLd5FoGOog9jxY8LcpQhCGaXAkqy0=], Payload[{"aps":{"alert":"message","badge" : "1","sound":"default"}}] [] []
[2015-08-27 11:19:03] logdisp.DEBUG: Message buf[...{"aps":{"alert":"message","badge" : "1","sound":"default"}}] from ip[127.0.0.1] [] []
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_iosproxy() [] []
[2015-08-27 11:19:03] logdisp.DEBUG: dispatcher::msg2apn() [] []
[2015-08-27 11:19:03] log.INFO: Message delivered [] []
[2015-08-27 11:19:03] logdisp.DEBUG: SUCCESS msg2apn, written[107], sent[107] [] []
[2015-08-27 11:19:03] log.DEBUG: DeviceToken[7FH3vRMfmb3qRBOaKY30GT/+82jU/6kx7RhBJq2Ihw8=], Payload[{"aps":{"alert":"message","badge" : "1","sound":"default"}}] [] []
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_iosproxy() [] []
[2015-08-27 11:19:03] log.INFO: Message delivered [] []
[2015-08-27 11:19:03] log.DEBUG: DeviceToken[b+20wASQBp9lD08l1+EY3CHJjcCl1UBWNmSeI1c9ouQ=], Payload[{"aps":{"alert":"message","badge" : "1","sound":"default"}}] [] []
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_iosproxy() [] []
[2015-08-27 11:19:03] log.INFO: Message delivered [] []
[2015-08-27 11:19:03] log.DEBUG: DeviceToken[Yya0lH080lz6fL2B/1UexOenBLyAJIp3zgSGgV9F5ms=], Payload[{"aps":{"alert":"message","badge" : "1","sound":"default"}}] [] []
[2015-08-27 11:19:04] logdisp.DEBUG: Client [127.0.0.1] connected [] []
[2015-08-27 11:19:04] logdisp.DEBUG: Message buf[...{"aps":{"alert":"message","badge" : "1","sound":"default"}}] from ip[127.0.0.1] [] []
[2015-08-27 11:19:04] logdisp.DEBUG: dispatcher::msg2apn() [] []
[2015-08-27 11:19:04] logdisp.DEBUG: SUCCESS msg2apn, written[107], sent[107] [] []
[2015-08-27 11:19:05] logdisp.DEBUG: Client [127.0.0.1] connected [] []
[2015-08-27 11:19:05] logdisp.DEBUG: Message buf[...{"aps":{"alert":"message","badge" : "1","sound":"default"}}] from ip[127.0.0.1] [] []
[2015-08-27 11:19:05] logdisp.DEBUG: dispatcher::msg2apn() [] []
[2015-08-27 11:19:05] logdisp.DEBUG: SUCCESS msg2apn, written[107], sent[107] [] []
[2015-08-27 11:19:07] logdisp.DEBUG: Client [127.0.0.1] connected [] []
[2015-08-27 11:19:07] logdisp.DEBUG: Message buf[...{"aps":{"alert":"message","badge" : "1","sound":"default"}}] from ip[127.0.0.1] [] []
[2015-08-27 11:19:07] logdisp.DEBUG: dispatcher::msg2apn() [] []
[2015-08-27 11:19:07] logdisp.DEBUG: SUCCESS msg2apn, written[107], sent[107] [] []
我尝试发送了两种负载:
{"aps" : {"alert" : "message","badge" : "1","sound" : "default"}}
{"aps" : { "alert" : { "title" : "My Title", "body" : "My Message" }, "badge" : 2, "sound" : "default" } }
重新生成证书后,连接时出现以下错误:
[vagrant@server backend-src]$ php -f dispatcher.php
Log path: [/srv/backend/log/log.txt], Log level: [0],
Binding info: address[127.0.0.1], port[6060]
Connecting to [ssl://gateway.push.apple.com:2195], using certificate [/srv/backend/htdocs/ios_distribution.pem] and passphrase[?]
PHP Warning: stream_socket_client(): Failed to enable crypto in /vagrant/backend-src/dispatcher.php on line 182
PHP Warning: stream_socket_client(): unable to connect to ssl://gateway.push.apple.com:2195 (Unknown error) in /vagrant/backend-src/dispatcher.php on line 182
Warning: stream_socket_client(): unable to connect to ssl://gateway.push.apple.com:2195 (Unknown error) in /vagrant/backend-src/dispatcher.php on line 182
Connect failed error[0 ] to url [ssl://gateway.push.apple.com:2195] with certicate [/srv/backend/htdocs/ios_distribution.pem]
我现在可以连接了!这里的原因很简单。证书的路径不正确。但是,使用新证书的设备仍未收到推送通知。
应用程序是否需要使用新证书发布/发布才能运行???无论是在开发中还是在生产中,设备上都没有收到消息???
最后,它成功接收了沙盒和生产/临时令牌的消息。
// 64 chars token
// Sandbox and productions tokens have the same length and
// they cannot be distinguished
$deviceToken='1234567890123456789012345678901234567890123456789012345678901234';
$body['aps'] = array('alert' => 'Sample message', 'sound' => 'default');
$payload = json_encode($body);
$deviceToken = str_replace(' ', '', $deviceToken);
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
fwrite($apn_link, $msg, strlen($msg));
fflush($apn_link);
不要在生产中混用沙箱和生产设备令牌!
APN 生产环境不允许沙箱令牌
并且当
已使用沙盒令牌。
// 64 chars token
// Sandbox and productions tokens have the same length and
// they cannot be distinguished
$deviceToken='1234567890123456789012345678901234567890123456789012345678901234';
$body['aps'] = array('alert' => 'Sample message', 'sound' => 'default');
$payload = json_encode($body);
$deviceToken = str_replace(' ', '', $deviceToken);
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
fwrite($apn_link, $msg, strlen($msg));
fflush($apn_link);
不要在生产中混用沙盒和生产设备令牌!
APN 生产环境不允许沙盒令牌,并且在使用沙盒令牌时它会自动停止向生产令牌传送消息。
Apple 做了很大的改变:虽然它仍然允许创建仅限沙盒的证书,但生产证书可以在沙盒和生产环境中使用。
我已经设置了一个转发/调度服务,该服务保持与 APNs 的开放连接,如 Apple 所述/推荐的那样,以防止 DoS 检测并且证书正在运行...
连接成功,消息发送成功,但是iOS设备收不到消息(iPhone)...(?!?)发送的字节数相等到(字节数)fwrite returns.
最后,消息可能包含特定国家/地区的字符。 APNs 是如何处理的?以及这些字符应该如何发送到 APNs 服务器?
我尝试过使用和不使用转发服务,但都没有成功。 :-(
在代码片段中删除了所有对日志的调用以避免混淆。这意味着粘贴的日志消息不能直接映射到代码片段中,但应该可以 "guess" 来自日志消息的来源。
请帮我解决这些问题。 提前致谢
连接是这样的:
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $certPath);
stream_context_set_option($ctx, 'ssl', 'passphrase', $passPhrase);
$this->apn_link = stream_socket_client($url, $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx);
/* ... */
在转发服务上侦听套接字的循环内:
foreach ($changed as $changed_socket) {
while(isset($changed_socket) && socket_recv($changed_socket, $buf, 1024, 0) >= 1)
{
$buf = trim($buf);
if ($buf != '') {
$this->msg2apn($buf);
}
}
}
/* ... */
另外我是这样发消息的:
public function msg2apn($msg) {
if (isset($this->apn_link)) {
while(!$result = fwrite($this->apn_link, $msg, strlen($msg))) {
usleep(400000); //400 msec
}
if ($result) {
fflush($this->apn_link);
return True;
}
}
}
发送到转发服务的消息:
private function send_pn_iosproxy($msg) {
$address = IOS_APNPROXY_HOST;
$port = IOS_APNPROXY_PORT;
$fp = fsockopen($address,$port);
if (!$fp) {
elog("Connection to [".$address.":".$port."] could not be established");
return False;
}
$result = fwrite($fp, $msg, strlen($msg));
if (isset($fp)) fflush($fp);
if (isset($fp)) fclose($fp);
return True;
}
通过以下方式为每个设备令牌生成一条消息:
$payload = '{"aps":{"alert":"'.$message['message'].'","badge" : "1","sound":"default"}}';
foreach($deviceTokens as $deviceToken) {
// Build the binary notification
$msg = chr(0) . pack('n', 32) . base64_decode($deviceToken) . pack('n', strlen($payload)) . $payload;
// see function above
$result = $this->send_pn_iosproxy($msg);
if ($result) {
ilog("Message delivered");
dlog("DeviceToken[$deviceToken], Payload[$payload]");
} else {
wlog("Message not properly delivered");
dlog("Msg[$msg]");
dlog("DeviceToken[$deviceToken], Payload[$payload]");
}
}
logdisp的日志信息来自转发服务,而log的日志信息来自app-/web服务器。 Web 服务器使用 127.0.0.1/localhost 连接到转发服务,因此 Web 服务器和转发服务器托管在同一台物理计算机上。
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_ios() [] []
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_iosproxy() [] []
[2015-08-27 11:19:03] log.INFO: Message delivered [] []
[2015-08-27 11:19:03] logdisp.DEBUG: Client [127.0.0.1] connected [] []
[2015-08-27 11:19:03] log.DEBUG: DeviceToken[djYBbJepT8NOPKgLd5FoGOog9jxY8LcpQhCGaXAkqy0=], Payload[{"aps":{"alert":"message","badge" : "1","sound":"default"}}] [] []
[2015-08-27 11:19:03] logdisp.DEBUG: Message buf[...{"aps":{"alert":"message","badge" : "1","sound":"default"}}] from ip[127.0.0.1] [] []
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_iosproxy() [] []
[2015-08-27 11:19:03] logdisp.DEBUG: dispatcher::msg2apn() [] []
[2015-08-27 11:19:03] log.INFO: Message delivered [] []
[2015-08-27 11:19:03] logdisp.DEBUG: SUCCESS msg2apn, written[107], sent[107] [] []
[2015-08-27 11:19:03] log.DEBUG: DeviceToken[7FH3vRMfmb3qRBOaKY30GT/+82jU/6kx7RhBJq2Ihw8=], Payload[{"aps":{"alert":"message","badge" : "1","sound":"default"}}] [] []
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_iosproxy() [] []
[2015-08-27 11:19:03] log.INFO: Message delivered [] []
[2015-08-27 11:19:03] log.DEBUG: DeviceToken[b+20wASQBp9lD08l1+EY3CHJjcCl1UBWNmSeI1c9ouQ=], Payload[{"aps":{"alert":"message","badge" : "1","sound":"default"}}] [] []
[2015-08-27 11:19:03] log.DEBUG: pn::send_pn_iosproxy() [] []
[2015-08-27 11:19:03] log.INFO: Message delivered [] []
[2015-08-27 11:19:03] log.DEBUG: DeviceToken[Yya0lH080lz6fL2B/1UexOenBLyAJIp3zgSGgV9F5ms=], Payload[{"aps":{"alert":"message","badge" : "1","sound":"default"}}] [] []
[2015-08-27 11:19:04] logdisp.DEBUG: Client [127.0.0.1] connected [] []
[2015-08-27 11:19:04] logdisp.DEBUG: Message buf[...{"aps":{"alert":"message","badge" : "1","sound":"default"}}] from ip[127.0.0.1] [] []
[2015-08-27 11:19:04] logdisp.DEBUG: dispatcher::msg2apn() [] []
[2015-08-27 11:19:04] logdisp.DEBUG: SUCCESS msg2apn, written[107], sent[107] [] []
[2015-08-27 11:19:05] logdisp.DEBUG: Client [127.0.0.1] connected [] []
[2015-08-27 11:19:05] logdisp.DEBUG: Message buf[...{"aps":{"alert":"message","badge" : "1","sound":"default"}}] from ip[127.0.0.1] [] []
[2015-08-27 11:19:05] logdisp.DEBUG: dispatcher::msg2apn() [] []
[2015-08-27 11:19:05] logdisp.DEBUG: SUCCESS msg2apn, written[107], sent[107] [] []
[2015-08-27 11:19:07] logdisp.DEBUG: Client [127.0.0.1] connected [] []
[2015-08-27 11:19:07] logdisp.DEBUG: Message buf[...{"aps":{"alert":"message","badge" : "1","sound":"default"}}] from ip[127.0.0.1] [] []
[2015-08-27 11:19:07] logdisp.DEBUG: dispatcher::msg2apn() [] []
[2015-08-27 11:19:07] logdisp.DEBUG: SUCCESS msg2apn, written[107], sent[107] [] []
我尝试发送了两种负载:
{"aps" : {"alert" : "message","badge" : "1","sound" : "default"}}
{"aps" : { "alert" : { "title" : "My Title", "body" : "My Message" }, "badge" : 2, "sound" : "default" } }
重新生成证书后,连接时出现以下错误:
[vagrant@server backend-src]$ php -f dispatcher.php
Log path: [/srv/backend/log/log.txt], Log level: [0],
Binding info: address[127.0.0.1], port[6060]
Connecting to [ssl://gateway.push.apple.com:2195], using certificate [/srv/backend/htdocs/ios_distribution.pem] and passphrase[?]
PHP Warning: stream_socket_client(): Failed to enable crypto in /vagrant/backend-src/dispatcher.php on line 182
PHP Warning: stream_socket_client(): unable to connect to ssl://gateway.push.apple.com:2195 (Unknown error) in /vagrant/backend-src/dispatcher.php on line 182
Warning: stream_socket_client(): unable to connect to ssl://gateway.push.apple.com:2195 (Unknown error) in /vagrant/backend-src/dispatcher.php on line 182
Connect failed error[0 ] to url [ssl://gateway.push.apple.com:2195] with certicate [/srv/backend/htdocs/ios_distribution.pem]
我现在可以连接了!这里的原因很简单。证书的路径不正确。但是,使用新证书的设备仍未收到推送通知。
应用程序是否需要使用新证书发布/发布才能运行???无论是在开发中还是在生产中,设备上都没有收到消息???
最后,它成功接收了沙盒和生产/临时令牌的消息。
// 64 chars token
// Sandbox and productions tokens have the same length and
// they cannot be distinguished
$deviceToken='1234567890123456789012345678901234567890123456789012345678901234';
$body['aps'] = array('alert' => 'Sample message', 'sound' => 'default');
$payload = json_encode($body);
$deviceToken = str_replace(' ', '', $deviceToken);
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
fwrite($apn_link, $msg, strlen($msg));
fflush($apn_link);
不要在生产中混用沙箱和生产设备令牌!
APN 生产环境不允许沙箱令牌 并且当 已使用沙盒令牌。
// 64 chars token
// Sandbox and productions tokens have the same length and
// they cannot be distinguished
$deviceToken='1234567890123456789012345678901234567890123456789012345678901234';
$body['aps'] = array('alert' => 'Sample message', 'sound' => 'default');
$payload = json_encode($body);
$deviceToken = str_replace(' ', '', $deviceToken);
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
fwrite($apn_link, $msg, strlen($msg));
fflush($apn_link);
不要在生产中混用沙盒和生产设备令牌!
APN 生产环境不允许沙盒令牌,并且在使用沙盒令牌时它会自动停止向生产令牌传送消息。
Apple 做了很大的改变:虽然它仍然允许创建仅限沙盒的证书,但生产证书可以在沙盒和生产环境中使用。