Socket accept: примеры (PHP)

Функция socket_accept: создание сетевых серверов на PHP
Раздел: Сокеты
socket_accept(resource socket): resource|false

Функция socket_accept() принимает входящее соединение на сокете, который был предварительно создан и переведен в режим прослушивания с помощью socket_listen(). Она используется в серверных сетевых приложениях на PHP для обработки множественных клиентских подключений.

Аргументы функции
  • socket (обязательный) - ресурс сокета, созданный socket_create() и переведенный в режим ожидания socket_listen().
  • timeout (опциональный, доступен с PHP 8.0) - время ожидания в секундах для операции accept. Если не указан, функция работает в блокирующем режиме.

Функция возвращает новый ресурс сокета для общения с клиентом или false при ошибке.

Примеры использования
Базовый пример TCP-сервера
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '127.0.0.1', 8080);
socket_listen($socket);

// Блокирующее ожидание подключения
$client = socket_accept($socket);

if ($client !== false) {
    socket_write($client, "Hello from server!\n");
    socket_close($client);
}

socket_close($socket);
// Результат: клиент получает строку "Hello from server!"
С использованием таймаута (PHP 8+)
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 5, 'usec' => 0]);
socket_bind($socket, '0.0.0.0', 9000);
socket_listen($socket);

// Ожидание 3 секунды
$client = socket_accept($socket, 3);

if ($client === false) {
    echo "Таймаут подключения\n";
} else {
    socket_write($client, "Connected!\n");
}

socket_close($socket);
// Если подключение не произошло за 3 секунды:
// Таймаут подключения
Альтернативы в PHP

stream_socket_accept() - функция потоков, работающая с потоковыми ресурсами. Позволяет использовать контексты потоков и неблокирующие операции через stream_set_blocking(). Часто используется в веб-серверах и более высокоуровневых библиотеках.

<?php
$server = stream_socket_server('tcp://127.0.0.1:8081');
$client = stream_socket_accept($server);

Socket\select() (из расширения sockets в PHP 8+) - мультиплексирование нескольких сокетов. Позволяет отслеживать несколько сокетов одновременно, что полезно для асинхронных операций.

Аналоги в других языках
Python: socket.accept()
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8080))
server.listen()
client, addr = server.accept()  # Возвращает кортеж (сокет, адрес)
client.send(b'Hello')
client.close()
Node.js: server.accept()
const net = require('net');
const server = net.createServer();
server.on('connection', (socket) => {
    socket.write('Hello');
    socket.end();
});
server.listen(8080);
Go: Accept()
ln, _ := net.Listen("tcp", ":8080")
conn, _ := ln.Accept()  // Возвращает net.Conn
conn.Write([]byte("Hello\n"))
conn.Close()

Ключевое отличие PHP - необходимость предварительной настройки сокета через отдельные вызовы (create, bind, listen), тогда как в других языках часто используются более высокоуровневые API.

Типичные ошибки
Попытка accept на неслушающем сокете
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '127.0.0.1', 8080);
// Пропущен socket_listen()
$client = socket_accept($socket);  // Ошибка!
Warning: socket_accept(): unable to accept incoming connection
Блокировка основного потока
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '0.0.0.0', 8080);
socket_listen($socket);

// Блокирует выполнение скрипта до подключения
$client = socket_accept($socket);
// Дальнейший код не выполняется
Использование закрытого сокета
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '127.0.0.1', 8080);
socket_listen($socket);
socket_close($socket);

$client = socket_accept($socket);  // Неверный ресурс
Warning: socket_accept() expects parameter 1 to be resource, bool given
Изменения в PHP 8
  • Добавлен необязательный параметр timeout, позволяющий установить время ожидания подключения в секундах.
  • Расширение sockets стало частью ядра PHP (ранее требовалась отдельная компиляция).
  • Улучшена обработка ошибок - многие функции теперь выбрасывают исключения вместо генерации предупреждений.
<?php
// Новый параметр timeout в PHP 8+
$client = socket_accept($socket, 5.5); // Ожидание 5.5 секунд
Расширенные примеры
Многоклиентский сервер с fork
Пример php
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($socket, '0.0.0.0', 9999);
socket_listen($socket);

while (true) {
    $client = socket_accept($socket);
    
    $pid = pcntl_fork();
    if ($pid == 0) {
        // Дочерний процесс обрабатывает клиента
        socket_write($client, "PID: " . getmypid() . "\n");
        sleep(2); // Имитация обработки
        socket_close($client);
        exit(0);
    }
    
    socket_close($client); // Закрываем в родительском процессе
}

socket_close($socket);
Неблокирующий сервер с socket_select
Пример php
<?php
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_nonblock($server);
socket_bind($server, '127.0.0.1', 8888);
socket_listen($server);

$clients = [$server];

while (true) {
    $read = $clients;
    $write = $except = null;
    
    if (socket_select($read, $write, $except, 0) > 0) {
        if (in_array($server, $read)) {
            $client = socket_accept($server);
            $clients[] = $client;
            socket_write($client, "Welcome!\n");
        }
        
        // Обработка клиентских сокетов...
    }
    usleep(100000); // 100ms
}
Сервер с обработкой таймаутов (PHP 8)
Пример php
<?php
declare(strict_types=1);

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

$start = microtime(true);

// Пытаемся принять соединение 2 секунды
while (microtime(true) - $start < 2.0) {
    $client = socket_accept($socket, 0.5); // Проверка каждые 500ms
    
    if ($client !== false) {
        $peer = '';
        socket_getpeername($client, $peer);
        echo "Подключен клиент: $peer\n";
        socket_close($client);
        break;
    }
    
    echo "."; // Индикатор ожидания
}

if ($client === false) {
    echo "\nКлиенты не подключились за отведенное время\n";
}

socket_close($socket);
......
// Если клиент подключился:
// Подключен клиент: 127.0.0.1:54321
// Если не подключился:
// Клиенты не подключились за отведенное время

PHP socket_accept function comments

En
Socket accept Accepts a connection on a socket