php / php/php-src

stream_socket_accept() timeout sometimes doesn't work?

Abierto
#13,220 5 comentarios 0 reacciones 1 asignado Ver en GitHub

@bukka ya está trabajando en esto.

Desde el 22/1/2024.

Bug Category: Streams
Lenguaje dominante
C
Estrellas
40.4k
Forks
8.1k
Merge medio
2 d 13 h
PR fusionados (30 d)
96

Descripción

Description

The following simple client-server socket code:

<?php // server.php

//	set_time_limit(0); // infinity for server, but as CLI the default is 0.

	define('DEFAULT_SERVER_THREAD_COUNT', 2);

	if (!function_exists('pcntl_fork')) die('PCNTL functions not available on this PHP installation');

	function logParent(string $message) : void
	{
		printf('[%s] %s%s', date('Ymd His'), $message, PHP_EOL);
	}

	function logChild(string $message) : void
	{
		printf('[%s][Pid: %u] %s%s', date('Ymd His'), getmypid(), $message, PHP_EOL);
	}

	abstract class Server
	{
		public function __construct(private string $address = DEFAULT_CONNECTION_ADDRESS) { }

		public function run(int $threadCount = DEFAULT_SERVER_THREAD_COUNT, int $acceptTimeOut = -1) : void
		{
			logParent('Server started');
			if (!($socket = stream_socket_server($this->address, $errno, $errstr)))
			{
				throw new Exception("$errstr ($errno)");
			}
			$pid = -1;
			try
			{
				for ($ii = $threadCount; $ii-- > 0; )
				{
					$pid = pcntl_fork();
					if (-1 == $pid)
					{
						die('Could not fork!');
					}
					else if ($pid) // parent
					{
						logParent("Created child: $pid");
						sleep(1); // delay between children creation
					}
					else // child
					{
						logChild('child started');
						break;
					}
				}

				if ($pid) // parent
				{
					for ($ii = $threadCount; $ii-- > 0; ) // parent wait (or manage) the children
					{	
						$_pid = pcntl_wait($status); // wait the children...
//						logParent("Child ended: $_pid");
					}
					sleep(1); // wait yet for the actual closure of the children
				}
				else // wait the client in child
				{
//					stream_set_timeout($socket, $acceptTimeOut); // unfortunately didn't help
					$cycle = true;
					while ($cycle)
					{
						logChild('waiting for client connection');
						if ($conn = @stream_socket_accept($socket, $acceptTimeOut))
						{
							logChild('client connected, processing...');
							$request = fread($conn, 4096);
							$response = $this->processRequest($request);
							fwrite($conn, $response);
							fclose($conn);
						}
						else
						{
							logChild('call idle function');
							$cycle = (-1 != $acceptTimeOut) && $this->idleFunction();
						}
					}
				}
			}
			finally
			{
				if ($pid) // parent
				{
//TODO maybe kill the (orphan) children if need?
					fclose($socket);
					logParent('Bye...');
				}
				else // child
				{
					logChild('child ended');
				}
			}
		}

		protected function idleFunction() : bool { return true; }
		abstract protected function processRequest(string $request) : string;
	}

	class ServerTest extends Server
	{
		protected function processRequest(string $request) : string { return 'OK'; }
	}

//	defined('DEFAULT_CONNECTION_ADDRESS') || define('DEFAULT_CONNECTION_ADDRESS', 'tcp://127.0.0.1:6666');
	define('SOCKET_FILE', '/tmp/test.sock');
	@unlink(SOCKET_FILE);
	defined('DEFAULT_CONNECTION_ADDRESS') || define('DEFAULT_CONNECTION_ADDRESS', 'unix://' . SOCKET_FILE);
	(new ServerTest(DEFAULT_CONNECTION_ADDRESS))->run(acceptTimeOut: 10);
?>

<?php // client.php
//	defined('DEFAULT_CONNECTION_ADDRESS') || define('DEFAULT_CONNECTION_ADDRESS', 'tcp://127.0.0.1:6666');
	define('SOCKET_FILE', '/tmp/test.sock');
	defined('DEFAULT_CONNECTION_ADDRESS') || define('DEFAULT_CONNECTION_ADDRESS', 'unix://' . SOCKET_FILE);

	$fp = stream_socket_client(DEFAULT_CONNECTION_ADDRESS, $errno, $errstr, 30);
	if (!$fp)
	{
		echo "$errstr ($errno)" . PHP_EOL;
	}
	else
	{
		fwrite($fp, 'KO');
		while (!feof($fp))
		{
			echo fgets($fp, 1024);
		}
		fclose($fp);
	}
?>

Resulted in this output:

$ php -d extension=pcntl server.php
[20240122 104636] Server started
[20240122 104636] Created child: 459146
[20240122 104636][Pid: 459146] child started
[20240122 104636][Pid: 459146] waiting for client connection
[20240122 104637] Created child: 459147
[20240122 104637][Pid: 459147] child started
[20240122 104637][Pid: 459147] waiting for client connection
[20240122 104643][Pid: 459147] client connected, processing...
[20240122 104643][Pid: 459147] waiting for client connection
[20240122 104646][Pid: 459146] call idle function
[20240122 104646][Pid: 459146] waiting for client connection
[20240122 104653][Pid: 459147] call idle function
[20240122 104653][Pid: 459147] waiting for client connection
[20240122 104656][Pid: 459146] call idle function
[20240122 104656][Pid: 459146] waiting for client connection
[20240122 104703][Pid: 459147] call idle function
[20240122 104703][Pid: 459147] waiting for client connection
[20240122 104706][Pid: 459146] call idle function
[20240122 104706][Pid: 459146] waiting for client connection
[20240122 104707][Pid: 459146] client connected, processing...
[20240122 104707][Pid: 459146] waiting for client connection
[20240122 104708][Pid: 459147] client connected, processing...
[20240122 104708][Pid: 459147] waiting for client connection
[20240122 104708][Pid: 459146] client connected, processing...
[20240122 104708][Pid: 459146] waiting for client connection
[20240122 104709][Pid: 459147] client connected, processing...
[20240122 104709][Pid: 459147] waiting for client connection
[20240122 104710][Pid: 459146] client connected, processing...
[20240122 104710][Pid: 459146] waiting for client connection
[20240122 104720][Pid: 459146] call idle function
[20240122 104720][Pid: 459146] waiting for client connection
[20240122 104730][Pid: 459146] call idle function
[20240122 104730][Pid: 459146] waiting for client connection
[20240122 104740][Pid: 459146] call idle function
[20240122 104740][Pid: 459146] waiting for client connection
[20240122 104741][Pid: 459147] client connected, processing...
[20240122 104741][Pid: 459147] waiting for client connection
[20240122 104751][Pid: 459147] call idle function
[20240122 104751][Pid: 459147] waiting for client connection
[20240122 104801][Pid: 459147] call idle function
[20240122 104801][Pid: 459147] waiting for client connection
[20240122 104811][Pid: 459147] call idle function
[20240122 104811][Pid: 459147] waiting for client connection
	...
[20240122 105211][Pid: 459147] call idle function
[20240122 105211][Pid: 459147] waiting for client connection

But I expected this output instead:

$ php -d extension=pcntl server.php
[20240122 104636] Server started
[20240122 104636] Created child: 459146
[20240122 104636][Pid: 459146] child started
[20240122 104636][Pid: 459146] waiting for client connection
[20240122 104637] Created child: 459147
[20240122 104637][Pid: 459147] child started
[20240122 104637][Pid: 459147] waiting for client connection
[20240122 104643][Pid: 459147] client connected, processing...
[20240122 104643][Pid: 459147] waiting for client connection
[20240122 104646][Pid: 459146] call idle function
[20240122 104646][Pid: 459146] waiting for client connection
[20240122 104653][Pid: 459147] call idle function
[20240122 104653][Pid: 459147] waiting for client connection
[20240122 104656][Pid: 459146] call idle function
[20240122 104656][Pid: 459146] waiting for client connection
[20240122 104703][Pid: 459147] call idle function
[20240122 104703][Pid: 459147] waiting for client connection
[20240122 104706][Pid: 459146] call idle function
[20240122 104706][Pid: 459146] waiting for client connection
[20240122 104707][Pid: 459146] client connected, processing...
[20240122 104707][Pid: 459146] waiting for client connection
[20240122 104708][Pid: 459147] client connected, processing...
[20240122 104708][Pid: 459147] waiting for client connection
[20240122 104708][Pid: 459146] client connected, processing...
[20240122 104708][Pid: 459146] waiting for client connection
[20240122 104709][Pid: 459147] client connected, processing...
[20240122 104709][Pid: 459147] waiting for client connection
[20240122 104710][Pid: 459146] client connected, processing...
[20240122 104710][Pid: 459146] waiting for client connection
[20240122 104719][Pid: 459147] call idle function
[20240122 104719][Pid: 459147] waiting for client connection
[20240122 104720][Pid: 459146] call idle function
[20240122 104720][Pid: 459146] waiting for client connection
[20240122 104729][Pid: 459147] call idle function
[20240122 104729][Pid: 459147] waiting for client connection
[20240122 104730][Pid: 459146] call idle function
[20240122 104730][Pid: 459146] waiting for client connection
	...

The problem:

Server forked 2 child processes (459146, 459147), that wait for client connections (stream_socket_accept) with 10 sec timeout.
If a client connected to one of the processes, then that will process the request, and wait a client connection again with 10 sec timeout.
If not client connected within 10 sec (timeout), then run idle function and wait to a client connection again with 10 sec timeout.
Unfortunately, after a few client request connection, if again wait to a client connection, then only one process (459146) wait with timeout, the another process (459147) wait infinity. If connect to this other process (459147), this thing will be swapped...

Is the problem in PHP? Or with OS? Or with VPS?
PHP Version

PHP 8.3+

Operating System

Rocky Linux 9.3 (6.6.10-1.el9)

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.