When building a Socket-based server application using PHP, socket_accept() is one of the very core functions that accepts connection requests from the client. However, if the connection management is not handled properly, it is easy to cause resource leakage and even abnormal service termination. This article will explore how these connections can be made to ensure that resources can be released in time and to maintain the stability and reliability of services.
Before understanding elegant shutdown, look at a basic Socket server example:
$address = '0.0.0.0';
$port = 12345;
$serverSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($serverSocket, $address, $port);
socket_listen($serverSocket);
while (true) {
$clientSocket = socket_accept($serverSocket);
// Simple response
socket_write($clientSocket, "Hello client!\n");
socket_close($clientSocket);
}
In this example, the server continues to run, receives the connection and responds. Although the code can run, if the connection is closed and the exception is handled unreasonably during high concurrency or long-term running, the following problems may occur:
File descriptor leak (exceeding system resource limits)
The connection is not closed, causing TIME_WAIT to accumulate
The process is forced to terminate (if encountered SIGINT)
PHP's socket_accept() is a blocking function , meaning it will wait for no connection request. This behavior prevents normal shutdown of the process when exiting the service, restarting or terminating. If the resource is not released correctly, it may result in:
socket_close() not executed : the connection is not closed, the system resources are not released.
The script exits abnormally : causes the created connection to be not cleaned, leaving behind a zombie socket.
Signal interrupt (such as Ctrl+C): No aftermath is processed.
Although the socket_* series provides lower-level control, PHP's stream_socket_server() is easier to implement timeout control and non-blocking behavior, suitable for simplifying code.
However, if you insist on using socket_* , the following methods can also achieve elegant closure.
Socket_set_nonblock() can be avoided from blocking socket_accept() :
socket_set_nonblock($serverSocket);
In this way, we can use a loop to check whether there is a connection and listen for the termination signal:
declare(ticks = 1);
$running = true;
pcntl_signal(SIGINT, function() use (&$running) {
echo "capture SIGINT,Prepare to exit...\n";
$running = false;
});
while ($running) {
$clientSocket = @socket_accept($serverSocket);
if ($clientSocket !== false) {
socket_write($clientSocket, "Hello client!\n");
socket_close($clientSocket);
} else {
usleep(100000); // avoid CPU Too high occupancy
}
}
socket_close($serverSocket);
If you need to listen on multiple sockets, you can combine socket_select() to implement the event-driven model:
$readSockets = [$serverSocket];
$write = $except = null;
if (socket_select($readSockets, $write, $except, 1) > 0) {
foreach ($readSockets as $sock) {
if ($sock === $serverSocket) {
$clientSocket = socket_accept($serverSocket);
if ($clientSocket !== false) {
socket_write($clientSocket, "Hi\n");
socket_close($clientSocket);
}
}
}
}
socket_select() can set a timeout or respond to interrupt signals in time, which is suitable for implementing more robust server programs.
Make sure that each resource obtained by socket_create() and socket_accept() is paired and executed socket_close() . A well-structured socket service life cycle is as follows:
Initialize socket
Set non-blocking/select
Receive connection in the main loop and process requests
Capture the termination signal and exit the loop
Clean up all open connections
Finally, close the listening socket
Here is a complete example that implements signal capture, non-blocking mode, and resource cleaning:
declare(ticks = 1);
$serverSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($serverSocket, '0.0.0.0', 8000);
socket_listen($serverSocket);
socket_set_nonblock($serverSocket);
$running = true;
pcntl_signal(SIGINT, function() use (&$running) {
echo "Received a termination signal,Prepare to close the service...\n";
$running = false;
});
while ($running) {
$clientSocket = @socket_accept($serverSocket);
if ($clientSocket) {
socket_write($clientSocket, "Welcome to visit m66.net!\n");
socket_close($clientSocket);
} else {
usleep(100000);
}
}
socket_close($serverSocket);
echo "Service has been elegantly closed。\n";
Although the socket programming interface provided by PHP is powerful, it also brings the complexity of resource management. By setting non-blocking mode, listening to termination signals, using socket_select() and other methods, "elegant shutdown" can be achieved in PHP, effectively avoiding the problems of resource leakage and abnormal termination. These methods are particularly important when building long-running services.
Remember: Always release every resource you open, including sockets.