【问题标题】:What language to use for Push Notification Server maintaining socket stream connection to Apple Push Notification ServerPush Notification Server 使用什么语言来维护与 Apple Push Notification Server 的套接字流连接
【发布时间】:2012-11-16 02:24:37
【问题描述】:

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

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

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

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

2) 开始无限循环

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() 无限 while 循环永远持续)但是当我将我的代码部署到生产服务器时,我的 pnsKeepAlive()(见上面的代码)没有永远继续下去。

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

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

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

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

【问题讨论】:

    标签: php ios apache symfony push-notification


    【解决方案1】:

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

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

    【讨论】:

    • 感谢您分享链接 y_s。我听到的建议之一也是 NodeJS。我只是想知道它是否可以保持连接,但我找到了这个链接:bravenewmethod.wordpress.com/2010/12/09/…,那个家伙提到保持连接活跃。可能会尝试他的实现,看看它是否有效。
    • 在我自己的测试中,NodeJS 默认保持连接处于活动状态,直到您终止它们。
    • 哇。我对 Node.js 持怀疑态度,它听起来像一个 jQuery 库,一个 jQuery 库怎么能......做服务器的东西,更不用说推送通知了。我已经设法发送测试通知。我认为它看起来非常好,我的 node.js 服务器一直在我的 shell 中运行,直到我告诉它停止。我还没有实际测试过,但看起来这是要走的路:)我注意到node.js在我测试时没有发送我在for循环中的所有10个通知,它最多只发送3个,可能是我编码不好?
    • 我想我也会这么说。实际上,我一口气看完了这本很短的书(为了支持作者而购买了它,他写得很好:D):nodebeginner.org,然后更容易按照本教程进行推送通知:bravenewmethod.wordpress.com/2010/12/09/…我想从这里开始,我只是改进它并学习将它与管理通​​知、令牌和令牌组实体的 Symfony 管理系统集成的缺失部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-25
    • 2013-01-14
    相关资源
    最近更新 更多