How to NOT reuse a resource in PHP during socket-action? - php

We have the following setup:
A server written in JAVA who accepts connections, gets data, calculates a result and sends it back. This is located one one machine.
A client written in JAVA which connects, sends data and waits for the response of the server. These work from different machines as the server.
A client written in PHP which should be able to do the same work as the JAVA-client. Works from different machines as the server too.
Anytime there are several JAVA-clients who contact the server and exchange data and responses. This works fine. Using just one PHP-client works as well.
The problem occurs when several PHP-clients are launched. They all use the same resource when fired from the same web-server. This results in the unpleasant situation that if you close one client because the work is done, the transmission to the other PHP-Clients will be terminated as well because the shared resource will be closed.
What we need is a unique resource in PHP for every script-start so we can work and close it separately from all others. Is this possible?
The network-connection from PHP-side will be started like this:
function network($host, $port) {
if ( ($socket = #socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === FALSE ) {
return -1;
} else {
echo "Resource available? ".is_resource($socket)."<br />";
if ( ($result = #socket_connect($socket, $host, $port)) === FALSE ) {
return -2;
}
return $socket;
}
}
The closing of the socket in PHP is simple:
socket_close($socket);

Related

Detecting peer disconnect (EOF) with PHP socket module

I'm having a weird issue with PHP's sockets library: I do not seem to be able to detect/distinguish server EOF, and my code is helplessly going into an infinite loop as a result.
Further explanation below; first of all, some context (there's nothing particularly fancy going on here):
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 8081);
for (;;) {
$read = [$socket];
$except = NULL;
$write = [];
print "Select <";
$n = socket_select($read, $write, $except, NULL);
print ">\n";
if (count($read)) {
print "New data: ";
#socket_recv($socket, $data, 1024, NULL);
$data = socket_read($socket, 1024);
print $data."\n";
}
print "Socket status: ".socket_strerror(socket_last_error())."\n";
}
The above code simply connects to a server and prints what it reads. It's a cut-down version of what I have in the small socket library I'm writing.
For testing, I'm currently using ncat -vvklp 8081 to bind a socket and be a server. With that running, I can fire up the code above and it connects and works - eg, I can type in the ncat window, and PHP receives it. (Sending data from PHP is working too, but I've excluded that code as it's not relevant.)
However, the moment I ^C ncat, the code above enters a hard infinite loop - and PHP says there's no error on the socket.
I am trying to figure out where the button is that whacks PHP upside the head and makes it realize that the peer has disconnected.
socket_get_status() is a great misnomer - it's an alias for stream_get_meta_data(), and it doesn't actually work on sockets!
feof() similarly spouts Warning: feof(): supplied resource is not a valid stream resource.
I can't find a socket_* function for detecting peer EOF.
One of the PHP manual notes for socket_read() initially dissuaded me from using that function so I used socket_recv() instead, but I eventually tried it just in case - but no dice; switching the receive call has no effect.
I have discovered that watching the socket for writing and then attempting to write to it will suddenly make PHP go "oh, wait, right" and start returning Broken pipe - but I'm not interested in writing to the server, I want to read from it!
Finally, regarding the commented part - I would far prefer to use PHP's builtin stream functionality, but the stream_* functions do not provide any means for handling asynchronous connect events (which I want to do, as I'm making multiple connections). I can do stream_socket_client(... STREAM_CLIENT_ASYNC_CONNECT ...) but then cannot find out when the connection has been established (6yo PHP bug #52811).
Okay, I figure I might as well turn the comments above into an answer. All credit goes to Ryan Vincent for helping my thick head figure this out :)
socket_recv will return 0 specifically if the peer has disconnected, or FALSE if any other network error has occurred.
For reference, in C, recv()'s return value is the length of the new data you've just received (which can be 0), or -1 to indicate an error condition (the value of which can be found in errno).
Using 0 to indicate an error condition (and just one arbitrary type of error condition, at that) is not standard and unique to PHP in all the wrong ways. Other network libraries don't work this way.
You need to to handle it like this.
$r = socket_recv($socket, $buf, $len);
if ($r === FALSE) {
// Find out what just happened with socket_last_error()
// (there's a great list of error codes in the comments at
// http://php.net/socket_last_error - considering/researching
// the ramifications of each condition is recommended)
} elseif ($r === 0) {
// The peer closed the connection. You need to handle this
// condition and clean up.
} else {
// You DO have data at this point.
// While unlikely, it's possible the remote peer has
// sent you data of 0 length; remember to use strlen($buf).
}

PHP fputs "waits" until the end of the script

Currently I am trying to develop a PHP script used as a publicly available part of a client/server application. The php script should be used to authenticate users with a one-time token.
The other part of the application is a java program, which offers a telnet socket for other applications to connect to. Authentication is done through this telnet connection.
The java part is already working - but I still have a huge problem with the PHP part.
In php, I have opened a connection to the telnet port of the java program, which works so far. After the connection is initialized, the java program waits for input from the PHP script in order to authenticate the user.
After the authentication process has been finished, it returns a String to the PHP script (or any other program connected to its telnet server) which the PHP script should output.
Before I explain my problem, this is the part of the PHP script where the actual communication happens:
$tnconn = fsockopen("localhost", 53135, $errno, $errstr, 2);
if(!$tnconn) {
echo "SERVER_UNAVAILABLE";
die();
} else {
$data = $p_ip." ".$p_name." ".$p_token;
fputs($tnconn, $data);
while (true) {
if(($telnet_response = fgets($tnconn)) == false) {
break;
}
}
}
echo $telnet_response;
It seems like the fputs() statement is executed after the loop even tho it should happen before it starts - else the java application couldn't get the data that is passed to the php script, but it is needed to authenticate users.
Right after the data was received, the telnet server would output the String to indicate whether authentication was successful or not.
I tried temporarily removing the loop and the data was successfully passed with fputs() so I assume php waits until the whole script is finished and then executes the function.
How can I make it send the data before the loop?
Thank you in advance.
The issue is probably that you need to send a \n at the end of your data string so the telnet server knows you have sent a full sequence of data. Otherwise it is most likely sitting there waiting for more input.
Try:
$data = $p_ip." ".$p_name." ".$p_token . "\n";

PHP: how to use socket_select() and socket_read() properly

Edit: *Bug in test harness was causing me to misinterpret the results. socket_select() works exactly as you might expect: it really does wait until there is data ready on the socket. But it will report ready if you call it after the client closes the connection. Turns out it's all my fault, but I'll leave the question here in case anyone else ever suspects the behavior of socket_select().
I have a multi-threaded PHP application and I'm using sockets to communicate between the threads. The communication works pretty well, but I end up doing a lot of unnecessary reading of sockets that have no data ready. Perhaps there's something about socket programming that I'm missing.
The PHP doc for socket_select() says The sockets listed in the read array will be watched to see if a read will not block.
This is exactly the behavior I want: call socket_select() to wait until one of my child threads is trying to talk to me. But that only works for the very first time the thread writes, after I've accepted the connection. After that, socket_select() will forever say that the socket is ready, even after I've read all the data from it.
Is there some way I can mark that socket 'unready' so socket_select() won't report that it's ready until more data arrives? Or is it conventional to close that connection after I've read all the data, and await another connection request? I've done a lot of tutorial- and explanation-reading, but I haven't been able to figure out how to do this correctly. Maybe I'm missing something obvious?
In case it helps to see code, here's what I'm doing:
// Server side setup in the main thread
$this->mConnectionSocket = socket_create(AF_INET, SOCK_STREAM, 0);
$arrOpt = array('l_onoff' => 1, 'l_linger' => 0);
#socket_set_option($this->mConnectionSocket, SOL_SOCKET, SO_LINGER, $arrOpt);
#socket_set_option($this->mConnectionSocket, SOL_SOCKET, SO_REUSEADDR, true);
#socket_bind($this->mConnectionSocket, Hostname, $this->mPortNumber);
#socket_listen($this->mConnectionSocket);
#socket_set_block($this->mConnectionSocket);
.
.
.
// Then call listen(), which looks like this:
public function listen(&$outReadySockets) {
$null = null;
while(true) {
$readyArray = array_merge(array($this->mConnectionSocket), $this->mReceiverSockets);
socket_select($readyArray, $null, $null, $waitTime = null);
if(in_array($this->mConnectionSocket, $readyArray) === true) {
$this->acceptConnection();
$key = array_search($this->mConnectionSocket, $readyArray);
if($key === false) {
throw new IPCException("array_search() returned unexpected value");
} else {
unset($readyArray[$key]);
if(in_array($this->mConnectionSocket, $readyArray) === true) {
throw new IPCException("in_array() says the key is still there");
}
}
}
if(count($readyArray) > 0) {
$outReadySockets = array_merge($readyArray);
break;
}
}
}
// Client side setup in the child thread
$this->mSocket = #socket_create(AF_INET, SOCK_STREAM, 0);
#socket_set_block($this->mSocket);
#socket_connect($this->mSocket, Hostname, $this->mPortNumber);
.
.
.
#socket_write($this->mSocket, $inDataToWrite, $lengthToWrite);
// Main thread reads the socket until it's empty
$data = "";
$totalBytesRead = 0;
while($totalBytesRead < $inNumberOfBytesToRead) {
// Strange that even if we set the socket to block mode, socket_read()
// will not block. If there's nothing there, it will just return an
// empty string. This is documented in the PHP docs.
$tdata = socket_read($inSock, $inNumberOfBytesToRead);
if($tdata === false) {
throw new IPCException("socket_read() failed: " . socket_strerror(socket_last_error()));
} else {
$data .= $tdata;
$bytesReadThisPass = strlen($tdata);
if($bytesReadThisPass === 0) {
break;
}
}
$totalBytesRead += $bytesReadThisPass;
}
.
.
.
// Then calls listen() again
As I say, it works great, except that when I call listen() a second time, it tells me that the socket is still ready. That seems to be what the PHP doc is saying, but I don't want that. I want to know when there really is data there. Am I doing it wrong? Or just missing the point?
You are misusing the socket functions.
First of all, socket_read will return false on error; your code does not check for this and will treat error returns the same as empty strings (this is an artifact of the specific constructs you are using, i.e. string concatenation and strlen).
Another problem is that when a connection attempt is detected you are clearly violating the instructions for socket_select:
No socket resource must be added to any set if you do not intend to
check its result after the socket_select() call, and respond
appropriately. After socket_select() returns, all socket resources in
all arrays must be checked. Any socket resource that is available for
writing must be written to, and any socket resource available for
reading must be read from.
Instead of this, if a connection is detected the code accepts it and goes back to socket_select again; sockets ready for reading are not serviced.
Finally it looks like you are confused about what EOF means on a socket: it means that the client has closed its write end of the connection. Once socket_read returns the empty string (not false!) for the first time, it will never return anything meaningful again. Once that happens you can send data from the server's write end and/or simply close the connection entirely.

Webbased chat in php without using database or file

I am trying to implement a realtime chat application using PHP . Is it possible to do it without using a persistent data storage like database or file . Basically what I need is a mediator written in PHP who
accepts messages from client browsers
Broadcasts the message to other clients
Forgets the message
You should check out Web Sockets of html5. It uses two way connection so you will not need any database or file. Any chat message comes to the server will directly sent to the other users browser without any Ajax call. But you need also to setup web socket server.
Web sockets are used in many real time applications as well. I am shortly planing to write full tutorial on that. I will notify you.
Just tried something I had never done before in response to this question. Seemed to work but I only tested it once. Instead of using a Socket I had an idea of using a shared Session variable. Basically I forced the Session_id to be the same value regardless of the user therefore they are all sharing the same data. From a quick test it seems to work. Here is what I did:
session_id('12345');
session_start();
$session_id = session_id();
$_SESSION['test'] = $_SESSION['test'] + 1;
echo "session: {$session_id} test: {$_SESSION['test']} <br />";
So my thought process was that you could simply store the chat info in a Session variable and force everyone regardless of who they are to use a shared session. Then you can simply use ajax to continually reload the current Session variable, and use ajax to edit the session variable when adding a message. Also you would probably want to set the Session to never expire or have a really long maxlifetime.
As I said I just played around with this for a few minutes to see if it would work.
You will want to use Sockets. This article will cover exactly what you want to do: http://devzone.zend.com/209/writing-socket-servers-in-php/
When I tried to solve the same problem, I went with Nginx's Push Module. I chose to go this way since I had to support older browsers (that usually won't support WebSockets) and had no confidence in setting up an appropriate solution like Socket.io behind a TCP proxy.
The workflow went like this:
The clients connect through long-polling to my /subscriber location, which is open to all.
The /publisher location only accepts connections from my own server
When a client subscribes and talks, it basically just asks a PHP script to handle whatever data is sent.
This script can do validation, authorization, and such, and then forwards (via curl) the message in a JSON format to the /publisher.
Nginx's Push Module handles sending the message back to the subscribers and the client establishes a new long-polling connection.
If I had to do this all over again, then I would definitely go the Socket.io route, as it has proper fallbacks to Comet-style long-polling and has great docs for both Client and Server scripts.
Hope this helps.
If you have a business need for PHP, then adding another language to the mix just means you then have two problems.
It is perfectly possible to run a permanent, constantly-running daemonised PHP IRCd server: I know, because I've done it, to make an online game which ran for years.
The IRC server part I used is a modified version of WaveIRCd:
http://sourceforge.net/projects/waveircd/
I daemonised it using code I made available here:
http://www.thudgame.com/node/254
That code might be overkill: I wrote it to be as rugged as I could, so it tries to daemonise using PHP's pcntl_fork(), then falls back to calling itself recursively in the background, then falls back to perl, and so on: it also handles the security restrictions of PHP's safe mode in case someone turns that on, and the security restrictions imposed by being called through cron.
You could probably strip it down to just a few lines: the bits with the comments "Daemon Rule..." - follow those rules, and you'll daemonize your process just fine.
In order to handle any unexpected daemon deaths, etc, I then ran that daemoniser every minute through cron, where it checked to see if the daemon was already running, and if so either quietly died, or if the daemon was nonresponsive, killed it and took its place.
Because of the whole distributed nature of IRC, it was nicely rugged, and gave me a multiplayer browser game with no downtime for a good few years until bit-rot ate the site a few months back. I should try to rewrite the front end in Flash and get it back up again someday, when I have time...
(I then ran another daemonizer for a PHP bot to manage the game itself, then had my game connect to it as a java applet, and talk to the bot to play the game, but that's irrelevant here).
Since WaveIRCd is no longer maintained, it's probably worth having a hunt around to find if anyone else has forked the project and is supporting it.
[2012 edit: that said, if you want your front end to be HTML5/Javascript, or if you want to connect through the same port that HTTP connects through, then your options are more limited than when using Flash or Java. In that case, take the advice of others, and use "WebSockets" (poor support in most current browsers) or the "Socket.io" project (which uses WebSockets, but falls back to Flash, or various other methods, depending what the browser has available).
The above is for situations where your host allows you to run a service on another port. In particular, many have explicit rules in their ToS against running an IRCd.]
[2019 edit: WebSockets are now widely supported, you should be fine using them. As a relevant case study, Slack is written in PHP (per https://slack.engineering/taking-php-seriously-cf7a60065329), and for some time supported the IRC protocol, though I believe that that has since been retired. As its main protocol, it uses an API based on JSON over WebSockets (https://api.slack.com/rtm). This all shows that a PHP IRCd can deliver enterprise-level performance and quality, even where the IRC protocol is translated to/from another one, which you'd expect to give poorer performance.]
You need to use some kind of storage as a buffer. It IS plausable not to use file or db (which also uses a file). You can try using php's shared memory functions, but I don't know any working solution so you'll have to do it from scratch.
Is it possible to do it without using a persistent data storage like
database or file?
It is possible but you shouldn't use. Database or file based doesn't slows down chat. It will be giving additional security to your chat application. You can make web based chat using ajax and sockets without persistent data.
You should see following posts:
Is database based chat room bad idea?
Will polling from a SQL DB instead of a file for chat application increase performance?
Using memcached as a database buffer for chat messages
persistent data in php question
https://stackoverflow.com/questions/6569754/how-can-i-develop-social-network-chat-without-using-a-database-for-storing-the-c
File vs database for storage efficiency in chat app
PHP is not a good fit for your requirements (in a normal setup like apache-php, fastcgi etc.), because the PHP script gets executed from top to bottom for every request and cannot maintain any state between the requests without the use of external services or databases/files (Except e.g. http://php.net/manual/de/book.apc.php, but it is not intended for implementing a chat and will not scale to multiple servers.)
You should definitely look at Node.js and especially the Node.js module Socket.IO (A Websocket library). It's incredibly easy to use and rocks. Socket.IO can also scale to multiple chat servers with an optional redis backend, which means it's easier to scale.
Trying to use $_SESSION with a static session id as communication channel is not a solution by the way, because PHP saves the session data into files.
One solution to achieving this is by writing a PHP socket server.
<?php
// Set time limit to indefinite execution
set_time_limit (0);
// Set the ip and port we will listen on
$address = '192.168.0.100';
$port = 9000;
$max_clients = 10;
// Array that will hold client information
$clients = Array();
// Create a TCP Stream socket
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
// Bind the socket to an address/port
socket_bind($sock, $address, $port) or die('Could not bind to address');
// Start listening for connections
socket_listen($sock);
// Loop continuously
while (true) {
// Setup clients listen socket for reading
$read[0] = $sock;
for ($i = 0; $i < $max_clients; $i++)
{
if ($client[$i]['sock'] != null)
$read[$i + 1] = $client[$i]['sock'] ;
}
// Set up a blocking call to socket_select()
$ready = socket_select($read,null,null,null);
/* if a new connection is being made add it to the client array */
if (in_array($sock, $read)) {
for ($i = 0; $i < $max_clients; $i++)
{
if ($client[$i]['sock'] == null) {
$client[$i]['sock'] = socket_accept($sock);
break;
}
elseif ($i == $max_clients - 1)
print ("too many clients")
}
if (--$ready <= 0)
continue;
} // end if in_array
// If a client is trying to write - handle it now
for ($i = 0; $i < $max_clients; $i++) // for each client
{
if (in_array($client[$i]['sock'] , $read))
{
$input = socket_read($client[$i]['sock'] , 1024);
if ($input == null) {
// Zero length string meaning disconnected
unset($client[$i]);
}
$n = trim($input);
if ($input == 'exit') {
// requested disconnect
socket_close($client[$i]['sock']);
} elseif ($input) {
// strip white spaces and write back to user
$output = ereg_replace("[ \t\n\r]","",$input).chr(0);
socket_write($client[$i]['sock'],$output);
}
} else {
// Close the socket
socket_close($client[$i]['sock']);
unset($client[$i]);
}
}
} // end while
// Close the master sockets
socket_close($sock);
?>
You would execute this by running it through command line and would always have to run for your PHP clients to connect to it. You could then write a PHP client that would connect to the socket.
<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
You would have to use some type of ajax to call with jQuery posting the message to this PHP client.
http://devzone.zend.com/209/writing-socket-servers-in-php/
http://php.net/manual/en/function.fsockopen.php
Better use a node.js server for this. WebSockets aren't cross-browser nowadays (except socket.io for node.js that works perfect)
in short answer, you can't.
the current HTTP/HTML implementation doesn't support the pushstate so the algorithm of your chat app should follow :
A: sent message
B,C,D: do while a new message has been sent get this message.
so the receivers always have to make a new request and check if a new message has been sent. (AJAX Call or something similar )
so always there are a delay between the sent event and the receive event.
which means the data must be saved in something global, like db or file system.
take a look for :
http://today.java.net/article/2010/03/31/html5-server-push-technologies-part-1
You didn't say it had to all be written it PHP :)
Install RabbitMQ, and then use this chat implementation built on top of websockets and RabbitMQ.
Your PHP is pretty much just 'chat room chrome'. It's possible most of your site would fit within the 5 meg limit of offline HTML5 content, and you have a very flexible (and likely more robust than if you did it yourself) chat system.
It even has 20 messages of chat history if you leave the room.
https://github.com/videlalvaro/rabbitmq-chat
If You need to use just PHP, then You can store chat messages in session variables, session could be like object, storing a lot of information.
If You can use jQuery then You could just append paragraph to a div after message has been sent, but then if site is refreshed, messages will be gone.
Or combining, store messages in session and update that with jQuery and ajax.
Try looking into socket libraries like ZeroMQ they allow for instant transport of the message, and are quicker than TCP, and is realtime. Their infrastructure allows for instant data send between points A and B, without the data being stored anywhere first (although you can still choose to).
Here's a tutorial for a chat client in ZeroMQ

How to get my client to ping server?

I'm trying to figure a way where my clients can ping my server via php, and then retrieve the results into format like this "15 MS".
I ended up finding a way where servers can ping servers. However I want to be able to have the remote user somehow ping the server, or maybe have the server ping the client possibly?
function track($host, $port, $timeout) {
$firstTime = microtime(true);
$sock = fSockOpen($host, $port, $errno, $errstr, $timeout);
if (!$sock) {
echo "<b>Offline</b>";
}
$secondTime = microtime(true);
$ping = round((($secondTime - $firstTime) * 1000), 0);
echo $ping." ms";
}
track($_SERVER["REMOTE_ADDR"], 80, 10);
I tried this function, where I'd get the server to ping the client to see the response time between the client/server.
fsockopen() [function.fsockopen]: unable to connect to XXXXXXXXXXX:80
I'm trying to figure a way where my clients can ping my server via php
Why? What's the value in knowing this information? Although I noted that Emil's answer didn't address the question, it might address the problem - the time it takes for an ICMP packet to go to a server and come back will be different from the time taken to complete a TCP handshake on port 80 across the internet (they should be roughly the same on a LAN provided the webserver is not saturated).
If you want to get good information about RTT times, then a better solution would be to use a network monitoring tool / software. PastMon is an obvious candidate.
If you really must send a ping from the client, then you'd need to do this using a java applet / flash / activeX (assuming that these have the low-level TCP stack access required to carry out a ping).
C.
What makes you think clients are going to reliably respond to ICMP echo requests?
A better solution would be to write a client-side Java applet (or JavaScript?) to ping your server.
Remember, security restrictions will limit both languages to only communicate back to their origin server. You won't be able to allow your users to ping a server besides your own... but that doesn't appear to be an issue in your case.
You have to do this in JavaScript. The client cannot run PHP at all, and your attempt to ping the client will not work when their firewall blocks requests from the outside (which is often the case).
Build a "ping" function in JS which fetches a page a few times by AJAX. This example is built upon the jQuery framework:
Web page/JS
<script src="jquery-1.4.2.min.js"></script>
<script>
var global_runs = 0;
function ping(data) {
var op = "";
if(global_runs == 0) {
op = "start";
}else if(global_runs == 3){
op = "end";
}else if(global_runs > 3) {
return;
}
global_runs++;
$.post("ping.php", {op: op}, ping);
if(data != null) {
$("#time").text(data + " MS");
}
}
$(document).ready(function(){
//Start pinging
ping();
});
</script>
<p id="time">Time will be here</p>
ping.php
<?php
session_start();
if($_POST['op'] == 'start' || !isset($_SESSION['start'])) {
$_SESSION['start'] = microtime(true);
$_SESSION['runs'] = 0;
}else{
$_SESSION['runs']++;
$now = microtime(true);
$time = $now - $_SESSION['start'];
echo ($time / $_SESSION['runs']) * 1000; //milliseconds
}
?>
I don't really make use of the end op here, but it can be used to let the server now that this is the last ping request. Note that this code trusts the client, which means you may need some extra work on the security on the serverside (maybe take away the whole notion of "ops" from the serverside and ust send the pings?). Also, you probably want different counters for each page, maybe identified by some token that you return in the first request.
Also note that this is depending on HTTP and CPU latency as well, not just the network, but I think it's as good as you can get it.

Categories