0

0

基于Codeigniter框架实现的APNS批量推送—叮咚,查水表

php中文网

php中文网

发布时间:2016-08-18 08:57:58

|

1476人浏览过

|

来源于php中文网

原创

   最近兼职公司已经众筹成功的无线门铃的消息推送出现了问题,导致有些用户接收不到推送的消息,真是吓死宝宝了,毕竟自己一手包办的后台服务,影响公司信誉是多么的尴尬,容我简单介绍一下我们的需求:公司开发的是一款无线门铃系统,如果有人在门外按了门铃开关,门铃开关会发射一个信号,屋里的接收网关接收到信号会发出响声,同时也会推送一条消息到用户手机,即使这个手机是远程的,也就是主人不在家也知道有人按了家里的门铃。这里后台需要解决的问题是搭建apns推送的provider,因为要想把消息推送到苹果手机,按照苹果公司设计的机制,必须通过自己的服务器推送到苹果的push服务器,再由它推送到手机,每个手机对应一个devicetoken,我这里介绍的重点并不是这个平台怎么搭建,这个国内网上的教程已经相当丰富了。比如你可以参考:一步一步教你做ios推送

网上的教程大多是走的通的,但是他们操作的对象是一个手机,我的意思是它们是一次给一个手机终端推送消息,在我们公司设计的产品中,同一个账户可以在多个手机上登录(理论上是无数个,因为我在后台并没有限制),每个手机对应的deviceToken是不同的,另外公司的产品还设计了分享功能,也就是主用户可以把设备分享给其他用户,而其他用户也有可能在不同设备上同时登录,如果有人按了门铃要向所有已经登录的用户包括分享的用户推送消息,也就是要批量推送到很多个手机终端。当然我这里举的例子并不会有这么复杂,所有的问题抽象出来其实就是一个问题:给你一个存储deviceToken的数组,APNS如何批量推送给多个用户?

 

首先我们设计一个数据库,用来存储用户的推送令牌(deviceToken),为简单起见,这个表就两个字段。

client_id deviceToken
1 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

 

这里我使用的是CodeIgniter3的框架,我们新建一个Model,来管理用户deviceToken数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// ios推送令牌管理
 
 
 
class Apns_model extends CI_Model
{
 
    public function __construct()
    {
        $this->load->database();
    }
 
 
    /**
     * 新建推送令牌
     * create a apns如果已经存在就更新这个deviceToken
     * $data is an array organized by controller
     */
    public function create($data)
    {
        if($this->db->replace('tb_apns', $data))
        {
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
 
 
 
 
    //删除某个用户的推送令牌
    public function delete($user_id)
    {
         
        if(isset($user_id)){
            $result=$this->db->delete('tb_apns', array('client_id' => $user_id)); 
            return TRUE;           
        }else{
            return FALSE;
        }
 
    }
 
 
    //根据推送令牌删除推送令牌
    public function deletebytoken($token)
    {
         
        if (isset($token)) {
            $result=$this->db->delete('tb_apns', array('deviceToken'=>$token));
            return TRUE;
 
        }else{
            return FALSE;
        }
 
    }
 
 
 
    //查询某个用户的iso推送令牌
    public function get($client_id)
    {
        $sql = "SELECT deviceToken FROM `tb_apns` WHERE `client_id`='$client_id'";
 
        $result = $this->db->query($sql);
 
        if ($result->num_rows()>0)
        {
            return $result->result_array();
        }
        else
        {
            return FALSE;
        }
    }
 
 
 
 
}

在我后台的第一个版本中,按照网上的教程,大多是一次给一个终端推送消息的,我稍微改了一下,将所有取得的deviceToken存在$deviceTokens数组中,参数$message是需要推送的消息,使用for循环依次从数组中取出一个deviceToken来推送,然后计数,如果所有的推送成功则返回true。这个方法看似是没有任何破绽的,而且也测试成功了,所以我就直接上线了,(主要是我也没想到公司会突然出这样一个产品,把推送功能的地位抬得很高,我一直以为是可有可无的)。

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
function _send_apns($deviceTokens,$message)
    {
 
 
      // Put your private key's passphrase here:密语
       $passphrase = 'xxxxx';
 
           ////////////////////////////////////////////////////////////////////////////////
 
       $ctx = stream_context_create();
       stream_context_set_option($ctx, 'ssl', 'local_cert', 'xxxx.pem');
       stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
 
       // Open a connection to the APNS server
       $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;
 
        // Create the payload body
       $body['aps'] = array(
           'alert' => $message,
           'sound' => 'default'
           );
 
         // Encode the payload as JSON
       $payload = json_encode($body);
 
       $num=count($deviceTokens);
       $countOK=0;//统计发送成功的条数
 
       for($i=0;$i$num;$i++)
       {
           $deviceToken=$deviceTokens[$i];
 
           $deviceToken=preg_replace("/\s/","",$deviceToken);//删除deviceToken里的空格
 
 
        // Build the binary notification
           $msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
 
        // Send it to the server
           $result = fwrite($fp, $msg, strlen($msg));
 
           if ($result)
         
           {
               $countOK++;
            
           }
       }
 
      // Close the connection to the server
       fclose($fp);
       if($countOK==$num)
           return TRUE;
       else
           return FALSE;
 
   }

就是上面的代码导致了后来推送出现了一系列问题。

第一个大问题是:这里默认了所有的推送令牌都是有效的,而实际上,如果用户直接删除了app或者app升级都有可能造成后台数据库里的deviceToken没有发生更新,从而使推送令牌失效。但是有人按了门铃,后台还是会把它当成有效的deviceToken纳入到$deviceTokens中,如何清除失效过期的deviceToken是个必须考虑的问题。

Postme
Postme

Postme是一款强大的AI写作工具,可以帮助您快速生成高质量、原创的外贸营销文案,助您征服全球市场。

下载

查阅相关资料发现APNS服务有一个The Feedback Service的服务,国内的博客基本上忽略了这个环节,很少有资料提及,还是谷歌找个官方网站比较靠谱。下面简要介绍一下这个服务:

 

在进行APNS远程推送时,如果由于用户卸载了app而导致推送失败,APNS服务器会记录下这个deviceToken,加入到一个列表中,可以通过查询这个列表,获取失效的推送令牌,从数据库中清除这些失效的令牌就可以避免下次推送时被加入到推送数组中来。连接这项服务很简单和推送工程类似,只不过地址不同,开发环境feedback.push.apple.com ,测试环境为feedback.sandbox.push.apple.com端口都是2196。APNS服务器返回的数据格式为:


 

Timestamp

A timestamp (as a four-byte time_t value) indicating when APNs determined that the app no longer exists on the device. This value, which is in network order, represents the seconds since 12:00 midnight on January 1, 1970 UTC.

Token length

The length of the device token as a two-byte integer value in network order.

Device token

The device token in binary format.

为了进行这项服务,我写了一个CI框架的控制器

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
defined('BASEPATH') OR exit('No direct script access allowed');
 
class Admin extends CI_Controller {
 
    public function __construct()
    {
        parent::__construct();
        // 加载数据库
        $this->load->database();
        $this->load->model('apns_model');
    }
 
    public function apnsfeedback()
    {
        $ctx = stream_context_create();
         $passphrase = 'xxxxx';
        stream_context_set_option($ctx, 'ssl', 'local_cert', 'xxxxxxx.pem');
        stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
 
        $fp = stream_socket_client('ssl://feedback.push.apple.com:2196', $error, $errorString, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
        if (!$fp) {
            echo "Failed to connect feedback server: $err $errstr\n";
            return;
        }
        else {
            echo "Connection to feedback server OK\n";
            echo "
"
;
        }
 
        while ($devcon = fread($fp, 38))
        {
            $arr = unpack("H*", $devc

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
C++ 高级模板编程与元编程
C++ 高级模板编程与元编程

本专题深入讲解 C++ 中的高级模板编程与元编程技术,涵盖模板特化、SFINAE、模板递归、类型萃取、编译时常量与计算、C++17 的折叠表达式与变长模板参数等。通过多个实际示例,帮助开发者掌握 如何利用 C++ 模板机制编写高效、可扩展的通用代码,并提升代码的灵活性与性能。

10

2026.01.23

php远程文件教程合集
php远程文件教程合集

本专题整合了php远程文件相关教程,阅读专题下面的文章了解更多详细内容。

29

2026.01.22

PHP后端开发相关内容汇总
PHP后端开发相关内容汇总

本专题整合了PHP后端开发相关内容,阅读专题下面的文章了解更多详细内容。

21

2026.01.22

php会话教程合集
php会话教程合集

本专题整合了php会话教程相关合集,阅读专题下面的文章了解更多详细内容。

21

2026.01.22

宝塔PHP8.4相关教程汇总
宝塔PHP8.4相关教程汇总

本专题整合了宝塔PHP8.4相关教程,阅读专题下面的文章了解更多详细内容。

13

2026.01.22

PHP特殊符号教程合集
PHP特殊符号教程合集

本专题整合了PHP特殊符号相关处理方法,阅读专题下面的文章了解更多详细内容。

11

2026.01.22

PHP探针相关教程合集
PHP探针相关教程合集

本专题整合了PHP探针相关教程,阅读专题下面的文章了解更多详细内容。

8

2026.01.22

菜鸟裹裹入口以及教程汇总
菜鸟裹裹入口以及教程汇总

本专题整合了菜鸟裹裹入口地址及教程分享,阅读专题下面的文章了解更多详细内容。

55

2026.01.22

Golang 性能分析与pprof调优实战
Golang 性能分析与pprof调优实战

本专题系统讲解 Golang 应用的性能分析与调优方法,重点覆盖 pprof 的使用方式,包括 CPU、内存、阻塞与 goroutine 分析,火焰图解读,常见性能瓶颈定位思路,以及在真实项目中进行针对性优化的实践技巧。通过案例讲解,帮助开发者掌握 用数据驱动的方式持续提升 Go 程序性能与稳定性。

9

2026.01.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP面向对象基础课程(更新中)
PHP面向对象基础课程(更新中)

共12课时 | 0.7万人学习

【李炎恢】ThinkPHP8.x 后端框架课程
【李炎恢】ThinkPHP8.x 后端框架课程

共50课时 | 4.5万人学习

Swoft2.x速学之http api篇课程
Swoft2.x速学之http api篇课程

共16课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号