0

我正在为我们的 iPhone 应用程序实现一个推送通知服务器。

我正在使用 Symfony Web 框架为我的 iPhone 应用程序构建后端系统。

我构建推送通知服务器的方式是:

1) 与 Apple 的 PNS 建立套接字流连接。

2) 开始一个无限的while循环

3) 在 while 循环中,查找 SQL 数据库中的任何新通知

4) 如果我的 SQL 数据库中有新的推送通知实体,将它们全部获取并发送出去

下面是我的PHP代码:

// ----------------------------------------------------------
// Opens a connection to Apple's Push Notification server
// ----------------------------------------------------------
public function pnsConnect()
{
    // push notification pem certificate file
    //$this->apnscert_dev = $this->container->getParameter('apnscert_dev');
    //$this->apnshost_dev = $this->container->getParameter('apnshost_dev');
    $this->apnscert_dev = 'cert_and_key_dev.pem';
    $this->apnshost_dev = 'gateway.sandbox.push.apple.com';
    $this->apnsport = 2195; //$this->container->getParameter('apnsport');

    $pempath_dev = __DIR__."/../Resources/config/".$this->apnscert_dev;

    echo 'pem path = '.$pempath_dev.'<br />';

    $streamContext = stream_context_create();

    stream_context_set_option($streamContext, 'ssl', 'local_cert', $pempath_dev);

    // push notification server connection object
    $this->apns = stream_socket_client('ssl://' . $this->apnshost_dev . ':' . $this->apnsport, $error, $errorString, 2, STREAM_CLIENT_CONNECT, $streamContext);
    error_log(date('Y-m-d H:i:s')." - Successfully connected to APNS", 3, 'PushLog.log'); // log for successful connection to help with debugging
}


// ---------------------------------------------------------
// Sends a push notification to multiple targeted tokens
// i.e. a group of users
//
// pnsSendToTokenGroup() only sends to a group, not a list
// of tokens specifically selected
// ---------------------------------------------------------    
public function pnsSendToMultiple($paramArrTokens, $paramMessage)
{
    if(!$paramMessage || !$paramArrTokens)
    {
        return new Response('Missing input parameters');
    }

    $badge = 1;
    $sound = 'default';
    $development = true;

    $payload = array();
    $payload['aps'] = array('alert' => $paramMessage, 'badge' => intval($badge), 'sound' => $sound);
    $payload = json_encode($payload);

    //echo 'message = '.$paramMessage.'<br />';

    //echo '<br />Received '.count($paramArrTokens).' tokens<br />';

    foreach($paramArrTokens as $paramToken)
    {
        //echo 'current token = '.$paramToken.'<br />';

        $apns_message = chr(0).chr(0).chr(32).pack('H*', str_replace(' ', '', $paramToken)).chr(0).chr(strlen($payload)).$payload;
        fwrite($this->apns, $apns_message);

        $apns_message = null;
        //$paramToken = null;
    }

    $badge = null;
    $sound = null;
    $development = null;
    $payload = null;

    //$paramArrTokens = null;
    //$paramMessage = null;
}


// ---------------------------------------------------------
// Keeps this PNS server alive to maintain the connection
// to Apple's PNS server. This process will continually
// check the database for any new notification and push
// the notificaiton to who ever we need to.
// ---------------------------------------------------------    
public function pnsKeepAlive()
{
    // prevents time out
    ini_set('max_input_time', 0);
    ini_set('max_execution_time', 0);

    $this->pnsDisconnect();
    $this->pnsConnect();

    // circular reference collector
    gc_enable();

    // start infinite loop to keep monitoring for new notifications
    while(true)
    {
        echo 'checking database for new notifications ...<br />';

        set_time_limit(0);

        gc_collect_cycles();

        $today = new DateTime('today');
        $now = new DateTime();

        $query = $this->em->createQuery('SELECT n FROM MyAppWebServiceBundle:Notification n WHERE n.sent = :paramSent and n.notificationdate >= :paramDate and n.notificationdate <= :paramNow')
            ->setParameter('paramSent', false)
            ->setParameter('paramDate', $today)
            ->setParameter('paramNow', $now);

        $notifications = $query->getResult();

        $today = null;
        $now = null;

        //echo 'number of unsent notifications found for today = '.count($notifications).'<br />';

        // for each notification, combine all tokens into a single array
        // to be looped through and sent into notification processing queue
        foreach($notifications as $notification)
        {
            $this->pushNotification($notification);
        }

        $this->em->detach($query);

        $notifications = null;
        $query = null;
    }

    $this->pnsDisconnect();
}




// ---------------------------------------------------------    
// Finds all tokens attached to Notification
// and construct an array of all unique tokens to be
// sent out to all receivers of the notification
// ---------------------------------------------------------    
public function pushNotification($notification)
{
    // initialise an empty array
    $arrAllTokens = array();

    // add all raw tokens to final array first
    foreach($notification->getTokens() as $token)
    {
        // only add active tokens
        if($token->getActive() == true)
        {
            $arrAllTokens[] = $token->getToken();
        }
    }

    // for each token group add all 
    // tokens in each group to final array
    foreach($notification->getTokenGroups() as $tokenGroup)
    {
        foreach($tokenGroup->getTokens() as $token)
        {
            // only add active tokens
            if($token->getActive() == true)
            {
                $arrAllTokens[] = $token->getToken();
            }
        }
    }

    $arrAllTokens = array_unique($arrAllTokens);

    $this->pnsSendToMultiple($arrAllTokens, $notification->getMessage());

    $notification->setSent(true);
    $this->em->flush();
    $this->em->detach($notification);

    $arrTokens = null;
    $arrAllTokens = null;
    $notification = null;
}

Apple 已声明 Push Provider 应保持与 Apple Push Server 的连接,而不是频繁断开和重新连接,否则他们会将其视为拒绝服务攻击并阻止我们。

这就是为什么我有一个无限的 while 循环,所以脚本不会结束以允许我保持与 Apple 的推送通知套接字流的持续连接。

目前,我的推送通知服务器在我的本地机器上工作(我的 pnsKeepAlive() 无限循环永远持续)但是当我将我的代码部署到生产服务器时,我的 pnsKeepAlive()(见上面的代码)不会永远持续下去.

我的生产服务器是 Linode 上的共享主机。它是一个运行 Apache 和 Debian 的 LAMP 服务器。

我听说 PHP 不是为做这些工作而设计的。

所以我的问题是是否有其他语言是为这类事情设计的(维护推送通知服务器的持久连接)。

我调查过 Gearman,但也有人告诉我 Gearman 并不是我真正需要的。

4

1 回答 1

2

我有一些关于推送通知服务器的 Node JS + socket.io 的好故事。

这是一篇很棒的文章,可能会对您有所帮助:http: //www.gianlucaguarini.com/blog/nodejs-and-a-simple-push-notification-server/

于 2012-11-16T02:48:35.157 回答