Reputation: 1037
I'm writing a websocket server in PHP, that needs to be able to handle a large number of concurrent connections. I'm currently using the socket_select function to allow it to handle them, but this still blocks all other connections when sending a large block of data to a client. Is there a way for the master script to accept the incoming socket, and then start up a second PHP script (in a non-blocking fashion, obviously) and pass the client socket to that script for processing? I know this is possible in C, but the codebase is such that a migration is impossible, sadly.
*The server is running exclusively on a Unix stack, no need for a MS compatible solution.
Upvotes: 1
Views: 1333
Reputation: 3106
I'm currently using the socket_select function to allow it to handle them, but this still blocks all other connections when sending a large block of data to a client.
Then don't send all the data at once. If you are going to do
socket_write ($mysocket, $mybuffer, 10000000);
then yeah, you'll have to wait until all 10 million bytes have been sent out. However, you can use the $write
array of socket_select
to check if you can write to the socket, in combination with non-blocking sockets. Each time socket_select
says you have a 'go!' on the socket, write data until socket_write
starts to complain (i.e. returns FALSE or less than the specified length). This will keep the socket's send buffer optimally filled.
The downside is that you must keep track of exactly where in your output buffer you are; also, turn off non-blocking on the socket after you've written al your data or socket_select
will keep on firing (this assumes you want to send multiple large blobs of data).
Upvotes: 1
Reputation: 1037
The answer turns out to be the same answer you'd use in C - fork(). When you fork, the state of all open files and ports is preserved, so a child process can read a port that was opened by its parent (this is the same way that modern webservers spin off worker threads for each client connection that comes in) It does require using the pcntl (process control) module which is disabled by default and should be used sparingly, but it works:
if($verbose)
echo "Connected Client from $remoteaddy to $localaddy\n";
echo "Forking...";
$pid = pcntl_fork(); // you're bringing children into this world, just to kill them in a few seconds. You monster.
if($pid==0){
$p = posix.getpid();
echo "PID OF CHILD: $p\n";
//in child process. Send a handshake and wait for the callback from the WebSockets library
$this->send($client, "Thank you for contacting myAwesomeServer.com! I'm slave #{$p}, and I'll be your host today");
}else if($pid>0){
$childWorkers[]=$pid;
echo "[ OK ]\n";
$this->disconnect($client->socket, false); //disconnect the clients socket from the master thread, so only the child thread is talking to the client
}else if($pid==-1){
echo "[FAIL] unable to create child worker\n";
}
NOTE!! This approach is PURELY ACADEMIC, and should only be used on small, 'pet' projects when you don't have enough time to learn a more appropriate language (personally, I know C well enough to fork(), but my lack of knowledge of its string manipulation functions would no doubt leave a gaping security hole in the server). I'm not sure how the Zend engine is doing this pcntl_fork(), but I'd imagine that the memory image of this monstrosity is going to be many times the size of equivalent C code..
Upvotes: 0