PHP를 사용하여 소켓 기반 서버 애플리케이션을 구축 할 때 Socket_accept ()는 클라이언트의 연결 요청을 수락하는 핵심 기능 중 하나입니다. 그러나 연결 관리가 제대로 처리되지 않으면 자원 누출과 비정상적인 서비스 종료를 쉽게 유발할 수 있습니다. 이 기사는 자원이 제 시간에 방출되도록하고 서비스의 안정성과 신뢰성을 유지하기 위해 이러한 연결을 어떻게 만들 수 있는지 탐구합니다.
우아한 셧다운을 이해하기 전에 기본 소켓 서버 예를보십시오.
$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);
// 간단한 응답
socket_write($clientSocket, "Hello client!\n");
socket_close($clientSocket);
}
이 예에서 서버는 계속 실행되고 연결을 수신하고 응답합니다. 코드가 실행될 수 있지만 연결이 닫히고 동시성이 높은 또는 장기 실행 중에 예외가 부당하게 처리되면 다음과 같은 문제가 발생할 수 있습니다.
파일 디스크립터 누출 (시스템 리소스 제한 초과)
연결이 닫히지 않아 Time_wait가 축적됩니다
프로세스는 종료해야합니다 (Sigint가 발생한 경우)
php의 socket_accept () 는 차단 함수 이므로 연결 요청이 없을 때까지 대기합니다. 이 동작은 서비스를 종료하거나 다시 시작하거나 종료 할 때 프로세스의 정상적인 종료를 방지합니다. 리소스가 올바르게 릴리스되지 않으면 다음이 발생할 수 있습니다.
Socket_close () 실행되지 않음 : 연결이 닫히지 않고 시스템 리소스가 해제되지 않습니다.
스크립트는 비정상적으로 종료됩니다 . 생성 된 연결을 청소하지 않아 좀비 소켓 뒤에 남겨집니다.
신호 인터럽트 (예 : Ctrl+C) : 여파는 처리되지 않습니다.
Socket_* 시리즈는 낮은 수준의 제어를 제공하지만 PHP의 Stream_Socket_Server ()는 코드를 단순화하는 데 적합한 타임 아웃 제어 및 비 블로킹 동작을 구현하기가 더 쉽습니다.
그러나 Socket_*를 사용한다고 주장하면 다음 방법이 우아한 폐쇄를 달성 할 수 있습니다.
socket_set_nonblock ()은 socket_accept ()를 차단하지 않아도됩니다.
socket_set_nonblock($serverSocket);
이런 식으로 루프를 사용하여 연결이 있는지 확인하고 종료 신호를들을 수 있습니다.
declare(ticks = 1);
$running = true;
pcntl_signal(SIGINT, function() use (&$running) {
echo "포착 SIGINT,종료 준비...\n";
$running = false;
});
while ($running) {
$clientSocket = @socket_accept($serverSocket);
if ($clientSocket !== false) {
socket_write($clientSocket, "Hello client!\n");
socket_close($clientSocket);
} else {
usleep(100000); // 피하다 CPU 너무 높은 점유
}
}
socket_close($serverSocket);
여러 소켓에서 청취 해야하는 경우 Socket_Select ()를 결합하여 이벤트 중심 모델을 구현할 수 있습니다.
$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 ()는 시간 초과를 설정하거나 정시에 인터럽트 신호에 응답 할 수 있으며, 이는보다 강력한 서버 프로그램을 구현하는 데 적합합니다.
socket_create () 및 socket_accept () 가 얻은 각 리소스가 쌍을 이루고 socket_close ()를 실행하는지 확인하십시오. 잘 구조화 된 소켓 서비스 수명주기는 다음과 같습니다.
소켓 초기화
비 블로킹/선택을 설정하십시오
기본 루프 및 프로세스 요청에 연결을받습니다
종료 신호를 캡처하고 루프를 종료하십시오
모든 열린 연결을 정리하십시오
마지막으로 청취 소켓을 닫으십시오
다음은 신호 캡처, 비 블로킹 모드 및 리소스 청소를 구현하는 완전한 예입니다.
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 "종료 신호를 받았습니다,서비스를 닫을 준비를하십시오...\n";
$running = false;
});
while ($running) {
$clientSocket = @socket_accept($serverSocket);
if ($clientSocket) {
socket_write($clientSocket, "방문에 오신 것을 환영합니다 m66.net!\n");
socket_close($clientSocket);
} else {
usleep(100000);
}
}
socket_close($serverSocket);
echo "서비스는 우아하게 폐쇄되었습니다。\n";
PHP가 제공하는 소켓 프로그래밍 인터페이스는 강력하지만 자원 관리의 복잡성도 제공합니다. 비 블로킹 모드를 설정하고 Socket_Select () 및 기타 방법을 사용하여 종단 신호를 듣고 "우아한 셧다운"을 PHP에서 달성하여 리소스 누출 및 비정상 종료 문제를 효과적으로 피할 수 있습니다. 이러한 방법은 장기 실행 서비스를 구축 할 때 특히 중요합니다.
기억하십시오 : 항상 소켓을 포함하여 열린 모든 리소스를 출시하십시오.