>php教程 >php手册 >Codeigniter 프레임워크 기반 APNS 일괄 푸시—Ding Dong, 수량계 확인

Codeigniter 프레임워크 기반 APNS 일괄 푸시—Ding Dong, 수량계 확인

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB원래의
2016-08-18 08:57:581463검색

최근 저희 아르바이트 회사에서 크라우드 펀딩에 성공한 무선 초인종의 메시지 푸시에 문제가 발생하여 일부 사용자가 푸시 메시지를 수신할 수 없는 상황이 발생했습니다. 제가 직접 제공한 백엔드 서비스가 회사의 평판에 영향을 미치다니 참으로 당혹스럽습니다. 우리의 요구 사항을 간단히 소개하자면, 회사에서는 누군가가 문 밖에서 초인종 스위치를 누르면 신호가 울리는 시스템을 개발하고 있습니다. 집에 있는 수신 게이트웨이는 소리가 나면 사용자의 휴대폰으로 메시지를 보냅니다. 즉, 휴대폰이 원격인 경우에도 소유자는 누군가가 버튼을 눌렀다는 것을 알 수 있습니다. 그가 집에 없을 때 집에 있는 초인종. 여기서 백그라운드에서 해결해야 할 문제는 APNS 푸시를 위한 공급자를 구축하는 것입니다. 왜냐하면 Apple 휴대폰에 메시지를 푸시하려면 Apple이 설계한 메커니즘에 따라 자체 메시지를 통해 Apple의 PUSH 서버로 푸시되어야 하기 때문입니다. 각 휴대폰은 deviceToken에 해당합니다. 여기서 소개하는 내용의 초점은 이 플랫폼을 구축하는 방법이 아닙니다. 국내 인터넷에는 이미 많은 튜토리얼이 있습니다. 예를 들어 다음을 참고하세요: iOS 푸시 방법을 단계별로 가르쳐주세요

""

대부분의 온라인 튜토리얼은 휴대폰에서 작동합니다. 즉, 우리 회사에서 설계한 제품에서는 동일한 계정으로 메시지를 한 번에 푸시한다는 것입니다. 여러 대의 휴대폰에서 로그인이 가능합니다.(이론적으로는 백그라운드에 제한이 없기 때문에 셀 수 없을 정도입니다.) 또한 각 휴대폰에 해당하는 deviceToken도 다릅니다. , 기본 사용자는 기기를 다른 사용자와 공유할 수 있으며, 다른 사용자는 다른 기기에서 동시에 로그인할 수 있습니다. 누군가 초인종을 누르면 공유 사용자를 포함하여 로그인한 모든 사용자에게 메시지가 푸시되어야 합니다. 이는 다수의 휴대전화 단말에 일괄적으로 푸시되어야 한다는 의미이다. 물론 여기서 제시한 예는 그렇게 복잡하지 않습니다. 모든 문제는 하나의 질문으로 요약됩니다. deviceToken을 저장할 배열을 제공하세요. APNS는 어떻게 여러 사용자에게 일괄 푸시하나요?

먼저 사용자의 푸시 토큰(deviceToken)을 저장할 데이터베이스를 설계합니다. 이 테이블에는 두 개의 필드만 있습니다.

client_id deviceToken
1 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

여기서는 CodeIgniter3 프레임워크를 사용하여 사용자 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
<?php
// 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);
 
       <code class="php variable">$num=count($deviceTokens);
       $countOK=0;//统计发送成功的条数
 
       for($i=0;$i<code class="php variable">$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==<code class="php variable">$num)
           return TRUE;
       else
           return FALSE;
 
   }

나중에 일련의 푸시 문제를 일으킨 것은 바로 위 코드였습니다.

첫 번째 큰 문제는 여기서 모든 푸시 토큰은 기본적으로 유효하지만 실제로는 사용자가 앱을 직접 삭제하거나 앱을 업그레이드하면 백엔드 데이터베이스의 deviceToken이 업데이트되지 않아 푸시 토큰이 무효화됩니다. 그러나 누군가가 초인종을 누르면 백그라운드에서는 이를 유효한 deviceToken으로 처리하고 $deviceTokens에 포함합니다. 만료된 deviceToken을 삭제하는 방법은 고려해야 할 문제입니다.

관련 정보를 찾아보니 APNS 서비스에는 피드백 서비스 서비스가 있다는 걸 알게 됐습니다. 일부 정보에서는 Google에서 공식 웹사이트를 찾는 것이 더 신뢰할 수 있다고 언급했습니다. 이 서비스에 대한 간략한 소개는 다음과 같습니다.

APNS 원격 푸시를 수행할 때 사용자가 앱을 제거하여 푸시가 실패하면 APNS 서버는 deviceToken을 기록하고 이를 목록에 추가하여 만료된 푸시 토큰을 얻을 수 있습니다. , 데이터베이스에서 이러한 잘못된 토큰을 지우면 다음 푸시 중에 푸시 배열에 추가되는 것을 방지할 수 있습니다. 이 서비스에 연결하는 방법은 매우 간단하며 푸시 프로젝트와 유사하지만 개발 환경 <code style="font-size: 13.39px; font-family: courier, consolas, monospace; color: #666666; font-variant-ligatures: normal; line-height: normal; orphans: 2; widows: 2; background-color: #ffffff;"><span style="font-family: 宋体, SimSun; font-size: 18px;">feedback.push.apple.com</span>feedback.push.apple .com, 테스트 환경은 feedback.sandbox.push.apple.com 포트는 모두 2196입니다. APNS 서버에서 반환되는 데이터 형식은 다음과 같습니다.

""

타임스탬프

APN이 해당 앱이 기기에 더 이상 존재하지 않는다고 판단한 시점을 나타내는 타임스탬프(4바이트 time_t 값)입니다. 네트워크 순서에 따른 이 값은 1970년 1월 1일 자정(UTC) 12시 이후의 초를 나타냅니다.

토큰 길이

네트워크 순서에 따라 2바이트 정수 값으로 표시되는 기기 토큰의 길이입니다.

기기 토큰

바이너리 형식의 기기 토큰

이 서비스를 수행하기 위해 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
<?php
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 $errstrn";
            return;
        }
        else {
            echo "Connection to feedback server OKn";
            echo "<br>";
        }
 
        while ($devcon = fread($fp, 38))
        {
            $arr = unpack("H*", $devc
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.