Socket shutdown: примеры (PHP)
socket_shutdown(resource socket [, int how]): boolФункция socket_shutdown останавливает передачу данных в одном или обоих направлениях по сокету. Она используется для корректного завершения сетевого взаимодействия, сигнализируя удаленной стороне о завершении отправки или получения данных, при этом сам сокет не закрывается.
Когда используется: При реализации протоколов, требующих явного уведомления о конце передачи (например, FTP), для graceful shutdown серверов или клиентов, при необходимости прекратить только отправку или только прием данных.
Синтаксис:socket_shutdown(Socket $socket, int $mode = 2): bool
Аргументы:
- $socket - экземпляр сокета, созданный
socket_createили принятыйsocket_accept. - $mode (необязательный, по умолчанию 2) - определяет, какое направление передачи останавливается:
- 0 (SHUT_RD): Запрещает дальнейшее чтение из сокета.
- 1 (SHUT_WR): Запрещает дальнейшую запись в сокет. Это стандартный способ уведомить peer о завершении отправки.
- 2 (SHUT_RDWR): Запрещает и чтение, и запись.
Функция возвращает true в случае успеха или false в случае ошибки.
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 8080);
socket_write($socket, "Final message");
// Сигнал о завершении отправки
$result = socket_shutdown($socket, 1);
var_dump($result); // bool(true)
// Можно еще читать ответ...
// socket_close($socket);
?>bool(true)
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 8080);
// Немедленное прекращение всех операций
$result = socket_shutdown($socket, 2);
var_dump($result);
// socket_write($socket, "test"); // Вызовет предупреждение
?>bool(true)
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (defined('SHUT_RDWR')) {
$result = socket_shutdown($socket, SHUT_RDWR);
echo "Использована константа SHUT_RDWR\n";
}
?>Использована константа SHUT_RDWR
socket_close - полностью закрывает и уничтожает ресурс сокета. В отличие от socket_shutdown, после закрытия сокет становится непригодным для любых операций. Обычно socket_shutdown вызывают перед socket_close для graceful завершения.
stream_socket_shutdown - аналог для потоков (stream), созданных через stream_socket_client/server. Принимает те же константы режима (STREAM_SHUT_RD, STREAM_SHUT_WR, STREAM_SHUT_RDWR). Работает на более высоком уровне абстракции.
fclose - закрывает файловый указатель, включая сокеты, открытые как потоки. Действует аналогично socket_close, а не поэтапному отключению.
Выбор функции зависит от API: для расширения Socket используйте socket_shutdown, для потоков - stream_socket_shutdown.
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 8080))
s.send(b'data')
s.shutdown(socket.SHUT_WR) # Останавливаем запись
# s.shutdown(socket.SHUT_RD)
# s.shutdown(socket.SHUT_RDWR)
s.close()# Функция выполняется без возвращаемого значения в случае успеха
const net = require('net');
const client = net.createConnection({ port: 8080 }, () => {
client.write('data');
// Аналог SHUT_WR: прекращает запись, но позволяет читать
client.end(); // Отправляет FIN пакет
// Принудительное закрытие (аналог SHUT_RDWR)
// client.destroy();
});// socket.end() - асинхронный, вызывает событие 'finish'
В Python метод shutdown() почти идентичен PHP. В Node.js нет прямого аналога: socket.end() выполняет SHUT_WR и закрывает сокет после отправки буфера, socket.destroy() - принудительное закрытие (SHUT_RDWR). MySQL не предоставляет функций для низкоуровневой работы с сокетами.
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_close($socket);
$result = @socket_shutdown($socket, 2); // Подавляем предупреждение
var_dump($result); // bool(false)
if ($result === false) {
echo "Ошибка: " . socket_strerror(socket_last_error()) . "\n";
}
?>bool(false) Ошибка: Socket argument is not a valid socket resource
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = @socket_shutdown($socket, 5); // Несуществующий режим
var_dump($result);
?>bool(false)
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = @socket_shutdown($socket, 2);
var_dump($result);
?>bool(false)
Все ошибки приводят к возврату false. Рекомендуется проверять результат и использовать socket_last_error() для диагностики.
В PHP 8.0 тип параметра $socket изменен с resource на Socket (объект). Теперь передача некорректного ресурса вызывает TypeError.
// PHP 7
$result = socket_shutdown(fopen('test.txt', 'r'), 2); // Предупреждение
// PHP 8
$result = socket_shutdown(fopen('test.txt', 'r'), 2); // TypeErrorДругих значимых изменений в поведении функции не было. Константы SHUT_RD, SHUT_WR, SHUT_RDWR доступны с ранних версий.
<?php
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '0.0.0.0', 9000);
socket_listen($server);
$client = socket_accept($server);
// Диалог...
socket_write($client, "Server shutting down write\n");
// Прекращаем отправку данных клиенту
socket_shutdown($client, SHUT_WR);
// Но еще можем читать финальное сообщение от клиента
$final = socket_read($client, 1024);
echo "От клиента: $final";
// Теперь полностью закрываем соединение
socket_shutdown($client, SHUT_RDWR);
socket_close($client);
socket_close($server);
?><?php
// Клиент отправляет запрос и сигнализирует о конце отправки,
// но ожидает большой ответ от сервера
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, 'example.com', 80);
$request = "GET /largefile HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
socket_write($socket, $request);
// Сигнал серверу, что клиент больше не будет отправлять данные
socket_shutdown($socket, SHUT_WR);
// Но продолжает читать ответ
$response = '';
while ($buf = socket_read($socket, 4096)) {
$response .= $buf;
}
echo strlen($response) . " байт получено\n";
socket_close($socket);
?><?php
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_nonblock($server); // Неблокирующий режим
socket_bind($server, '0.0.0.0', 9001);
socket_listen($server);
$clients = [];
while (true) {
$client = @socket_accept($server);
if ($client) {
$clients[] = $client;
socket_write($client, "Welcome\n");
}
foreach ($clients as $i => $client) {
// Читаем команду
$cmd = socket_read($client, 1024, PHP_NORMAL_READ);
if (trim($cmd) === 'QUIT') {
// Graceful shutdown для этого клиента
socket_shutdown($client, SHUT_WR);
socket_shutdown($client, SHUT_RD);
socket_close($client);
unset($clients[$i]);
}
}
}
?><?php
// Для UDP сокетов shutdown имеет ограниченный смысл,
// так как нет установленного соединения
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($socket, '127.0.0.1', 10000);
// Остановка чтения предотвратит получение новых датаграмм
socket_shutdown($socket, SHUT_RD);
// socket_recvfrom($socket, $buf, 1024, 0, $from, $port); // Не сработает
socket_close($socket);
?>