This Socket Server should handle about 5000 lines of log file entries per second from at least 15 machines from same network. Any tips to further optimize this script or are there any big mistakes?
class Server {
private $callback;
private $clients;
private $socket;
public function __construct($ip, $port) {
$this->hooks = array();
$this->clients = array();
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($this->socket, $ip, $port);
socket_listen($this->socket);
}
public function setCallback($function) {
$this->callback = $function;
}
public function loop_once() {
$read = array( $this->socket );
$write = NULL;
$except = NULL;
foreach ($this->clients as $index => &$client) {
$read[] = &$client->socket;
}
if (socket_select($read, $write, $except, 1) < 1) {
return true;
}
if (in_array($this->socket, $read)) {
$this->clients[] = new Client($this->socket, 0);
}
foreach ($this->clients as $index => &$client) {
if (in_array($client->socket, $read)) {
$input = socket_read($client->socket, 1024, PHP_NORMAL_READ);
if ($input === false) {
$client->destroy();
unset($this->clients[$index]);
} else {
if (isset($this->callback)) {
call_user_func($this->callback, $input);
}
}
}
}
$this->clients = array_values($this->clients);
return true;
}
}
class Client {
public $socket;
public function __construct(&$socket, $i) {
$this->socket = socket_accept($socket);
}
public function destroy() {
socket_close($this->socket);
}
}
-
\$\begingroup\$ * removed @-operator from socket_select and socket_read \$\endgroup\$Tobias Herkula– Tobias Herkula2012年02月23日 11:19:20 +00:00Commented Feb 23, 2012 at 11:19
-
\$\begingroup\$ * added socket_set_option for reuse of address \$\endgroup\$Tobias Herkula– Tobias Herkula2012年02月23日 11:20:27 +00:00Commented Feb 23, 2012 at 11:20
3 Answers 3
Try dropping the reference usage. Objects are copied by reference already:
<?php
$a = (object)array('one' => 1, 'two' => 2);
$b = $a;
$b->three = 3;
print_r($a);
print_r($b);
?>
Output:
stdClass Object
(
[one] => 1
[two] => 2
[three] => 3
)
stdClass Object
(
[one] => 1
[two] => 2
[three] => 3
)
However, arrays are copied by value (using copy-on-write):
<?php
$a = array('one' => 1, 'two' => 2);
$b = $a;
$b['three'] = 3;
print_r($a);
print_r($b);
?>
Output:
Array
(
[one] => 1
[two] => 2
)
Array
(
[one] => 1
[two] => 2
[three] => 3
)
Since Client
is an object type, you should be able to replace:
foreach ($this->clients as $index => &$client)
with:
foreach ($this->clients as $index => $client)
Actually, "objects are copied by reference" isn't entirely true. An "object" in PHP is just a handle referring to a shared entity, making it more like a reference in Java or a pointer in C. This is not true for arrays, where modification is confined to the variable upon which it happens.
References in PHP are one level above the notion of object handle. When you modify a reference, you are changing what the referent variable names. For example:
$obj = (object)array('one' => 1, 'two' => 2);
$a = $obj;
$a->three = 'three';
Now $obj
and $a
point to the same object, but they are distinct variables. Now let's do something funky with references:
$b =& $a;
$b = null;
This actually sets both $a
and $b
to null
, but leaves $obj
intact.
Confused? Good.
For more information about references in PHP, see:
-
\$\begingroup\$ $this->clients is an array and only the content are objects, but I see your point. \$\endgroup\$Tobias Herkula– Tobias Herkula2012年05月02日 12:02:10 +00:00Commented May 2, 2012 at 12:02
Why are you suppressing socket_select
errors - I'd suggest at least catching any errors.
-
\$\begingroup\$ Had problems with
PHP Warning: socket_select(): unable to select [4]: Interrupted system call in
because of the older use ofdeclare(ticks = 1);
changed that now to a call topcntl_signal_dispatch();
in each loop to get all signaling handled properly. \$\endgroup\$Tobias Herkula– Tobias Herkula2012年02月23日 11:13:37 +00:00Commented Feb 23, 2012 at 11:13 -
\$\begingroup\$ Ah I see, makes sense \$\endgroup\$BenOfTheNorth– BenOfTheNorth2012年02月23日 11:21:33 +00:00Commented Feb 23, 2012 at 11:21
The code looks good (a few comments would have helped me understand it quicker though).
I have only very minor things to comment on:
$index
is not used in the firstforeach
. I believe this has a small impact on performance.else { if () {} }
is equivalent toelseif { }
- Client constructor does not use
$i