I have a PHP server-like script that runs indefinitely. It connects to APNS (Apple Push Notification Service) with a stream_socket_client object in PHP.
The problem is that while this works most of the time, I cannot figure out how to validate if the connection to APNS is available when I want to send a push notification. The script receives incoming connections and copies the content to APNS.
When it doesn't work, the script reconnects (the return value of stream_copy_to_stream is 0 on 0 bytes written, which triggers reconnect) and then the next incoming token will succeed, but it logs the following and of course does not deliver the message:
PHP Warning: stream_copy_to_stream(): SSL: An established connection was
aborted by the software in your host machine.
Is there any way to detect the above and thus reconnect to APNS before trying to write to the socket.
And doing this before trying to copy to APNS:
if (!$socketObject) {
// reconnect
}
Does not work, as the object still exists (var_dump will print a description). So how can I check if the connection is still open?
Writing anything to the socket that is not a valid token and payload will cause APNS to disconnect.
The code that performs the writing looks like this:
function writeToAPNS($client) {
return stream_copy_to_stream ( $client, $this->connectionAPNS );
}
Where $client is returned from stream_socket_accept ( $this->socket, -1 ) and $this->socket is a variable containing the local socket object listening for incoming connections. -1 means there is no timeout when waiting for an incoming connection. When it works this function returns the number of bytes written. When it fails it returns 0.
$this->connectionAPNS is the object returned from stream_socket_client when connecting to APNS. This is what I want to check for validity before I perform the stream_copy_to_stream function.
So basically I want to detect if $this->connectionAPNS is valid.
I tried dumping the metadata for the connection object, but it doesn't say anything about the validity, only if it's timed out, protocol type etc.
Did you see this note in the official stream_socket_client documentation?
UDP sockets will sometimes appear to have opened without an error, even if the remote host is unreachable. The error will only become apparent when you read or write data to/from the socket. The reason for this is because UDP is a "connectionless" protocol, which means that the operating system does not try to establish a link for the socket until it actually needs to send or receive data.
Please try this methiod stream_get_meta_data on your connection to try to detect a difference. I can't test it right now, but it might help you. Especially the timed_out property.
Another option to at least mute the error messages, since you are handling them, is to set an appropriate errno on the stream_socket_client.
Well, as explained by M.K., this is how UDP works. And by the way, your code works. Your problem is the warning, which is fairly easy to get rid of by adding an # to your write function.
Your code would then look like :
function writeToAPNS($client) {
return #stream_copy_to_stream ( $client, $this->connectionAPNS );
}
Related
I am implementing my custom XMPP PHP library (Packagist repo) and I have trouble fetching messages (that the client sent) from XMPP server.
Library is using PHP sockets to connect to the server, and I am able to fetch a response from server when initially connecting and authenticating. I can also send a message from server to the client, and that part works.
I can't however receive a message.
This is the code I am using when receiving anything from server:
public function getRawResponse()
{
// Wait max 3 seconds before terminating the socket
socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, ["sec" => $this->options->getSocketWaitPeriod(), "usec" => 0]);
while ($out = socket_read($this->socket, 2048)) {
echo "*** Data ***\n\n";
echo str_replace("><", ">\n<", $out) . "\n\n";
echo "\n\n************\n";
}
}
This while loop is here to fetch all one-batch responses from the server, and it reads from server while it has something to read, otherwise it terminates the connection.
In the main program I am thus doing a do{...}while(true) and putting this method inside so that it doesn't terminate ever. But still I am not getting any response when sending the other way around, from client back to server.
I have found that I needed to send initial empty presence stanza to server
<presence/>
Once I got the server response back, so did the message responses started incoming.
I have a server online, and I wrote a PHP Code to act as a Socket Server to handle messages. The messages are coming in from devices that connect to the IP and the Port and send messages.
I found this code example that lets me handle multiple connections at the same time and manage the messages as they come in.
I have modified the code to properly parse the messages that come in from my devices.
Everything has been going well during testing. However, when we did extended testing, the PHP Socket Server would shutdown at seemingly random times and would give the error:
socket_read() failed: reason: Connection reset by Peer at line 104.
Looking at the code I am running, this is the code block for line 104, it starts at the if condition.
if (false === ($buf = socket_read($client, 115200, PHP_BINARY_READ))) {
echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($client)) . "\n";
$GLOBALS['mysqli']->close();
break 2;
}
I actually am not sure how to make sense of this error message. During our testing periods, sometimes that error would show up and shut down the Socket Server while the device is sending a message - but we are sure that the device did not send a Socket Shutdown command. At other times, when we would let the Socket Server just run through, that error would show up.
Does anyone know how to effectively keep a PHP Socket Server from shutting down and keep on running?
I know I can run a cronjob that periodically checks if the socket is being used. However, when the socket server shuts down unexpectedly, the PHP Code can't execute as it still reads that the port is still in use, even if the previous socket server instance is already down and we can no longer connect to it. It takes quite some time until the cronjob manages to put the socket back up again and accept messages - and we need the socket server to run virtually all the time.
"Connection reset by Peer" means the client disconnected unexpectedly. For instance, the user may have closed the client before it was done talking to the server.
Your server code needs to be prepared to handle errors on client sockets, and not quit when it gets an error on a client socket. When you get an error from a client socket it's appropriate to just disconnect that client.
I am integrating an IP-Restricted API from a company called XendPay. They allowed requests from the Server IP only. I know PHP scripts run on server but I am not getting response when I request data.
Here is an example:
$jsonRequestURL = 'http://uat.xendpay.com/partner/api/1.0/{partner_key}/quote?countryFrom=PL&countryTo=GB¤cyFrom=PLN¤cyTo=GBP&deliveryType=BANK_TRANSFER&paymentMethod=banktransferlocal&amount=1000.00&amountCurrency=PLN&indent=true';
json_decode ( file_get_contents ( $jsonRequestURL ) );
It sometimes stuck here or after waiting quite long, gives the following error:
A connection attempt failed because the connected party did not
properly respond after a period of time, or established connection
failed because connected host has failed to respond.
I am not sure but I assume that request is not being sent through Server IP as I have called above mentioned json_decode() on pageload.
Can anyone suggest what should I do?
I've been working on this for about 20 hours now and I need some major help. I'm able to get the file size and the timestamp on the file but I am unable to actually obtain the data.
The server I'm trying to get the data from requires FTP over explicit TLS
I'm receiving the same error(s) with both FTP_BINARY and FTP_ASCII in the ftp_fget()
The server the file is coming from is UNIX
If I refresh the page every few hours the errors I get from PHP are different with no change in code
Error 1: 'ftp_fget(): Transfer mode set to BINARY if ftp_get is binary
or it's 'ftp_fget(): Transfer mode set to ASCII' if ftp_get is ascii
Error 2: 'ftp_fget(): Entering Passive Mode(12.345.678.90.12.34)'
On the above errors I read that PASV mode being FALSE is what triggers Error 1, so I think the switching between the errors is for pasv mode working or not working. Not positive though.
<?php
$server = "12.345.678.90";
$local_file = 'inv3.txt';
$file = 'inventory-alp.txt';
$con = ftp_ssl_connect($server,21) or die("Could not connect to $server");
ftp_login($con,"xxxxxx","xxxxxx") or die("Could not login");
ftp_pasv($con,true);
$fsize = ftp_size($con, $file); // works
if ($fsize != -1)
{
echo "</br>$file is $fsize bytes.</br></br>";
}
else
{
echo "</br>Error getting file size.</br></br>";
}
$lastchanged = ftp_mdtm($con, $file); //works
if ($lastchanged != -1)
{
echo date("F d Y H:i:s.",$lastchanged)."</br></br>";
}
else
{
echo "Could not get last modified</br></br>";
}
if (ftp_get($con,$local_file,$file,FTP_ASCII)) //fails
{
echo "successfully written to $local_file";
}
else
{
echo "There was a problem while downloading $file to $local_file";
}
$var = error_get_last();
echo '<pre>';
var_dump($var);
echo '</pre>';
ftp_close($con);
?>
EDIT 1: Solution: I ended up not being able to access what I needed to change the firewall settings and such in php. While this is not the true answer, I did make it work and it is relatively easy.I ended up running across WINSCP, having the ability to connect to the server in a filezilla type layout and then save the session url was nice. All i did was access the saved session in the .exe and was able to set up my connection in half an hour.
What Martin indicated is very true, the SIZE and MDTM commands run synchronized over the main FTP Command Connection only. The transferring of data files, and usually the directory listing also (unless MLST/MSLD is used) requires a separate connection, the Data Connection, which is negotiated by the client and server over the Control Connection using a series of commands, most notably PORT and PASV.
Without going into a ton of detail (There's a link to our white paper later), when the Client & Server negotiate the terms of the Data Connection, one of the end points will tell the other endpoint the specific IP address and Port number for the connection. One endpoint will listen and wait for a connection from the other endpoint. This works great unless there is a firewall in front of the endpoint that is waiting for the inbound connection. If the client/server session is running in Active mode, the Server will actively connect back to the client on the IP/Port which was received by the server from the client in the form of the PORT command. In Passive mode, the Server will passively wait for the client to connect on the IP/Port which was sent by the server to the client in the response to the PASV command sent by the client to the server.
Again, firewalls tend to block FTP data connections, unless the firewall does active FTP NAT'ing or unless Port Forwarding has been set up on the Firewall and a set of passive-ports has been opened and routed to the endpoint specifically.
So check the firewall settings on the client if you want to use Active/Port mode; check the firewall settings on the server if you want to use Passive/PASV mode.
Here's a link to our white paper which outlines the basics of FTP/PASV/PORT, hopefully it'll help you with your issue.
http://www.webdrive.com/wp-content/uploads/FTP_Explained1.pdf
Best of Luck!
Michael
With the FTP protocol, it's perfectly possible that you are able to obtain the file size and the modification timestamp (using SIZE and MDTM commands respectively), but not the file itself.
The SIZE and MDTM commands use the FTP control connection only.
While a file transfer (or a directory listing) requires a separate data connection. And it's likely that there's something that prevents the data connection from being opened.
See (my) article on the FTP connection modes for more details and typical issues with data connections.
Typically a culprit would be a firewall on your webserver. If you have an SSH/terminal access to the webserver, are you able to connect from it to the FTP server?
Another possibility is a misconfigured FTP server. Is the IP address in "Error 2" routable from your web server? (=Is it the real IP address you connect to?)
It is unlikely this is related to an ASCII/BINARY mode. The messages you are getting (Transfer mode set to ...) are status messages, not error messages. They are not related to your problem. It's indeed strange that you got no other message/error.
You can try to use the active mode, instead of the passive.
ftp_pasv($con, false);
But usually the active mode is more problematic.
I created a socket server with 10 possible clients by using socket_accept(socket).
It is possible to connect up to 10 times now.
The problem appears after the first client submitted a message to the server which is caught by
$data = #socket_read($clients[$i]['socket'], 1024, PHP_NORMAL_READ);
Then the script tries to listen for clients and messages again by socket_accept and socket_read, but all socket_accept-requests fail with the error string "Invalid argument".
var_dump indicates that the parameter is a resource(14) of type (Socket), though.
Already connected clients can continue using the "server" script as nothing happened and remain connected. Only new clients are unable to connect and the port seems to close (no telnet and netcat requests are possible - connection refused)
Any ideas would be helpful. Thanks!
Discovered that I assumed to disconnect all sockets when the destructor of the socket handler class was called - which terminated all connections once the child process closed.
Removing the socket_shutdown() and socket_close() from the destructor solved the problem.