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 は、プッシュ プロバイダーは Apple のプッシュ サーバーへの接続を維持する必要があり、頻繁に切断して再接続しないようにする必要があると述べています。
そのため、スクリプトが終了しないように無限の while ループを使用して、Apple のプッシュ通知ソケット ストリームへの常時接続を維持できるようにしています。
現時点では、プッシュ通知サーバーはローカル マシンで動作します (ループが永遠に続く間、pnsKeepAlive() 無限) が、コードを運用サーバーにデプロイすると、pnsKeepAlive() (上記のコードを参照) は永遠に続きません。 .
私のプロダクション サーバーは、Linode の共有ホスティングです。Apache と Debian を実行する LAMP サーバーです。
PHP は、この種の仕事をするように設計されていないと聞いたことがあります。
ですから、この種のこと (プッシュ通知サーバーの永続的な接続を維持すること) のために設計された他の言語があるかどうか、私の質問です。
私は Gearman を調べましたが、Gearman は私が必要としているものではないと他の人からも言われました。