Función PHP socket_set_blocking(): Todo lo que necesitas saber
Aprende a controlar el modo de bloqueo de sockets en PHP con socket_set_blocking(), su deprecación en PHP 8.1 y las alternativas correctas.
Cuando escribes código de red en PHP, a menudo necesitas controlar si una llamada de E/S espera a que lleguen datos (bloqueante) o regresa inmediatamente (no bloqueante). La función socket_set_blocking() fue el alias histórico para establecer este modo. Fue deprecada en PHP 8.1 y no debes usarla en código nuevo.
El problema es que el reemplazo correcto depende del tipo de conexión que tengas, y aquí es donde muchas guías se equivocan:
- Si usaste la extensión Sockets (
socket_create(), que devuelve un objetoSocket), usasocket_set_nonblock()ysocket_set_block(). - Si usaste un stream (
fsockopen()ostream_socket_client(), que devuelven un recurso de stream), usastream_set_blocking().
socket_set_blocking() era en realidad un alias de stream_set_blocking(), por lo que solo funcionaba con recursos de stream — nunca con objetos Socket. Esa distinción es clave para evitar un TypeError en tiempo de ejecución.
Modo Bloqueante vs. No Bloqueante
En modo bloqueante (el predeterminado), una llamada de lectura o escritura pausa la ejecución del script hasta que la operación puede completarse. Un socket_read() sobre un socket vacío simplemente espera hasta que lleguen bytes.
En modo no bloqueante, la misma llamada regresa de inmediato. Si no hay datos listos, devuelve un resultado vacío (o false) en lugar de esperar. Esto permite que un solo script maneje muchas conexiones o permanezca reactivo — a costa de tener que hacer polling y verificar si los datos realmente llegaron.
| Usa el modo bloqueante cuando… | Usa el modo no bloqueante cuando… |
|---|---|
| Manejas una conexión a la vez | Atiendes muchos clientes en un solo bucle |
| La simplicidad importa más que el rendimiento | El script debe permanecer reactivo |
| Una solicitud/respuesta corta y predecible | Implementas tu propio bucle de polling/eventos |
Establecer el Modo en un Objeto Socket
Si creaste la conexión con la extensión Sockets, usa socket_set_nonblock() y socket_set_block():
socket_set_nonblock(Socket $socket): bool
socket_set_block(Socket $socket): boolAmbas devuelven true en caso de éxito y false en caso de fallo. Un ejemplo completo y ejecutable con manejo de errores y limpieza:
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
die("Socket creation failed: " . socket_strerror(socket_last_error()) . "\n");
}
// Switch to non-blocking mode
if (!socket_set_nonblock($socket)) {
die("Failed to set non-blocking mode\n");
}
echo "Socket is now non-blocking.\n";
// ... perform non-blocking socket operations ...
// Switch back to blocking mode if needed
socket_set_block($socket);
echo "Socket is now blocking again.\n";
socket_close($socket);No llames a
stream_set_blocking($socket, false)sobre un objetoSocket—socket_create()devuelve una instancia deSocket, ystream_set_blocking()solo acepta un recurso de stream. Pasar unSocketlanza unTypeError.
Establecer el Modo en un Stream
Si abriste la conexión con fsockopen() o stream_socket_client(), tienes un recurso de stream y debes usar stream_set_blocking():
stream_set_blocking(resource $stream, bool $enable): bool$stream: el recurso de stream a configurar.$enable:truepara modo bloqueante,falsepara modo no bloqueante.
<?php
$stream = stream_socket_client('tcp://example.com:80', $errno, $errstr, 5);
if ($stream === false) {
die("Connect failed: $errstr ($errno)\n");
}
// Switch the stream to non-blocking mode
if (!stream_set_blocking($stream, false)) {
die("Failed to set non-blocking mode\n");
}
echo "Stream is now non-blocking.\n";
fclose($stream);Un Bucle de Lectura No Bloqueante
El modo no bloqueante solo es útil si haces polling. Un patrón típico envía una solicitud y luego verifica repetidamente si hay respuesta sin bloquear el script:
<?php
$stream = stream_socket_client('tcp://example.com:80', $errno, $errstr, 5);
if ($stream === false) {
die("Connect failed: $errstr ($errno)\n");
}
stream_set_blocking($stream, false);
fwrite($stream, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
$response = '';
$start = time();
while (!feof($stream) && time() - $start < 5) {
$chunk = fread($stream, 8192);
if ($chunk === '' || $chunk === false) {
// No data yet — do other work or wait briefly
usleep(50000); // 50 ms
continue;
}
$response .= $chunk;
}
echo substr($response, 0, 15), "\n"; // e.g. "HTTP/1.1 200 OK"
fclose($stream);La llamada a usleep() evita que un bucle ocupado consuma la CPU de manera continua. En producción normalmente reemplazarías este polling manual con stream_select() para esperar eficientemente en múltiples streams a la vez.
Notas de Versión
- PHP 8.1:
socket_set_blocking()está deprecada. Al llamarla se emite un aviso de deprecación. - La función era un alias de
stream_set_blocking(), por lo que nunca aceptó objetosSocket. - Para objetos
Socket,socket_set_nonblock()/socket_set_block()siempre han sido las llamadas correctas y siguen siendo compatibles.
Funciones Relacionadas
socket_get_status()— inspecciona el estado de un socket, incluyendo si está bloqueado.socket_set_timeout()— controla cuánto tiempo espera una operación bloqueante antes de rendirse.- PHP Streams — la API de streams más amplia a la que pertenece
stream_set_blocking().
Conclusión
Controlar el modo de bloqueo de una conexión es esencial para construir aplicaciones de red PHP reactivas. La clave es hacer coincidir la función con el tipo de conexión: usa socket_set_nonblock() / socket_set_block() para objetos Socket, y stream_set_blocking() para recursos de stream. Evita la función deprecada socket_set_blocking() en código nuevo, y recuerda que el modo no bloqueante solo da sus frutos cuando lo combinas con un bucle de polling.