php / php/php-src

stream_socket_accept() timeout sometimes doesn't work?

Aperta
#13,220 5 commenti 0 reazioni 1 assegnatario Vedi su GitHub

@bukka ci sta già lavorando.

Dal 22/1/2024.

Bug Category: Streams
Lingua principale
C
Stelle
40.4k
Fork
8.1k
Merge medio
2g 13h
PR unite (30g)
96

Descrizione

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)

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.