I use a self written script to send push notifications to APNS with PHP. In order to be able to process errors I use the extended format for the Push notifications and would like to fetch results from the stream:
// $apns = a stream_socket_client connection
$apnsMessage = pack('CNNnH*', 1, $i, $pnDetails['expiration_time'], 32, $pnDetails['token']);
$apnsMessage .= pack('n', strlen($pnDetails['payload']));
$apnsMessage .= $pnDetails['payload'];
fwrite($apns, $apnsMessage);
// Check for errors
$errorResponse = #fread($apns, 6)
if ($errorResponse != FALSE) {
$unpackedError = unpack('Ccommand/CstatusCode/Nidentifier', $errorResponse);
}
I have seen a very similar practice in the apns-php project, however, in my case the script always waits indefinitely at the fread line because it tries to read data which is not there (Apple only sends a response if there was an error). I have looking for ways to tell if there is any new data to read from a TCP stream, however, I could find none and the stream callback methods available for HTTP calls are not available for "raw" TCP connections either.
How can I transform my script to make sure it only calls fread when there actually is data to read? How does the apns-php project solve this issue (from what I could tell they were just calling fread as well)?
Figured it out, the final hint came from Erwin. The trick was to deactivate the blocking with stream_set_blocking, now I just need to wait some time before fetching the results with fread to make sure that Apple has enough time to respond.
Are you connecting to the right host ssl://feedback.push.apple.com:2196 ?
They are using the following calls to connect and read data:
stream_context_create -> stream_socket_client -> stream_set_blocking (0) -> stream_set_write_buffer (0) -> while (!feof($socket)) {} -> fread (8192) -> stream_select (with timeout)
Related
I'm trying to make tcp socket server to create and maintain persistent bidirectional communications, via PHP's stream_socket_server().
Short version of question:
how to have tcp server created with stream_socket_server() staying
alive - not failing to receive data after first successful data
reception which is in my case one single character typed in terminal after telnet
command?
Long version - what exactly I expect to achieve
Just for illustration, look at communication type when telneting some host with smtp server. You type in terminal telnet somehost 25 and get welcome response and (usually) smtp banner. And connection persists. Than you type hello command, and you get, again, response with option to proceed issuing further commands. Again, connection persists, you got response and option to continue. This is exact communication type I'm after.
What have I done so far
this code:
<?php
$server = stream_socket_server("tcp://0.0.0.0:4444", $errno, $errorMessage);
if ($server === false) throw new UnexpectedValueException("Could not bind to socket: $errorMessage");
for (;;) {
$client = stream_socket_accept($server);
if ($client)
{
echo 'Connection accepted from ' . stream_socket_get_name($client, false) . "\n";
echo "Received Out-Of-Band: " . fread($client, 1500) . "\n";
#fclose($client);
}
}
which works fine if you are just trying to send data (once per shot) via another PHP script i.e. stream_socket_client().
Server created by this script fails to maintain connection type I have described. After doing telnet localhost 4444 I'm switched into terminal mode to write message. When I do so, on the server side I got first typed character caught up and - nothing more. Whatever I've tried, I couldn't catch new packages sent from client side by typing. Like stream_socket_accept() blocks or ignores all after first character-data package received.
So, what am I doing wrong - how to solve this issue?
Your program is only doing a single read before it loops back to accept another incoming connection.
The documentation for fread states that it will return as soon as a packet has been received. That is, it won't wait for the full 1500 bytes.
Given the slow speed of human typing, you end up sending a packet with a single character to your server which is returned and then it goes on to accept another incoming connection.
Put a loop around your read as such:
for (;;) {
$client = stream_socket_accept($server);
if ($client) {
echo 'Connection accepted from ' . stream_socket_get_name($client, false) . "\n";
for (;;) {
$data = fread($client, 1500);
if ($data == '') break;
echo "Received Out-Of-Band: " . $data . "\n";
}
fclose($client);
}
}
I have a client-server application, in which the server may require to send information back to clients.
As the client-server pattern does not allow the server to "request" the client, there are 2 solutions:
The client pull the server every few time (which is a bad solution)
The client maintain an open socket with the server, that allow the server to send new information back when required.
Currently, the client (Web app with JavaScript and Html/Css) open a streaming connection to the server (A C++ server) which may send information back to the client.
I would like to implement a PHP version of this feature to allow low-cost hosting to work with my program (low-cost hosting usually does not provide access to install/run binaries).
The idea is that the client make a request that establish the streaming socket, it save the socket and then, an other request may retrieve this socket and send new information through it.
So, my question is:
How to save an http socket in PHP, so a further request may retrieve it?
How to finish the PHP script without closing the socket?
How to save socket information?
How to retrieve the socket from a new thread/request?
I do not know even if that is possible, I read about pfsockopen, but it seem a bit different to what I need ( I may be wrong ).
So, you need two connections for each client, one persist for get data from server, and other to send data to.
Something like:
in persist.php:
$socket = stream_socket_server('unix:///tmp/unique.sock', $errno, $errstr);
if (!$socket) {
echo "$errstr ($errno)<br />\n";
} else {
while ($conn = stream_socket_accept($socket)) {
$buffer = "";
// Read until double CRLF
while( !preg_match('/\r?\n\r?\n/', $buffer) )
$buffer .= fread($client, 2046);
//Operate with our listener
echo $buffer;
flush();
// Respond to socket client
fwrite($conn, "200 OK HTTP/1.1\r\n\r\n");
fclose($conn);
}
fclose($socket);
}
in senddata.php:
$sock = stream_socket_client('unix:///tmp/unique.sock', $errno, $errstr);
fwrite($sock, $data);
fflush($sock);
fclose($sock);
One way to solve it - forget about sockets.
Pseudocode:
// receive request, set some session_id if not exists
// request contains last_timestamp, so we know which data client already have
// check have we any dataset for this session_id after last_timestamp
// return this dataset, or no_new_data signature
Data can be stored in database, for example.
I am trying to encode an image to base64 encoding and send it to a C++ server I am creating. I am using PHP to do that.
Therefore, the PHP code is the client and the C++ code is the listening server.
The problem occurs on large images; for example 70KB images. It is working properly on small images; such as 5KB.
The error occurring is: Warning: socket_write() [function.socket-write]: unable to write to socket [0]: A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself.
Can this problem be fixed without dividing the image into several small packets?
I only need the sending packet to be 1MB.
This is the code I am using:
$con=file_get_contents("image url");
$binary_data=base64_encode($con);
$host = "192.168.35.54";
$port = 1060;
$message = $binary_data;
// No Timeout
set_time_limit(0);
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP) or die("Could not create socket\n");
socket_connect($socket, $host, $port) or die("Could not connect to server\n");
socket_write($socket, $message, strlen($message)) or die("Could not send data to server\n");
There are a number of possible problems you could have. I assume you're using TCP. I'm also assuming you're writing the entire image in one call to socket_write. My first guess would be that the problem is one the receiving side. When you read the socket on the receiving side, you're not guarantted to get the entire block of data in one read. Normally, when reading TCP sockets, you read in a loop and accumulate the data that way until you've gotten all the data you're expecting, or you get an error.
You can see some example code in this other SO post:
Read from socket: Is it guaranteed to at least get x bytes?
EDIT
After seeing these additional details, my first suggestion would be to switch to TCP. You can do a single send that way, and then read in a loop on the receiving side like in the example code above. Otherwise, you'll have to break up the packet, and build in your own error detection code to make sure all the pieces arrive, plus put in sequence codes to reassemble them in order, which is basically just duplicating a bunch of functionality TCP already provides.
You need to set the socket send buffer to be at least as large as the largest UDP datagram you are sending.
Don't ask me how to do that in PHP but at the Sockets API level it is setsockopt() with the SO_SNDBUF option.
I'm using the PHP Stomp client to send a stomp message.
I would like to leave a persistent connection open, in the background, and send messages occasionally.
However, I can't find a way to handle connection errors if they happen after opening the connection (on send()).
For example, when running:
<?php
$stomp = new Stomp('tcp://localhost:61613');
sleep(5); // Connection goes down in the meantime
$result = $stomp->send('/topic/test', 'TEST');
print "send " . ($result ? "successful\n": "failed\n");
?>
Output: send successful
Even if the connection goes down while in sleep(), send() always returns true.
The docs weren't very helpful, Stomp::error() and stomp_connect_error() also don't help much as they return false.
As a temporary solution, I'm reconnecting before every send().
Is there a better way to catch connection errors?
Found the answer in the specification of the stomp protocol itself:
Any client frame other than CONNECT MAY specify a receipt header with an arbitrary value. This will cause the server to acknowledge receipt of the frame with a RECEIPT frame which contains the value of this header as the value of the receipt-id header in the RECEIPT frame.
So setting a "receipt" header makes the request synchronous, so the connection to the server must be alive.
So the code:
$result = $stomp->send('/topic/test', 'TEST');
print "send " . ($result ? "successful\n": "failed\n");
$result = $stomp->send('/topic/test', 'TEST', array('receipt' => 'message-123'));
print "send " . ($result ? "successful\n": "failed\n");
Gives output:
send successful
send failed
It doesn't seem like the best solution for this case, but it works for me.
If anyone knows a better way I'll be happy to hear it.
Update:
Eventually I switched to Stomp-PHP (a pure PHP client) instead of the Pecl stomp client, which handles it much better.
I have a php server listening for 1 c# client.
When a connection is established, it is kept alive until client sends the command "quit" which kills the PHP server.
But when the c# client disconnects without the "quit" command (ie : clicking the close (x) button in the windows form) the server just keep listening, and can't receive any other connection from that client.
Is there a way to check from the server side (PHP) if connection is still alive with client?
My php server code is based on example1 of: http://php.net/manual/en/sockets.examples.php
If someone is interested in reproducing the bug/error behavior, paste code from example1 : http://php.net/manual/en/sockets.examples.php, connect by telnet from a remote client in lan, unplug client wire... php server will hang around forever, no new connection is accepted.
In your loop, you need to check the return value of socket_read(). If it returns FALSE, then there was a read error (which can be caused by the remote host closing the connection). The example code in the link you provided covers this case.
If you need to gracefully handle certain error states, you can always check the socket error code using socket_last_error() -- this note decribes the possible codes.
Edit:
When using putty for telnet, if i close with X button, connecion is closed properly in PHP, but if i unplug the ethernet wire of the putty machine, PHP server just hangs around.
The reason that the connection is closed when killing PuTTY is that PuTTY closes its open connection(s) when exiting. This causes socket_read() to return with an error code (I believe ECONNRESET). If you pull the network cable, it doesn't have a chance to do that.
Depending on how your network is configured, the TCP connection should eventually fail. You can attempt to control the timeout by setting SO_RCVTIMEO with socket_set_option(), but this doesn't always work on all platforms (I'm looking at you, WinSock).
Alternatively, you can roll your own polling loop using socket_select() with a reasonable timeout. If none of the connected sockets have data to send after your timeout, then kill the server.
I did this by checking if the socket_select() $read array includes the connection in question but socket_read() data is empty (twice).
It seems that socket_select() adds disconnected clients to the $read array and socket_read() gives an empty string '' when trying to read the disconnected client's data.
The following code will show the state of connection.
$address='example.com';
$port = '9065';
if (isset($port) && ($socket=socket_create(AF_INET, SOCK_STREAM, SOL_TCP))
&& (socket_connect($socket, $address, $port))) {
$text="Connection successful on IP $address, port $port";
socket_close($socket);
}
else {
$text='Unable to connect<pre>'.socket_strerror(socket_last_error()).'</pre>';
}
echo $text;
As Michael Dodd says, using socket_select() can have all sockets that have received data plus those they have closed. Also you can have all those that their buffer can accept data for transmission, and / or those that have raised some exception. Do have this info, a (separate) copy of sockets array must be placed in 2nd and / 3d parameter of function.
(if not needed, this function must have $null as a variable equal to null, not just null)
The following example shows how to read data and test sockets if they are still open. I am using this code for sockets that have been created by socket_accept() on a listening socket, and it is working without problems. Instead of using socket_recv, socket_read can be used.
//all sockets created have been inserted to an array $socketArray
//get a copy of the sockets array
$tempArray = $socketArray;
//this command will remove from $tempArray all sockets that have no data and are alive, with a timeout of 0 sec, 100 msec
socket_select($tempArray, $null, $null, 0, 100);
if (count($tempArray)) {
//if we have some sockets in the array
foreach($tempArray as $socket) {
//read some data
$count = socket_recv($socket, $socketData, 1024, 0);
if ($count) {
//your code to do what you want with $socketData
} else {
//find socket position in initial socket array
$index = array_search($socket, $socketArray);
//if found, remove it from array and close the socket
if ($index !== false) {
array_splice($socketArray, $index, 1);
socket_close($socket);
}
}
}
}