views:

1667

answers:

6

Hi all,

Can someone clarify what the APNs (Apple Push Notification) wants as far as how you query it?

The docs say it starts sending as soon as the connection is made. Does this mean that I don't do an fread() on it?

Here's my current code to try and read it. I did NOT put the fread() in a loop as I do not know what response indicates "no more records to read" and I didn't want an infinite loop on my server.

<?php
$apnsCert = 'HOHRO-prod.pem';

$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);
stream_context_set_option($streamContext, 'ssl', 'verify_peer', false);

$apns = stream_socket_client('ssl://feedback.push.apple.com:2196', $error, $errorString, 60, STREAM_CLIENT_CONNECT, $streamContext);

echo 'error=' . $error;
echo 'errorString=' . $errorString;


$result = fread($apns, 38);
echo 'result=' . $result;


fclose($apns);
?>

So far all I am getting is a null reply. There are no errors so it is connecting.

I don't know if the null reply means no data is there, or my fread() is the wrong way to do it.

Thanks

A: 

Even I have a similar code and I too get null from fread. Not sure whether my code is really working and APNS is sending out a NULL reply or just something is missing. I'm sure my connection to APNS is successful. I had installed my application on some 5-10 devices and send couple of alerts and then uninstalled the application from few and started sending the alerts again so that APNS feedback server would let us know it failed to deliver on few devices. I haven't got anything other than blank in fread() :-(

If you have done something different and got it working please do let me know.

Thanks, -Anish

Anish
Have you used "pushutil"? Once you figure it out, it's a faster way to check the feedback server. It's a Mac OS X utility that you compile and then run from the Unix command line. Google Erica Sadun - it's on her site under Push.
Johnny
Still getting no response from the feedback server, even when I send out 76 push notifications - some of them HAD to be unsuccessful. Same thing happens when I use Erica Sadun's "pushutil" command-line utility - the push goes thru fine, then I remove the app and push again, and STILL nothing ever comes up on the feedback server. And this is with Erica's app so I know it must work.I think the problem must be the certificate. I am using the same push-production cert that I use to push. There's not a separate feedback cert, is there?
Johnny
A: 

Same problem for me also

Kamal Challa
A: 

I got the solution from apple forum and it is for development. Try this for production also.

"Well, as dumb as it sounds, I found a solution:

Create a dummy app id in the program portal, enable development push notifications on it Create and download the associated provisioning profile Create a new xcode project, and invoke the registerForRemoteNotificationTypes method on start. Install the dummy app on your device. At this point, you should have two DEVELOPMENT apps running on your device: the original app and the dummy app. Both should be registered to receive push notifications. Uninstall the original app, and try to send a push notification to that app. Invoke the feedback service, and you should receive data back."

dsk
+1  A: 

That code looks right however you need to loop and check for end of stream in order to read all the device codes.

 while (!feof($apns)) {
        $devcon = fread($apns, 38);
 }

However my problem is the actual unpacking of the data. Does anyone know how to unpack the binary data which you've just read to get the actual device ID (as string) along with the timestamp etc?

I think this is the right idea - $array = unpack("NnH32", $result);$feedbackTime = $row[0];$feedbackLen = $row[1];$feedbackUDID = $row[2];That unpacks the 38 bytes sent by the feedback server. However, the 32-bit date value is in network order, or big-endian. If someone can supply a PHP function that will flip this 4 bytes to Intel (little-endian) order, I think we have the solution. NOTE: the actual UDID is a character string and does NOT need to have its order flipped.
Johnny
This any good?---------/* Convert float from HostOrder to Network Order */function FToN( $val ){ $a = unpack("I",pack( "f",$val )); return pack("N",$a[1] );} /* Convert float from Network Order to HostOrder */function NToF($val ){ $a = unpack("N",$val); $b = unpack("f",pack( "I",$a[1])); return $b[1];}
A: 

This finally worked for me.

$arr = unpack("H*", $devconts); 
$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);

And then you simply check for the device token against the timestamp!

This works great, gw1921.I am storing the $feedbackDate in a SQL column for now. What type of data should the column be? I set it to Integer and that is giving me "2009". The other 2 columns, length and token, work GREAT!!Thanks
Johnny
+7  A: 

Here's a big gotcha which confused me when I first tried connecting: the APNS feedback servers only return the device tokens that have "expired" since your last feedback request. Which means most of the time you'll get a NULL response unless you're already dealing with a high volume of users of your app.

So make sure you store the expired device tokens to disk or db, because after your feedback query they're gone for good. This makes testing a pain to say the least!

Here's a complete function to fetch the device tokens from the APNS feedback servers (many thanks to the answers above for helping me put it all together):

function send_feedback_request() {
    //connect to the APNS feedback servers
    //make sure you're using the right dev/production server & cert combo!
    $stream_context = stream_context_create();
    stream_context_set_option($stream_context, 'ssl', 'local_cert', '/path/to/my/cert.pem');
    $apns = stream_socket_client('ssl://feedback.push.apple.com:2196', $errcode, $errstr, 60, STREAM_CLIENT_CONNECT, $stream_context);
    if(!$apns) {
        echo "ERROR $errcode: $errstr\n";
        return;
    }


    $feedback_tokens = array();
    //and read the data on the connection:
    while(!feof($apns)) {
        $data = fread($apns, 38);
        if(strlen($data)) {
            $feedback_tokens[] = unpack("N1timestamp/n1length/H*devtoken", $data);
        }
    }
    fclose($apns);
    return $feedback_tokens;
}

If all is well, the return values from this function will look something like this (via print_r()):

Array
(
    Array
    (
        [timestamp] => 1266604759
        [length] => 32
        [devtoken] => abc1234..............etcetc
    ),
    Array
    (
        [timestamp] => 1266604922
        [length] => 32
        [devtoken] => def56789..............etcetc
    ),
)
Nick Baicoianu
Thanks for this elegant solution.Now I just need to know how to compare the timestamp returned by the above feedback code to the timestamp that I have saved in my database, which of course represents the latest time that the device sent me a token.Do I just do a straight integer compare? Or do I have to convert the timestamps to correct endianness or something?
Johnny
This answer should be the accepted solution, IMO. This is basically what I'm doing, and it works perfectly. If you're still not seeing feedback data with this method, verify that you have another app on the device w/push enabled that reports to the same aps-environment as the uninstalled app. Johnny- regarding timestamp, once you unpack it as in Nick's post, you can do an integer compare.
brack