You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP调用APNS发送iOS推送失败,返回Result:114如何排查

APNS推送问题排查:Result:114含义与代码修正

一、Result:114的含义

fwrite返回的114是成功写入到Socket连接的字节总数,这仅代表你的推送请求已成功发送至APNS服务器,但不代表苹果会将推送给递到目标设备。推送未送达的问题出在APNS处理环节或设备端,而非请求发送失败。

二、你的代码存在的核心问题

  1. 使用已废弃的旧版二进制格式
    你当前用chr(0)开头的是APNS早期的简单二进制格式(格式0),苹果已明确推荐使用增强二进制格式(格式1)或HTTP/2 API。旧格式无法接收APNS返回的错误响应,导致你无法得知推送失败的具体原因。

  2. 未利用传入的参数构建Payload
    函数接收了$title和$message参数,但构建$body['aps']时硬编码了标题和内容,完全未使用传入的变量,这会导致推送内容固定,不符合业务预期。

  3. 环境不匹配风险
    你连接的是生产环境地址gateway.push.apple.com:2195,如果你的测试设备使用的是开发证书打包的App,必须切换到沙箱环境地址gateway.sandbox.push.apple.com:2195,否则推送会被APNS拒绝。

  4. 未读取APNS的错误反馈
    即使使用旧格式,APNS在推送失败时会返回错误数据包,但你的代码发送后立即关闭连接,完全忽略了这一关键反馈渠道。

三、获取APNS错误信息的解决方案

方案1:切换到增强二进制格式(兼容旧版,但能获取错误)

修改消息构建和响应读取逻辑,这样可以获取APNS返回的错误状态码:

function sendPushAPN($to,$title,$message)
{
    $to=str_replace(" ","",$to);
    $deviceToken=$to;
    $passphrase = 'abc';

    $ctx = stream_context_create();
    stream_context_set_option($ctx, 'ssl', 'local_cert', 'aps.pem');
    stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);

    // 根据环境选择地址:生产用gateway.push.apple.com,沙箱用gateway.sandbox.push.apple.com
    $fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx);
    if (!$fp) exit("Failed to connect: $err $errstr" . PHP_EOL);
    echo 'Connected to APNS' . PHP_EOL;

    // 正确使用传入的参数构建Payload
    $body['aps']=array(
        'alert' => array(
            'title' => $title,
            'body' => $message['text']
        ),
        'sound' => 'default'
    );
    // 自定义字段放在aps数组外部
    $body['id'] = $message['id'];
    $payload = json_encode($body);

    // 构建增强二进制格式消息
    $identifier = time(); // 唯一消息ID,用于定位错误
    $expiry = time() + 3600; // 推送过期时间(1小时后)
    $msg = pack('C', 1) 
           . pack('N', $identifier) 
           . pack('N', $expiry) 
           . pack('n', 32) 
           . pack('H*', $deviceToken) 
           . pack('n', strlen($payload)) 
           . $payload;

    $result = fwrite($fp, $msg, strlen($msg));
    echo 'Result:' . $result . PHP_EOL;

    // 读取APNS错误响应
    stream_set_blocking($fp, 0);
    usleep(500000); // 等待500ms让APNS返回响应
    $response = fread($fp, 6);
    if ($response) {
        $error = unpack('Ccommand/Cstatus/Nidentifier', $response);
        echo "APNS错误状态码: {$error['status']}" . PHP_EOL;
        echo "对应消息ID: {$error['identifier']}" . PHP_EOL;
        // 状态码对应含义:0=无错,8=无效令牌,10=连接关闭,255=未知错误等
    }

    fclose($fp);
}

方案2:改用APNS HTTP/2 API(官方推荐)

HTTP/2 API的错误反馈更直观,支持批量推送,无需维护长连接,是当前苹果推荐的方式。示例代码(依赖Guzzle HTTP客户端):

require 'vendor/autoload.php';
use GuzzleHttp\Client;

function sendPushAPN($to,$title,$message)
{
    $deviceToken = str_replace(" ","",$to);
    $passphrase = 'abc';
    $certPath = 'aps.pem';
    $bundleId = '你的App Bundle ID'; // 替换为你的App的Bundle ID

    // 环境选择:生产用api.push.apple.com,沙箱用api.development.push.apple.com
    $client = new Client([
        'base_uri' => 'https://api.push.apple.com',
        'cert' => $certPath,
        'ssl_key' => $certPath,
        'passphrase' => $passphrase,
        'verify' => true // 生产环境建议开启SSL验证
    ]);

    $payload = json_encode([
        'aps' => [
            'alert' => [
                'title' => $title,
                'body' => $message['text']
            ],
            'sound' => 'default'
        ],
        'id' => $message['id']
    ]);

    try {
        $response = $client->post("/3/device/{$deviceToken}", [
            'headers' => [
                'apns-topic' => $bundleId,
                'apns-push-type' => 'alert'
            ],
            'body' => $payload
        ]);
        echo "推送成功,HTTP状态码: {$response->getStatusCode()}" . PHP_EOL;
    } catch (\GuzzleHttp\Exception\ClientException $e) {
        $errorBody = json_decode($e->getResponse()->getBody()->getContents(), true);
        echo "推送失败: " . json_encode($errorBody) . PHP_EOL;
    }
}

四、额外排查要点

  • 设备令牌有效性:确保设备令牌是App启动时最新获取的,用户卸载重装后令牌会变更;检查令牌格式是否正确(无空格,大小写不敏感但建议与设备返回一致)。
  • 证书权限:确认aps.pem包含完整的证书和私钥,passphrase与生成证书时设置的一致;生产证书对应生产环境,开发证书对应沙箱环境,不可混用。
  • App权限:确保App已向用户请求推送通知权限,且用户已允许。
  • 网络环境:设备需正常联网,国内环境下可能需要特殊网络访问APNS服务器。

内容的提问来源于stack exchange,提问作者dembo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 23:01:04