for the past 3 days i have been trying to make things work with my php code and apns.
So far i'm able to deliver notifications (i only have one device so i don't know if this is working for multiple devices).
When i try to test sending one notification for 2 devices where the first is an device token invalid (invented by me) and the second is my device token, im unable to deliver the notification to my device... From what i read, when a notification is unreadable or erroneous, apns shuts the connection down and sends an error of 6 bytes long, where the 2nd byte (status_code) is the error type. If no error happens, im still unable to understand if apns send the status_code 0 (No errors encountered) or if it does not send anything at all.
So far i wasn't able to ever find this status_code, despite of following several codes found on internet.
CODE
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '.\certif\ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
// Create the payload body
//file_put_contents('logs/debug_not.log', 'body'. "\n", FILE_APPEND);
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
// Encode the payload as JSON
//file_put_contents('logs/debug_not.log', 'json body'. "\n", FILE_APPEND);
$payload = json_encode($body);
$open = true;
foreach ($devices as $device) {
//file_put_contents('logs/debug_not.log', 'inicio ciclo'. "\n", FILE_APPEND);
if($open == true){
//file_put_contents('logs/debug_not.log', 'abrir ligacao'. "\n", FILE_APPEND);
// 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){
//file_put_contents('logs/debug_not.log', 'erro abrir ligacao'. "\n", FILE_APPEND);
throw new \Exception;
}
}
// Build the binary notification
//file_put_contents('logs/debug_not.log', 'criar payload'. "\n", FILE_APPEND);
$msg = chr(0) . pack('n', 32) . pack('H*', str_replace(' ', '', $device['token'])) . pack('n', strlen($payload)) . $payload;
// Send it to the server
//file_put_contents('logs/debug_not.log', 'enviar payload'. "\n", FILE_APPEND);
$result = fwrite($fp, $msg, strlen($msg));
stream_set_blocking ($fp, 0);
$errorResponse = #fread($apns, 6);
//if i var_dump($errorResponse) it gives me null/false all the time, no matter what
//this is my workaround for invalid device tokens-- its not working or at least is not send the valid tokens
if(!$result || !$fp)
{
//file_put_contents('logs/debug_not.log', 'erro na not '. chr($data["status_code"]). "\n", FILE_APPEND);
fclose($fp);
$open = true;
} else{
//file_put_contents('logs/debug_not.log', 'suc na not'. "\n", FILE_APPEND);
$open = false;
}
//file_put_contents('logs/debug_not.log', 'fim ciclo'. "\n", FILE_APPEND);
}
// Close the connection to the server
if($fp){
//file_put_contents('logs/debug_not.log', 'fechar connection'. "\n", FILE_APPEND);
fclose($fp);
}
fread on my connection gives null/false all the time, even when i receive my notification or i'm trying with a wrong token.
My logic for connect after error, seems to be not working.
can please someone help me??? i dont intend to use 3rd classes or code, would like to have a simple basic one working for an array of device tokens, even if there are some that apns gives error.
Thanks everyone.
You have made your connection as $fp = stream_socket_client ......
and you are trying to $errorResponse = #fread($apns, 6); which will never give you any output as $apns is not defined.
Rather you should do this :
$errorResponse = #fread($fp, 6);
and your code will work fine
If you receive an error (most likely in your case because your invented device token does not exist on Apple's servers for your app), then an error message is returned just before the connection is closed. If the message is successful, there is no response, but the connection remains open.
Here's how you could handle this:
Send your message.
Read the error with a timeout (or in non-blocking mode). If you get an error response, the message failed. (You'll need a new connection.)
If the read times out (or returns nothing in non-blocking mode), the message may be successful. (Do nothing.)
Send the next message. If the send is successful (still connected), the last message was definitely successful.
Keep track of your last successful message. You could always follow up a series of messages by sending your own device a message just to prevent any false positives.
You might want to check out the source code for EasyAPNS, which is an open source PHP implementation of this. Also, you can read the details in Apple's documentation: Provider Communication with Apple Push Notification Service
Related
I am having issue with web socket connection/handshake from Firefox to my server written in Php.
Problem is when I try to connect client(using js/php or even websocket.org/echo.html) I get issue and the issue is client is firstly connected and then disconnected automatically. And at server, I am using fwrite to to send message to client. And i have also noticed that for a client from firefox fwrite returns 0 as a response, but at client side I can see server has posted handshake data.
Now I see problem is here, when server is writing but response is 0 and still browser(firefox) is getting response but it gets disconnected with the server at the same time. I even tried to compare(calculate) data in bytes, which server is posting to client. I see its exactly same.
Note:-
Just to mention here, same php server script is working fine for Opera/Chrome/Safari browsers.
Below are the things which are involved in this issue;
Firefox [71.0]
Php [7.0]
Apache
Let's Encrypt SSL
# STREAM SOCKET CREATION OVER SSL(WSS)
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'local_cert', 'ssl-test.pem'); #change to your own ssl.pem path
stream_context_set_option($context, 'ssl', 'allow_self_signed', true);
stream_context_set_option($context, 'ssl', 'verify_peer', false);
$sock = stream_socket_server("ssl://".HOST_NAME.":".PORT,$errno,$errstr,STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context) or die('Unable to start server, seems server is already running!');
// stream_set_blocking($sock,TRUE);
# PUTTING ALL(CURRENT/UPCOMING) SOCKET CONNECTIONS IN ARRAY
$clients = array();
# INFINTE LOOP THOURGH ALL SOCKECT CURRENT/UPCOMING CONNECTIONS, SEND/RECEIVE DATA
while (true) {
# ALL ACTIVE SOCKET CONNECTIONS
$read = $clients;
foreach($received_header as $key => $header){
if($header['time']+1 < microtime(true)){
$headers = [];
// echo $header['data'];
$lines = preg_split("/\r\n/", $header['data']);
foreach($lines as $line) {
$line = chop($line);
if(preg_match('/\A(\S+): (.*)\z/', $line, $matches)){
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
// create handshake header
$upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake" .PHP_EOL.
"Upgrade: websocket" .PHP_EOL.
"Connection: Upgrade" .PHP_EOL.
"Accept: text/html" .PHP_EOL.
"Sec-WebSocket-Accept: $secAccept".PHP_EOL.PHP_EOL;
// send handshake packet
if(!fwrite($clients[$key], $upgrade)){
file_put_contents('handshakes-est.txt', ($upgrade));
}
$handshakes[$key] = true;
$unset_key = $key;
}
}
/* i have rest of the script here which do further communication after connection is setup */
}
Below are some screen-shots to better understand connection behavior issue on Firefox.
Headers during handshake:
Connection behavior over websocket:
I want to send XML requests via EPP protocole to a registrar and get responses, the connection is successful, but when I get to fread($fp) , it takes forever to load.
Is there a way to make it fast and get a response from the registrar?
I am using the code bellow in a hostbill plugin.
/** open socket* */
$fp = fsockopen("tcp://registrarwebsite.com", 700, $errno, $errstr, 200);
stream_set_blocking($fp, true);
stream_context_set_option($fp, 'ssl', 'verify_host', true);
stream_context_set_option($fp, 'ssl', 'verify_peer', true);
stream_context_set_option($fp, 'ssl', 'allow_self_signed', false);
stream_context_set_option($fp, 'ssl', 'local_cert', __DIR__ . '/ma_cert.pem');
stream_context_set_option($fp, 'ssl', 'local_pk', __DIR__ . '/ma_key.pem');
// $secure = stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
// stream_set_blocking($fp, false);
if (!$fp) {
$this->addError('Il y a une erreur dans la connexion: ' . $errno . ' ' . $errstr);
return false;
} else {
$xml = htmlentities($this->prepareXMLRequest($xml));
fwrite($fp, $xml);
$out = fread($fp, 1024);
fclose($fp);
$out1 = htmlentities($fp);
$this->addError('<span style="color: green !important">Connexion se fait avec succes, le code retourné est : </span> ' . $out1);
Your EPP implementation is wrong (if the server supports the standard up to the letter of course), see RFC5734 which I quote:
Data Unit Format
The EPP data unit contains two fields: a 32-bit header that describes
the total length of the data unit, and the EPP XML instance. The
length of the EPP XML instance is determined by subtracting four
octets from the total length of the data unit. A receiver must
successfully read that many octets to retrieve the complete EPP XML
instance before processing the EPP message.
Note also section 3 that shows when you open the TCP/TLS connection the first party to speak is the server with the <greeting> so as the client you first need to read that and then send your login.
I need help implementing push notifications in swift. I followed the ray wenderlich tutorial http://www.raywenderlich.com/32960/apple-push-notification-services-in-ios-6-tutorial-part-1 but it does not mention how you actually call or run the php push notification script in Xcode. This is how I am attempting to call the script right now:
let request = NSMutableURLRequest(URL: NSURL(string: "http://website.com/pushNotification")!)
request.HTTPMethod = "POST"
let dataDictionary:[String:String] = ["NotificationData":"\(deviceTokenString)<*&*>password<*&*>my first push notification"]
let data:NSData = try! NSJSONSerialization.dataWithJSONObject(dataDictionary, options: [])
request.HTTPBody = data
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
// Create a NSURLSession task with completion handler
let task:NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
// Convert the data into a dictionary
let response:[String:String] = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! [String:String]
// Check if there was an error
if let result = response["result"] {
if result == "success"{
NSLog("Message deleviered successfully")
}
else if result == "error"{
NSLog("Message could not be deleviered")
}
}
})
// Run the task
task.resume()
Heres the php script it hits:
<?php
// Get the data from the request
$json = file_get_contents('php://input');
$data = json_decode($json, true);
$pushData = $data['NotificationData'];
// Format data
$keywords = explode("<*&*>", $pushData);
// Assign data into variables
$deviceToken = $keywords[0];
$passphrase = $keywords[1];
$message = $keywords[2];
////////////////////////////////////////////////////////////////////////////////
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'signingcertificate.p12');
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);
}
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
// Encode the payload as JSON
$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));
if (!$result) {
echo '{"result" : "error"}';
}
else {
echo '{"result" : "success"}';
}
// Close the connection to the server
fclose($fp);
?>
But then Xcode gives me this error:
fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}: file /Library/Caches/com.apple.xbs/Sources/swiftlang_PONDEROSA/swiftlang_PONDEROSA-700.1.101.6/src/swift/stdlib/public/core/ErrorType.swift, line 50
Im almost positive the error has something to do with the way I call for the php script but I don't know how this type of php script should be called. Please help! Any suggestions or insight you have will be appreciated!
The link does tell you what to do with the PHP script. You aren't suppose to call that through Xcode.
As the tutorial states:
As I’ve mentioned a few times before, you need to set up a server that sends the push notifications to your app. For this first test, you’re not going to set up a server just yet. Instead, I’ll give you a very simple PHP script that sets up a connection to APNS and sends a push notification to a device token that you specify. You can run this straight from your Mac.
...
You should copy the device token from the app into the $deviceToken variable. Be sure to leave out the spaces and brackets; it should just be 64 hexadecimal characters. Put your private key’s passphrase into $passphrase, and the text you wish to send in $message.
Copy your ck.pem file into the SimplePush folder. Remember, the ck.pem file contains both your certificate and the private key.
Then open a Terminal and type: .....
The PHP is a simple example of what your server would do. You need to build the server as well that will invoke a call to apple's APNS when a certain event occurs. The mobile app itself doesn't invoke a push notification.
I'm running push notification form server to my iphone.
... ... // Encode the payload as JSON
$payload = json_encode($body);
// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n',
strlen($payload)) . $payload;
$result = fwrite($fp, $msg, strlen($msg));
when I echo $result. I shown "102".
What does this mean?
1xx Informational
**102 Processing (WebDAV; RFC 2518)**
As a WebDAV request may contain many sub-requests involving file operations, it may take a long time to complete the request. This code indicates that the server has received and is processing the request, but no response is available yet.[2] This prevents the client from timing out and assuming the request was lost. Source of this Wikipedia List of HTTP status codes
I want to run following code in Background at certain occurrence:
File A :
// ------------------- Some Code Before Following -----------------------
// send notification to given device token
$notification = new Notification();
$notification->sendNotification($token,$count);
// ------------------- Some Code After Above ------------------------
This will called following class :
// Here I am sending notification to given device token (APNS Push Notification)
<?php
class Notification
{
public function sendNotification($token,$count)
{
if(strlen($token)%16 == 0)
{
// set Device token
$deviceToken = $token;
$passphrase = 'xxxxx';
$badge = $count;
// Displays alert message here:
$message = 'Hello! There.';
$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.");
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'badge' => $badge,
'sound' => 'default'
);
// Encode the payload as JSON
$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));
if (!$result)
echo("\n\nMessage not delivered.\n\n");
else
echo("\n\nMessage successfully delivered.\n\n");
// Close the connection to the server
fclose($fp);
}
}
}
?>
When Execution of this class will be completed then it will go back to file A and it will continue execution after sending notification.
But here it will take time to send notification (around 3-5-7 seconds) and for that time user has to wait which is not a good idea.
So what I want to do is that send notification in Background process instead of main thread.So user don't has to wait unnecessarily.
Please note that I don't want to use Cron.
I found that exec/shell_exec/passthru command can help me. But which command I used out of these three commands in this case?
Please Guide me on this. Any help will be appreciated.
passthru("/usr/bin/php /path/to/yourFile.php >> /path/to/log_file.log 2>&1 &");
There are a few thing that are important here.
First of all: put the full path to the php binary, because this command will run under the apache user, and you will probably not have command alias like php set in that user.
Seccond: Note 2 things at the end of the command string: the 2>&1 and the &. The 2>&1 is for redirecting errors to the standard IO. And the most important thing is the & at the end of the command string, which tells the terminal not to wait for a response.
There are a few options here:
You can fork a separate PHP process with pcntl_fork (http://php.net/manual/en/function.pcntl-fork.php)
You can use a cronjob to check for unsent modifications which you do not want to (you failed to mention WHY, this is why I mention it)
You can use a job queue system for it. By far the best solution but takes some time to get started. Gearman is the one I have experience with and that works like a charm.
Follow the instructions here to run a PHP script in the background:
php execute a background process
It requires the use of shell_exec to call the script from File A, so you will have to create a new file for that code to be called. It also assumes you are using linux