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?
Related
Here is my NodeJS code , which is working totally fine.
var options = {
//production
"cert": readCredentialsFile(cert_file),
"key": readCredentialsFile(cert_key_file),
"production" : production_val,
"passphrase": "mypassphrase",
"gateway": gateway_val,
"port": 2195,
"enhanced": true,
"cacheLength": 5,
"retryLimit":-1
};
options.errorCallback = apnError;
var feedBackOptions = {
"cert": readCredentialsFile(cert_file),
"key": readCredentialsFile(cert_key_file),
"passphrase": "mypassphrase",
"production" : production_val,
"batchFeedback": true,
"interval": 300
};
var apnConnection, feedback;
module.exports = {
initAPN : function(){
try {
apnConnection = new apn.Connection(options);
feedback = new apn.Feedback(feedBackOptions);
feedback.on("feedback", function (devices) {
devices.forEach(function (item) {
//TODO Do something with item.device and item.time;
});
});
} catch (error) {
console.log(error);
}
},
sendIOSNotification : function (device_token,message,params){
var myDevice, note;
try{
console.log("iOS device_token ", device_token);
var randomID = require("random-id");
var payloadMultiCast = {
"data": {
"id": randomID(),
"status": params.notification_type,
"title": message,
"body": params,
"message": message
}
};
myDevice = new apn.Device(device_token);
note = new apn.Notification();
note.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires 1 hour from now.
note.badge = 1;
note.sound = "ping.aiff";
note.alert = message;
note.payload = payloadMultiCast;
console.log("iOS Notification ", note);
if(apnConnection) {
apnConnection.pushNotification(note, myDevice);
console.log("iOS res "+ device_token+":::"+JSON.stringify(apnConnection));
}else{
console.log("NO APNS CONNECTION");
}
}catch(err){
console.log("error:-"+err);
}
}
}
And below is my php code , which is not working with same "pem" file and same credentials:-
$streamContext = stream_context_create();
stream_context_set_option($streamContext, $options);
$apns = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $error, $errorString, 60000, STREAM_CLIENT_CONNECT, $streamContext);
if (!$apns)
$errrrr = json_encode(error_get_last());
echo("Failed to connect 11amarnew: $err $errstr $errrrr" . PHP_EOL);
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $dev)) . chr(0) . chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);
fclose($apns);
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $file);
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
$err = ""; $errstr = "";
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195',
$err,
$errstr,
600000,
STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT,
$ctx);
if (!$fp)
$errrrr = json_encode(error_get_last());
exit("Failed to connect amarnew: $err $errstr $errrrr" . PHP_EOL);
//echo 'Connected to APNS' . PHP_EOL;
// Create the payload body
$message = "Hello Moto!!";
$body['aps'] = array(
'badge' => +1,
'alert' => $message,
'sound' => 'default'
);
$payload = json_encode($body);
$deviceToken = "ed3d13a56a92027acbb9805bfa5f9a3d6da4fdcf24a57b375bfb638259e9d31c";
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $dev) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
//echo "<pre>"; print_r($result); die();
if (!$result)
echo 'Message not delivered' . PHP_EOL;
else
echo 'Message successfully delivered amar'.$message. PHP_EOL;
// Close the connection to the server
fclose($fp);
The error i am getting is -
Failed to connect amarnew: 0 {"type":2,"message":"stream_socket_client(): unable to connect to ssl:\/\/gateway.sandbox.push.apple.com:2195 (Unknown error)","file":"\/var\/www\/html\/push.php","line":42}
FYI , I have allowed 2195 , 2196 ports also on my sever for PHP , NodeJS doesn't need port to be opened.
Then what is issue that causing not working in PHP and working in NodeJS, finally i have to use the php only hence i need to solve the issue.
Thanks,
I have set up a php file on my own server and used it to send push notifications. I have already tried it with two devices and it works well. The problem is with the feedback method which returns nothing. I have already tried everything and disabled firewall and so on. Note that i tested the feedback like this: I run the app on both devices. They both get the notification. then i delete the app on one of them and try again. Here is the PHP code i used:
<?php
$deviceToken1 = '.....';
$deviceToken2 = '.....';
$tokenArr = array($deviceToken1,$deviceToken2);
$passphrase = '.....';
$message = $_POST['message'];
$newsID = $_POST['newsID'];
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// 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;
for($i = 0; $i < count($tokenArr); $i++)
{
$body['aps'] = array(
'alert' => $message,
'badge' => 1,
'sound' => 'sound.aiff',
'newsID' => $newsID);
// Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $tokenArr[$i]) . pack('n', strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
if (!$result) {
echo '<br/>'.date("Y-m-d H:i:s").' Message not delivered' . PHP_EOL;
}
else {
echo '<br/>'.date("Y-m-d H:i:s").' Message successfully delivered' . PHP_EOL;
}
}
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
$apns = stream_socket_client('ssl://feedback.sandbox.push.apple.com:2196', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
echo $apns;
if(!$apns)
{
echo '<br/>';
echo "Error $errcode : $errstr\n";
return;
}
else {
echo "APNS feedback results\n";
while ($devcon = fread($fp, 38))
{
$arr = unpack("H*", $devcon);
$rawhex = trim(implode("", $arr));
$feedbackTime = hexdec(substr($rawhex, 0, 8));
$feedbackDate = date('Y-m-d H:i', $feedbackTime);
$feedbackLen = hexdec(substr($rawhex, 8, 4));
$feedbackDeviceToken = substr($rawhex, 12, 64);
echo "TIMESTAMP:" . $feedbackDate . "\n";
echo "DEVICE ID:" . $feedbackDeviceToken. "\n\n";
}
}
?>
Not sure if it's the same issue as iPhone SDK Push notification randomly fails . But some notifications do arrive, I do have does "unpack" warnings when upacking command 8 packets.
I have a php file that pulls any notifications from a table (DB) which has token, payload, and device (iphone,ipad). This php runs every 15 seconds on the server cron. I don't know why 20% of the notifications arrive sometimes.... sometimes 80% arrive.... and sometime none arrive (I have 10 devices for testing). I read that apples notifications are "best effort" and that there are not guaranteed to arrive but this is ridiculous, something must be wrong with the logic of my code. Here is my code, I have changed names of directories, DB info and files for privacy reasons. Please help.
<?php
include("/#####/DB.conf");
$link = mysql_connect($host,$userDB,$passDB) or die("Failed to connect: " . mysql_error());
mysql_select_db('#######') or die("Failed to connect: " . mysql_error());
mysql_query("SET NAMES 'utf8'");
$certFile_iphone = '/##########/push_production.pem';
$certFile_ipad = '/########/push_production.pem';
$passphrase = '#########';
$sent += sendPush($certFile_iphone,$passphrase,'iphone');
$borrados += checkFeedback($certFile_iphone,$passphrase,'iphone');
$sent += sendPush($certFile_ipad,$passphrase,'ipad');
$borrados += checkFeedback($certFile_ipad,$passphrase,'ipad');
//////////////////////////////
//Send notification to users//
//////////////////////////////
function sendPush($cert,$pass,$device)
{
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $cert);
stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);
// Open a connection to the APNS server
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 600, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp)
exit("Failed to connect: $err $errstr" . PHP_EOL);
//gets all the devices of that type and their current notification to send
$query = mysql_query("SELECT * FROM pushNotification_buffer WHERE device = '".$device."' LIMIT 1000;");
$errors = 0;
$sent = 0;
$i = 0;
while($row = mysql_fetch_array($query))
{
$payload = $row['payload'];
$deviceToken = $row['identificador'];
//$msg = chr(0) . pack('n', 32) . pack('H*', $row['identificador']) . pack('n', strlen($payload)) . $payload;
$msgInner =
chr(1)
. pack('n', 32)
. pack('H*', $deviceToken)
. chr(2)
. pack('n', strlen($payload))
. $payload
. chr(3)
. pack('n', 4)
. chr($i)
. chr(4)
. pack('n', 4)
. pack('N', time() + 86400)
. chr(5)
. pack('n', 1)
. chr(10)
;
$msg=
chr(2)
. pack('N', strlen($msgInner))
. $msgInner
;
$i++;
$result = fwrite($fp, $msg, strlen($msg));
if (!$result)
{
$errors++;
usleep(1000000);
$result = fwrite($fp, $msg, strlen($msg));
error_log("Error on Device (2): " . $row['identificador']);
}
if (!$result)
{
$errors++;
fclose($fp);
return $sent;
}
else
{
$sent++;
mysql_query("DELETE FROM pushNotification_buffer WHERE identificador = '".$row['identificador']."' AND payload = '".mysql_real_escape_string($row['payload'])."' LIMIT 1;");
}
error_response($fp);
}
fclose($fp);
return $sent;
}
/////////////////
//APNS FeedBack//
/////////////////
function checkFeedback($cert,$pass,$device){
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $cert);
stream_context_set_option($ctx, 'ssl', 'verify_peer', false);
stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);
$fp = stream_socket_client('ssl://feedback.push.apple.com:2196', $error, $errorString, 600, STREAM_CLIENT_CONNECT, $ctx);
$toDelete = 0;
if(!$fp) echo ("NOTICE: Failed to connect to device: $error - $errorString.");
while ($devcon = fread($fp, 38)){
$arr = unpack("H*", $devcon);
$rawhex = trim(implode("", $arr));
$token = substr($rawhex, 12, 64);
if(!empty($token))
{
if($device == 'iphone'){
mysql_query("DELETE FROM iphoneDevicesAPNS WHERE identificador = '" . $token . "' LIMIT 1;");
}else{
mysql_query("DELETE FROM ipadDevicesAPNS WHERE identificador = '" . $token . "' LIMIT 1;");
}
$toDelete++;
}
}
fclose($fp);
return $toDelete;
}
/////////////////////////////
//Error response, command 8//
/////////////////////////////
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);
if (strlen($response["command"] > 0) || strlen($response["status_code"] > 0) || strlen($response["identifier"] > 0)){
$texto=
"------------------------------------------". "\n".date("Y-m-d G:i:s"). "\n";
"id: " . $response["identifier"] . "\n" .
"code: ". $response["status_code"] . "\n" .
"command: ". $response["command"] . "\n". "\n"
;
file_put_contents ( "/#####/logAPNS.log" ,$texto, FILE_APPEND);
}
}
}
}
?>
I do have does "unpack" warnings when upacking command 8 packets
This implies that you are getting error responses from Apple, but from what I see in the code, you don't do anything with these errors beside logging them. Each such error response means that you sent invalid data to Apple, and as a result Apple closed the connection. This means any message sent after the invalid one will be discarded until you open a new connection.
The proper way to handle such responses is to parse the bad message ID contained in them, open a new connection to APNS, and re-send all the messages that you sent after the bad message.
The most likely error is invalid device token. That usually happen when you send a development token to production push environment or vice versa. If you clear the invalid device tokens from your DB, you are likely to see 100% of the messages arriving.
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;
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);
}