I need to send the same push notification to thousands of devices. I can get the tokens from DB.
Here is the example of PHP web service code.
public function send($tokens) {
if (is_array($tokens) && count($tokens) > 0) {
$body = array('aps' => array('alert' => $content, 'badge' => 0, 'sound' => 'received5.caf'));
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck5.pem');
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp) {
print "Failed to connect $token $err $errstrn";
return;
}
print "Connection OK/n";
$payload = json_encode($body);
foreach($tokens as $token) {
$msg = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $token)) . pack("n",strlen($payload)) . $payload;
print "sending message :" . $payload . "/n";
fwrite($fp, $msg);
}
fclose($fp);
}
}
So, first I get the connection to APNs calling:
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
Then I package the message in a foreach loop, send the notifications.
And finally, close the notifications.
fclose($fp);
But it just doesn't work, where am I doing wrong?
Give this a go... Ive added an extra chr(0), as per the API docs. The first chr(0) is the command. The next 2 bytes is the length of the token 0x0020.
$msg = chr(0) . chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $token)) . pack("n",strlen($payload)) . $payload;
Related
Right now I use loop to send bulk points, I want to send bulk push in ios, how can I send this without using loop.
Here is my code:
function sendToBulkIphone($artistID,$type="artist") {
$deviceToken = $this->deviceToken;
$message = $this->message;
$postid = $this->postid;
$notificationtype = $this->NotificationType;
$time = $this->time;
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'pushkey/artist_'.$artistID.'/ck_user_production.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', '1234'); STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
$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);
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
$body['title']="Notification";
$body['message']=$message;
$body['postid']=$postid;
$body['NotificationType']=$notificationtype;
$body['time']=$time;
for($n=0;$n<count($deviceToken); $n++) {
// Build the binary notification
$payload = json_encode($body);
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken[$n]). pack('n', strlen($payload)) . $payload;
$result = fwrite($fp, $msg, strlen($msg));//, strlen($msg)
if (!$result) {
$jsondata['message'] = 'Message not delivered';
$jsondata['status'] = 0;
}
else {
$jsondata['message'] = 'Message successfully delivered';
$jsondata['status'] = 1;
}
}
fclose($fp);
return true;
}
Can anyone please tell me how can I resolve this issue ?
I'm sad to tell you but you can't.
As the Apple API only takes one token by one, you're forced to send each notification inside a loop.
See : https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/APNsProviderAPI.html
Building from samplePush.php I have been able to build a transactional APNS script for my app. This works great and gives me the ability to one to one push notifications to a phone.
I need to move this to batch notifications and so built:
$APNSMessage = 'Test from APNS Bulk!';
// Bulk PUSH is the service designed to send iOS PUSH Notifications to hundreds of devices all at the same time using the same APNS connection.
function bulkPushToAPNS($TokenArray, $MessageToPush) {
echo "Sending to " . count($TokenArray) . " Devices ";
$body = array('aps' => array('alert' => $MessageToPush, 'badge' => 1, 'sound' => 'default'));
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'apnsCert.pem');
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp) {
print "Failed to connect $token $err $errstrn";
return;
}
print "Connection OK ";
$payload = json_encode($body);
foreach($TokenArray as $token) {
$msg = chr(0) . chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $token)) . pack("n",strlen($payload)) . $payload;
print $payload;
//fwrite($fp, $msg);
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
}
fclose($fp);
}
$TokensToPushToArray = array('ThisIsTokenOne', 'ThisIsTokenTwo');
bulkPushToAPNS($TokensToPushToArray, $APNSMessage);
When the script runs it gets passed an array of iOS tokens that need this message.
The output of the script is:
Sending to 2 Devices Connection OK {"aps":{"alert":"Test from APNS Bulk!","badge":1,"sound":"default"}}Message successfully delivered {"aps":{"alert":"Test from APNS Bulk!","badge":1,"sound":"default"}}Message successfully delivered
So the feedback from Apple is that these are getting delivered. But nothing is making it to the handhelds.
If I run each device token individually they work, so its something in the batch thats playing up..? Any ideas?
Answering my own question here;
Moved the code around so that the foreach is inside the connection:
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'apnsCert.pem');
// Open a connection to the APNS server
$fp = stream_socket_client(
'ssl://gateway.sandbox.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;
foreach ($APNSTokens as $APNSToken) {
// Create the payload body
$body['aps'] = array(
'alert' => $APNSMessage,
'sound' => 'default',
'badge' => 1
);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $APNSToken) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered' . PHP_EOL;
}
// Close the connection to the server
fclose($fp);
Now the connection is still live and runs until the foreach is complete.
Tested and working.
I need to send thousands (actually 10 000) push notifications at the same time, to do this, I checked StackOverflow and found those two questions:
Apple Push Notification: Sending high volumes of messages
Apple Push Notification: Sending high volumes of messages
And I implemented my push system using php:
// Create the payload body
$body['aps'] = [
'alert' => $notification->getMessage(),//$notification is just a notification model, where getMessage() returns the message I want to send as string
'sound' => 'default'
];
// Encode the payload as JSON
$payload = json_encode($body);
$fp = self::createSocket();
foreach($devices as $device) {
$token = $device->getToken();
$msg = chr(0) . pack('n', 32) . pack('H*', str_replace(' ', '', $token)) . pack('n', strlen($payload)) . $payload;
if(!self::sendSSLMessage($fp, $msg)){
//$msg is a parameter of the method, it's the message as string
fclose($fp);
$fp = self::createSocket();
}else{
//Here I log "n notification sent" every 100 notifications
}
}
fclose($fp);
Here is the createSocket() method:
private static function createSocket(){
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', PUSH_IOS_CERTIFICAT_LOCATION);
stream_context_set_option($ctx, 'ssl', 'passphrase', PUSH_IOS_PARAPHRASE);
// Open a connection to the APNS server
$fp = stream_socket_client(
'ssl://gateway.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
stream_set_blocking ($fp, 0);
if (!$fp) {
FileLog::log("Ios failed to connect: $err $errstr");
}
return $fp;
}
And the sendSSLMessage() method:
private static function sendSSLMessage($fp, $msg, $tries = 0){
if($tries >= 10){
return false;
}
return fwrite($fp, $msg, strlen($msg)) ? self::sendSSLMessage($fp, $msg, $tries++) : true;
}
I'm not getting any error message from apple, everything looks fine except nobody is receiving the notification.
Before this, I was using a method creating one socket connection per message and closing it after sending the message, but it was way too slow so we decided to change it, so I know that it's not a client related issue.
You have to first crate $fp socket then pass it to sendSSLMessage
// initally create Socket here
$fp = self::createSocket();
foreach($devices as $device) {
$token = $device->getToken();
$msg = chr(0) . pack('n', 32) . pack('H*', str_replace(' ', '', $token)) . pack('n', strlen($payload)) . $payload;
if(!self::sendSSLMessage($fp, $msg)){
fclose($fp);
$fp = self::createSocket();
}
}
fclose($fp);
UPDATE
// define in $body['aps'] message with sub array like this
$message = $notification->getMessage();
$body['aps'] = array(
'alert' => array(
'body' => $message,
),
'sound' => 'default',
);
I use php to send push message to apns , i use "Enhanced notification format" to sent .. but i can not get the return" Codes in error-response packet" anyone can help me ?? here is my code
<?php
header("Content-Type: text/html; charset=UTF-8");
$deviceToken = "123";
$content = "testing";
if(isset($content))
{
$newContent=substr($content,0,30)."...";
$re_content=iconv("GB2312","UTF-8",$newContent);
$pass = 'Ladder';
$body = array("aps" => array("alert" => $re_content, "badge" => 1, "sound" => 'received5.caf'));
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'dev.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);
//$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
stream_set_blocking ($fp, 0);
if (!$fp)
{
print "Failed to connect $err $errstrn";
return;
}
else
{
print "Connection OK\n<br/>";
}
$payload = json_encode($body);
$msg =
// new: Command "1"
chr(1)
// new: Identifier "1111"
. chr(1) . chr(1) . chr(1) . chr(1)
// new: Expiry "tomorrow"
. pack('N', time() + 86400)
// old
. chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) . chr(strlen($payload)) . $payload;
//print "sending message :" . $payload . "\n";
fwrite($fp, $msg);
//checkAppleErrorResponse($fp);
echo 'Done\n';
fclose($fp);
echo $apple_error_response = fread($fp, 6);
/* return false;
exit(); */
}
?>
I call this function after fwrite and before close commands
function error_response($fp)
{
$read = array($fp);
$null = null;
$changedStreams = stream_select($read, $null, $null, 0, 1000000);
if ($changedStreams === false)
{
echo ("Error: Unabled to wait for a stream availability");
}
elseif ($changedStreams > 0)
{
$responseBinary = fread($fp, 6);
if ($responseBinary !== false || strlen($responseBinary) == 6)
{
$response = unpack('Ccommand/Cstatus_code/Nidentifier', $responseBinary);
var_dump($response);
}
}
}
It works when I sending a notification in the enhanced format. But doesn't work with notification in the simple format. I don't have idea why... Any clue?
i use php to post to apple ..
$message = $error_msg;
$deviceToken = $dtoken;
$badge = 1;
$sound = 'received3.caf';
$body = array();
$body['aps'] = array('alert' => $message);
if ($badge)
$body['aps']['badge'] = $badge;
if ($sound)
$body['aps']['sound'] = $sound;
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '/home/administrator/applecert/apns-dev.pem');
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
error_reporting(E_ALL);
if (!$fp) {
print "Failed to connect $err $errstr\n";
return;
} else {
print "Connection OK\n";
}
$payload = json_encode($body);
$msg = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
print "sending message :" . $payload . "\n";
fwrite($fp, $msg);
fclose($fp);
and andriod have similar way to post with php?
thanks ,all
Take a look at the C2DM service google offers :
http://code.google.com/intl/fr-FR/android/c2dm/
This code is well tested.
Note : You need to remember 3 points to check.
Passphrase : confirm with IOS developer.
.PEM : Please verify with your IOS develoepr created '.PEM' file is for sandbox or live server.
PORT 2195 : Need to verify whether this port has opened or not on your server.
If you have done these 3 steps, Now you can send push notification with below code with few configuration changes.
function pushNotification($deviceToken, $msg, $sounds, $type) {
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '');
// Put your private key's passphrase here:
$passphrase = ;
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
$fp = stream_socket_client(
'ssl://gateway.sandbox.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
$body['aps'] = array(
'alert' => $msg,
'sound' => $sounds,
'badge' => 1,
'type' => $type,
);
$payload = json_encode($body);
// 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));
// print_r($result);
fclose($fp);
}