查询 APNs 反馈服务器的 PHP 技术

Posted

技术标签:

【中文标题】查询 APNs 反馈服务器的 PHP 技术【英文标题】:PHP technique to query the APNs Feedback Server 【发布时间】:2010-11-19 17:07:41 【问题描述】:

就您如何查询而言,有人可以澄清 APN(Apple 推送通知)想要什么吗?

文档说一旦建立连接就会开始发送。这是否意味着我没有在上面做fread()

这是我当前尝试阅读的代码。我没有将fread() 放入循环中,因为我不知道什么响应表明“没有更多记录可读取”并且我不希望我的服务器上出现无限循环。

<?php
$apnsCert = 'HOHRO-prod.pem';

$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
stream_context_set_option($streamContext, 'ssl', 'verify_peer', false);

$apns = stream_socket_client('ssl://feedback.push.apple.com:2196', $error, $errorString, 60, STREAM_CLIENT_CONNECT, $streamContext);

echo 'error=' . $error;
echo 'errorString=' . $errorString;


$result = fread($apns, 38);
echo 'result=' . $result;


fclose($apns);
?>

到目前为止,我得到的只是一个空回复。没有错误,所以它正在连接。

我不知道空回复是否意味着没有数据,或者我的fread()是错误的方法。

谢谢

【问题讨论】:

即使我有类似的代码,我也从 fread 得到 null。不确定我的代码是否真的有效,APNS 是否正在发送 NULL 回复或只是缺少某些东西。我确定我与 APNS 的连接是成功的。我已经在大约 5-10 台设备上安装了我的应用程序并发送了几个警报,然后从少数几个设备中卸载了该应用程序并再次开始发送警报,以便 APNS 反馈服务器让我们知道它无法在少数设备上交付。除了 fread() 中的空白之外,我什么都没有 :-( 如果你做了一些不同的事情并让它工作,请告诉我。 你用过“pushutil”吗?一旦弄清楚,这是检查反馈服务器的更快方法。它是一个 Mac OS X 实用程序,您可以编译然后从 Unix 命令行运行。谷歌 Erica Sadun - 它在她的网站上的 Push 下。 即使我发送了 76 条推送通知,仍然没有收到反馈服务器的响应——其中一些不得不失败。当我使用 Erica Sadun 的“pushutil”命令行实用程序时,也会发生同样的事情 - 推送正常,然后我删除应用程序并再次推送,但反馈服务器上仍然没有出现任何内容。这是 Erica 的应用程序,所以我知道它必须有效。我认为问题必须是证书。我正在使用与推送相同的推送生产证书。没有单独的反馈证书,是吗? 今天我发现 fread() 不起作用,因为 APNs 反馈发送垃圾数​​据,直到实际反馈。这会导致 fread 失败,因为它正在读取空/空数据。正如 gw1921 在下面提到的,您必须在 feof() 上循环,直到 strlen(fread($apns, 38))。从那时起,您将获得有效的反馈。然后,按照下面的 Nick B 的建议解压数据。 (另一个讨厌的问题:卸载的应用程序不会生成反馈,除非您的设备上有另一个应用程序具有相同的 aps 环境。因此,您的设备上需要 2 个启用推送的沙盒应用程序,以便卸载的应用程序填充反馈。) 【参考方案1】:

我从苹果论坛得到了解决方案,它是用于开发的。也可以试试这个。

“好吧,尽管听起来很愚蠢,但我找到了解决方案:

在程序门户中创建一个虚拟应用程序 ID,在其上启用开发推送通知 创建并下载关联的配置文件 创建一个新的 xcode 项目,并在启动时调用 registerForRemoteNotificationTypes 方法。 在您的设备上安装虚拟应用程序。此时,您的设备上应该运行了两个 DEVELOPMENT 应用程序:原始应用程序和虚拟应用程序。两者都应该注册以接收推送通知。 卸载原始应用程序,并尝试向该应用程序发送推送通知。 调用反馈服务,您应该会收到返回的数据。”

【讨论】:

【参考方案2】:

该代码看起来正确,但是您需要循环并检查流的结尾才能读取所有设备代码。

 while (!feof($apns)) 
        $devcon = fread($apns, 38);
 

但是我的问题是数据的实际解包。有谁知道如何解压您刚刚读取的二进制数据以获取实际的设备 ID(作为字符串)以及时间戳等?

【讨论】:

我认为这是正确的想法 - $array = unpack("NnH32", $result); $feedbackTime = $row[0]; $feedbackLen = $row[1]; $feedbackUDID = $row[2];这将解包反馈服务器发送的 38 个字节。但是,32 位日期值采用网络顺序或大端顺序。如果有人可以提供一个 PHP 函数,将这 4 个字节翻转为 Intel(小端)顺序,我认为我们有解决方案。注意:实际的 UDID 是一个字符串,不需要翻转它的顺序。 这有什么好处吗? --------- /* 将浮点数从 HostOrder 转换为 Network Order / function FToN( $val ) $a = unpack("I",pack( "f",$val ));返回包("N",$a[1]); / 将浮点数从 Network Order 转换为 HostOrder */ function NToF($val ) $a = unpack("N",$val); $b = unpack("f",pack("I",$a[1]));返回 $b[1]; 【参考方案3】:

这终于对我有用了。

$arr = unpack("H*", $devconts); 
$rawhex = trim(implode("", $arr));

$feedbackTime = hexdec(substr($rawhex, 0, 8)); 
$feedbackDate = date('Y-m-d H:i', $feedbackTime); 
$feedbackLen = hexdec(substr($rawhex, 8, 4)); 
$feedbackDeviceToken = substr($rawhex, 12, 64);

然后您只需根据时间戳检查设备令牌!

【讨论】:

这很好用,gw1921。我现在将 $feedbackDate 存储在 SQL 列中。该列应该是什么类型的数据?我将它设置为整数,这给了我“2009”。其他 2 列,长度和标记,工作得很好!!谢谢【参考方案4】:

当我第一次尝试连接时,有一个大问题让我感到困惑:APNS 反馈服务器只返回自您上次请求反馈后“过期”的设备令牌。这意味着大多数情况下您会收到 NULL 响应,除非您已经在与大量应用用户打交道。

因此,请确保将过期的设备令牌存储到磁盘或数据库中,因为在您的反馈查询之后,它们已经一去不复返了。至少可以说,这让测试变得很痛苦!

这是从 APNS 反馈服务器获取设备令牌的完整功能(非常感谢上面的答案帮助我将它们放在一起):

function send_feedback_request() 
    //connect to the APNS feedback servers
    //make sure you're using the right dev/production server & cert combo!
    $stream_context = stream_context_create();
    stream_context_set_option($stream_context, 'ssl', 'local_cert', '/path/to/my/cert.pem');
    $apns = stream_socket_client('ssl://feedback.push.apple.com:2196', $errcode, $errstr, 60, STREAM_CLIENT_CONNECT, $stream_context);
    if(!$apns) 
        echo "ERROR $errcode: $errstr\n";
        return;
    


    $feedback_tokens = array();
    //and read the data on the connection:
    while(!feof($apns)) 
        $data = fread($apns, 38);
        if(strlen($data)) 
            $feedback_tokens[] = unpack("N1timestamp/n1length/H*devtoken", $data);
        
    
    fclose($apns);
    return $feedback_tokens;

如果一切顺利,该函数的返回值将如下所示(通过 print_r()):

Array
(
    Array
    (
        [timestamp] => 1266604759
        [length] => 32
        [devtoken] => abc1234..............etcetc
    ),
    Array
    (
        [timestamp] => 1266604922
        [length] => 32
        [devtoken] => def56789..............etcetc
    ),
)

【讨论】:

感谢这个优雅的解决方案。现在我只需要知道如何将上述反馈代码返回的时间戳与我保存在数据库中的时间戳进行比较,这当然代表设备向我发送令牌的最近时间。我只是做一个直接整数比较吗?还是我必须将时间戳转换为正确的字节序或其他什么? 这个答案应该是公认的解决方案,IMO。这基本上就是我正在做的事情,而且效果很好。如果您仍然看不到使用此方法的反馈数据,请验证您的设备上是否有另一个应用程序启用了推送功能,该应用程序向与已卸载应用程序相同的 aps 环境报告。约翰尼-关于时间戳,一旦你按照尼克的帖子解压它,你就可以进行整数比较。 在什么情况下我不会得到反馈服务器的响应?我的通知通过正常(正如我可以从日志中说的那样),但由于某种原因,我从未在设备上收到任何通知! "APNS 反馈服务器仅返回自您上次反馈请求以来已“过期”的设备令牌。"哎呀,我想我真的应该阅读文档;( 我试过使用这个功能。我真的很确定我正在使用正确的证书/服务器组合。我一直在使用 .sandbox。我正在向开发设备发送两个推送通知,一个设备收到消息并且反馈数组为空。【参考方案5】:

刚开始使用这个库 - 非常适合我!

https://github.com/mac-cain13/notificato

【讨论】:

以上是关于查询 APNs 反馈服务器的 PHP 技术的主要内容,如果未能解决你的问题,请参考以下文章

来自 APNS(Apple 推送通知服务器)的反馈服务

傻瓜的 APNS 反馈和 PHP

如何使用 java 编写 APNS 反馈服务?

Apple 推送通知服务 (APNS):反馈服务响应缺少字节?

通知 APNS 反馈服务有关停用通知的设备?

APNS 奇怪的反馈