Current Location: Home> Latest Articles> How to elegantly close a connection received by socket_accept()

How to elegantly close a connection received by socket_accept()

M66 2025-05-19

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.

1. Basic use of socket_accept()

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)

2. Analysis of the root cause of the problem

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:

  1. socket_close() not executed : the connection is not closed, the system resources are not released.

  2. The script exits abnormally : causes the created connection to be not cleaned, leaving behind a zombie socket.

  3. Signal interrupt (such as Ctrl+C): No aftermath is processed.

3. Key technologies for achieving elegant closure

1. Use stream_socket_server() instead of socket_create()

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.

2. Set non-blocking mode + signal monitoring

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);

3. Use select to simulate event loops

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.

4. Resource cleaning suggestions

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:

  1. Initialize socket

  2. Set non-blocking/select

  3. Receive connection in the main loop and process requests

  4. Capture the termination signal and exit the loop

  5. Clean up all open connections

  6. Finally, close the listening socket

V. Complete example

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";

6. Summary

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.