I follow some tutorial (1), (2) on internet to create a PHP socket server, and it works quite well. There is only thing is my PHP script takes a lot of CPU usage (99%).
I do not show code here, because my code is similar to the above tutorials.
The main action is written in an infinitive loop
while (true)
{
$this->selectChangedSockets();
$this->handleNewClients();
$this->receiveInput();
$this->checkDisconnections();
}
The functions inside the my loop are just to handle inputs (new client's connection, client's message, etc.), and handle outputs (send back to the client's message, etc.)
My php script is always on top in CPU usage (99%). I read this, and they suggest to put "sleep(1)" in the loop. My socket server is for the real time applications, and I am not sure this is a good way to go?
Anyone who has experience with php socket can give me any suggestion?
Is there any standard php socket server library?
or do I need to run the script in the background?
Updated
Because you mention the function socket_select may pause a little bit, I give you my code:
private function selectChangedSockets()
{
// Reset array of changed sockets
$this->changed = array_merge(array($this->socket), $this->clients);
$null = null;
$res = socket_select($this->changed, $null, $null, 0);
if (false === $res)
{
echo "socket_select() failed, reason: " .
socket_strerror(socket_last_error()) . "\n";
}
}
public function run()
{
while (true) {
$this->selectChangedSockets();
//$this->handleNewClients();
//$this->receiveInput();
//$this->checkDisconnections();
}
}
My PHP script still takes 99% CPU usage.
Your selectChangedSockets method should be blocking, meaning, not return until there are changes on the sockets. See socket_select.
A while ( true ) {} will use 100% CPU time. You will need some kind of delay.
Since you can use socket_select, you can specify a timeout there, using 0% CPU time until there is actually some work to be done:
function selectChangedSockets() {
$read = array($socket1, $socket2); // array of your sockets
$write = NULL;
$except = NULL;
$num_changed_sockets = socket_select($read, $write, $except, NULL);
}
This will wait until one of the sockets in $read has data available. For a server socket, this happens when a new connection is established by a client. For a connected socket, this happens when the client has sent data.
Again, see socket_select for details and examples. Especially the first user contributed note, which uses a while (true), handles new connections, and reads data from clients.
Note however, from the socket_select page, about the 4th parameter:
tv_sec may be zero , causing socket_select() to return immediately. This is useful for polling. If tv_sec is NULL (no timeout), socket_select() can block indefinitely.
My solution is:
if socket server idle 5 second (not receive data), idle mode, relaxes CPU,
<?php
if (!($sock = socket_create(AF_INET, SOCK_DGRAM, 0))) {
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("Couldn't create socket: [$errorcode] $errormsg \n");
}
$server_idle=time();
while (1) {
$buf="";
$r = socket_recvfrom($sock, $buf, 512, MSG_DONTWAIT, $remote_ip, $remote_port);
if ($buf) {
$server_idle=time();
//socket process codes
//.
//.
}
if (time()-$server_idle>5) { // if Server idle 5 second; IDLE Mode!
usleep(100); // IDLE Mode
} else {
// usleep(1); // No IDLE Mode usleep(1) milisecond less CPU process
}
}
?>
Related
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);
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).
}
As a matter of fact I have a proc_open function which will execute a command and pipe it's sub-commands to it. There is a problem here which will occur sometimes and not usually.
Problem
When I want to get process piped out output there is no response from destination server and script will wait for response up to .....
Question
I want to say that wait for response for 20 sec and if you didn't get any response. return from function. This is important that I don't want to stop script execution but just return from function.
Am I have to use multi thread libraries as POSIX?
Is there any way to implement this idea?
Any idea will be appreciated. Thanks in advance
Code Sample
<?PHP
set_time_limit(0);
.....
public function test()
{
foreach ($this->commands as $cmd)
{
fwrite($pipes[0], "$cmd \n");
//Sometimes PHP stuck on the following line
//Wait for almost 20 sec if respond did not came return
$read = fread($pipes[1], 2096) . "\n";
}
}
You didn't say what OS this is running on, though you mention POSIX. Nor which SAPI. Collectively these have a lot of impact on your options.
Also, you've not said what the process you have started is, nor how it should be treated when it times out. Closing the stdio streams may force the process to crash. If you want to send a signal, you'll need to know the pid (IIRC not available from proc_open()).
The immediate problem you have is that the stream, by default is blocking reads, hence simply setting it to non-blocking gets you past the first hurdle:
set_stream_blocking($pipes[1],false);
...
fwrite($pipes[0], "$cmd \n");
$ttl=time()+20;
while(''==$read && time()<$ttl) {
$read=fread($pipes[1],2096);
if (!$read) sleep(1);
}
if (!$read) echo " timeout";
(BTW this probably won't work on mswindows)
Use stream_set_timeout (but after the fwrite if same stream):
fwrite($pipes[0], "$cmd \n");
stream_set_timeout($pipes[1], 20);
$read = fread($pipes[1], 2096) . "\n";
edit:
In some special cases you must use stream_select() instead, do to bugs in PHP.
$r = [$pipes[0]];
$w = $e = null;
if( stream_select($r, $w, $e, 20) !== false )
foreach($r as $s)
$read = fread($s,2096);
I hope this helps?
:)
I am trying to receive data from a c# realtime application in my php server and then move a picture in the browser according to the data.
There is no problem in data send and receive, but the memory usage for chrome is getteng more and more when running the code.
If I close the socket inside the while loop, performance gets very low but mem usage gets normal.So this is about the open socket...
here is the php code :
<?php
//http://www.binarytides.com/udp-socket-programming-in-php/
//Create a UDP socket
if(!($sock = socket_create(AF_INET, SOCK_DGRAM, 0)))
{
die("Couldn't create socket: [$errorcode] $errormsg \n");
}
echo "Socket created \n";
// Bind the source address
if( !socket_bind($sock, "0.0.0.0" , 41181) )
{
die("Could not bind socket : [$errorcode] $errormsg \n");
}
echo "Socket bind OK \n";
//Do some communication, this loop can handle multiple clients
while(1)
{
//echo "Waiting for data ... \n";
$r = socket_recvfrom($sock, $buf, 20, 0, $remote_ip, $remote_port);
?>
<script type="text/javascript">
var data = "<?php echo $buf ?>";
</script>
<?php
}
socket_close($sock);
?>
and here is the c# function (data sender):
public static void SendUDP(string hostNameOrAddress, int destinationPort, string data, int count)
{
//class member : Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Dgram, ProtocolType.Udp);
//socket is defined as class member and used here
for (int i = 0; i < count; i++)
{
socket.SendTo(buffer, endPoint);
}
}
Thanks ! :)
The technique you're using is called "long polling". It's a funny way to emulate bidirectional communication, especially with old browsers, but has its downsides.
The problem is that, with time, you are sending a huge amount of payload to the browser.
As you're continuously writing to the browser, the page size increases, and, with it, the DOM tree. All of this has to be stored in memory. I'd assume that when you send only a few of the script chunks, the performance is still ok. But thousands and ten thousands of them will of course eat up your memory.
Also, if you're using diagnostic tools, such as the Chrome Developer Tools or Firebug in Firefox, they store a lot of debugging information, which also consume a lot of memory. (Try disabling them.)
If you've written this code just for fun and experimenting, you shouldn't worry about the memory consumption; it's inherent to long polling.
But if you're trying to write a web application with a real bi-directional communication, you should use something like Web Sockets (and maybe a different language than PHP on the server side).
This is not a real answer to my question, but my experience may help someone.
I couldn't do this "long polling" with a PHP server, and it was the fault of PHP, not the browser.
I have developed a good application working this way using a Node.js server.
My C# app is used to process images from a camera. The real-time data resulting the image processing is sent over to the Node.js server and from the server to the browser .
Now I use the web technologies to develop a graphical user interface that was very hard to achieve in .Net, and it has many other benefits...
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.