我正在为我们的 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 并不是我真正需要的。