Current Location: Home> Latest Articles> How to avoid FD (file descriptor) exhaustion when using socket_accept()

How to avoid FD (file descriptor) exhaustion when using socket_accept()

M66 2025-06-04

When writing Socket-based server applications using PHP's socket_accept() function, developers often encounter a headache: This usually causes the server to fail to accept new client connections, causing service outages. This article will analyze the causes of this problem in depth and provide practical response strategies to help you build a more stable and robust Socket service.

1. What is a file descriptor (FD)?

In UNIX-like systems, each process has a file descriptor table that manages references to resources such as files, sockets, pipelines, etc. A file descriptor is an integer, and the system assigns a unique FD to each open resource. For network programming, a new FD is created every time the client connects.

By default, the number of FDs that a Linux system can open for a single process is limited (usually 1024). When you run a highly concurrent Socket service, if the connection resources are not properly managed, the FD will be exhausted very quickly.

2. The working mechanism of socket_accept()

When using Socket programming in PHP, socket_accept() is a key function used to accept client connections. The basic usage is as follows:

 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '0.0.0.0', 8080);
socket_listen($socket);

while (true) {
    $client = socket_accept($socket);
    if ($client) {
        // Handle client connections
    }
}

Every time socket_accept() is successfully executed, a new client socket resource is created, which will occupy an FD. If the client is not closed in time or is not released under abnormal conditions, the number of FDs will continue to increase, which will eventually lead to socket_accept() returning false and reporting an error.

3. How to avoid FD exhaustion?

1. Set the FD upper limit

First, confirm and appropriately increase the maximum number of file descriptors allowed by the system:

 ulimit -n 65535

You can also persist the setting by editing /etc/security/limits.conf :

 www-data soft nofile 65535
www-data hard nofile 65535

2. Set socket to non-blocking mode

The blocking socket_accept() will hang the thread while waiting for a connection. If the system FD is exhausted, the program will stall. Use socket_set_nonblock() to keep the program running even if the new connection is currently not acceptable.

 socket_set_nonblock($socket);

You can add delay retry logic after socket_accept() fails to avoid resource dead loop:

 $client = @socket_accept($socket);
if ($client === false) {
    usleep(100000); // wait 100ms Try again
    continue;
}

3. Actively close invalid connection

When a client connection completes data transfer, the connection must be explicitly closed and resources must be released:

 socket_close($client);

If not turned off, the FD will be consumed all the time. It is recommended to manage all client connections into an array, periodically check their active status, and clean up invalid connections in time.

4. Use select() multiplexing mechanism

Compared with the simple while + socket_accept() , it is recommended to use socket_select() to implement the event-driven model. Select() , you can listen to multiple connections at the same time and process the connection when an event occurs, avoiding unnecessary waste of resources.

 $read = [$socket];
$write = $except = [];

if (socket_select($read, $write, $except, 0) > 0) {
    if (in_array($socket, $read)) {
        $client = socket_accept($socket);
        if ($client) {
            socket_set_nonblock($client);
            $clients[] = $client;
        }
    }
}

5. Join the number of connections limits

In high concurrency scenarios, you can prevent FD from being filled simultaneously by limiting the maximum number of connections:

 $maxClients = 1000;

if (count($clients) >= $maxClients) {
    socket_close($client); // Reject new connection
    continue;
}

You can also limit the number of front-end connections through Nginx or similar middleware to reduce server pressure. For example:

 limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;

server {
    listen 80;
    server_name m66.net;

    location / {
        limit_conn conn_limit_per_ip 20;
        proxy_pass http://localhost:8080;
    }
}

4. Summary

FD exhaustion is one of the common pitfalls in Socket services, but as long as we master the relevant principles and deal with them through non-blocking mode, reasonable connection closing, connection number control and other methods, we can significantly improve the stability and availability of the system. Building a robust Socket service is not just about writing code that can run, but more importantly, it is about rationally managing system resources and preventing potential risks.

In your Socket project, if you are facing the error of socket_accept() returning false or "Too many open files", you might as well troubleshoot and optimize from the above directions to create an efficient and reliable server program.