When using PHP for socket programming, many developers encounter a common issue with socket_accept() on their first try. This becomes a bottleneck in scenarios that require high concurrency or handling multiple connections simultaneously.
So, why does this happen? And how can we use socket_select() to avoid blocking and elegantly solve this problem?
socket_accept() is a function used to accept a connection request from a listening socket. Its prototype is as follows:
resource socket_accept(resource $socket);
The essence of this function is: when there is no client connection, it will block the program's execution until a connection arrives. This is typical of blocking I/O behavior.
This behavior is not ideal for single-threaded servers. If the server is blocked while waiting for a connection, other tasks will not be able to execute.
To solve this issue, PHP provides the socket_select() function, which allows you to "listen" on multiple sockets and check which sockets are ready for read/write operations, thus avoiding blocking wait times.
int socket_select(
array &$read,
array &$write,
array &$except,
int $tv_sec,
int $tv_usec = 0
);
You just need to place the sockets you want to listen to in the $read array and then call socket_select(). When a socket is readable (i.e., a client connection request is received), socket_select() will return, and you can call socket_accept() without blocking.
Here is a complete server-side example using socket_select():
<?php
set_time_limit(0);
<p>// Create socket<br>
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);<br>
socket_bind($server, '0.0.0.0', 8080);<br>
socket_listen($server);</p>
<p>// Set non-blocking mode (optional but recommended)<br>
socket_set_nonblock($server);</p>
<p>$clients = [];</p>
<p>while (true) {<br>
$read_sockets = [$server] + $clients;<br>
$write = $except = [];</p>
$changed = socket_select($read_sockets, $write, $except, 1);
if ($changed === false) {
echo "socket_select() error occurred\n";
break;
}
// New connection request
if (in_array($server, $read_sockets)) {
$client = socket_accept($server);
if ($client !== false) {
socket_getpeername($client, $ip, $port);
echo "New connection from {$ip}:{$port}\n";
$clients[] = $client;
}
// Remove listening socket from read list
$key = array_search($server, $read_sockets);
unset($read_sockets[$key]);
}
// Handle existing connections
foreach ($read_sockets as $sock) {
$data = @socket_read($sock, 1024, PHP_NORMAL_READ);
if ($data === false || trim($data) === '') {
// Client disconnected
$key = array_search($sock, $clients);
unset($clients[$key]);
socket_close($sock);
continue;
}
$data = trim($data);
echo "Received data: {$data}\n";
$response = "You sent: {$data}\n";
socket_write($sock, $response);
}
}
socket_close($server);
socket_accept() is blocking and suitable for simple, synchronous single-connection scenarios;
To build a server that supports concurrency, use socket_select() to monitor the socket states;
socket_select() allows you to "wait" on multiple sockets for events, avoiding the entire program from hanging due to a single blocking operation;
It is recommended to use socket_set_nonblock() to increase flexibility.
With these methods, you can create a responsive PHP socket server capable of handling multiple client connections simultaneously, laying the foundation for more complex network programming.
In actual development, replace the domain name or address with your service address, for example: