현재 위치: > 최신 기사 목록> socket_accept ()에 의해 수신 된 연결을 우아하게 닫는 방법

socket_accept ()에 의해 수신 된 연결을 우아하게 닫는 방법

M66 2025-05-19

PHP를 사용하여 소켓 기반 서버 애플리케이션을 구축 할 때 Socket_accept ()는 클라이언트의 연결 요청을 수락하는 핵심 기능 중 하나입니다. 그러나 연결 관리가 제대로 처리되지 않으면 자원 누출과 비정상적인 서비스 종료를 쉽게 유발할 수 있습니다. 이 기사는 자원이 제 시간에 방출되도록하고 서비스의 안정성과 신뢰성을 유지하기 위해 이러한 연결을 어떻게 만들 수 있는지 탐구합니다.

1. 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가 발생한 경우)

2. 문제의 근본 원인 분석

php의 socket_accept ()차단 함수 이므로 연결 요청이 없을 때까지 대기합니다. 이 동작은 서비스를 종료하거나 다시 시작하거나 종료 할 때 프로세스의 정상적인 종료를 방지합니다. 리소스가 올바르게 릴리스되지 않으면 다음이 발생할 수 있습니다.

  1. Socket_close () 실행되지 않음 : 연결이 닫히지 않고 시스템 리소스가 해제되지 않습니다.

  2. 스크립트는 비정상적으로 종료됩니다 . 생성 된 연결을 청소하지 않아 좀비 소켓 뒤에 남겨집니다.

  3. 신호 인터럽트 (예 : Ctrl+C) : 여파는 처리되지 않습니다.

3. 우아한 폐쇄를 달성하기위한 주요 기술

1. socket_create ( ) 대신 stream_socket_server ()를 사용하십시오.

Socket_* 시리즈는 낮은 수준의 제어를 제공하지만 PHP의 Stream_Socket_Server ()는 코드를 단순화하는 데 적합한 타임 아웃 제어 및 비 블로킹 동작을 구현하기가 더 쉽습니다.

그러나 Socket_*를 사용한다고 주장하면 다음 방법이 우아한 폐쇄를 달성 할 수 있습니다.

2. 비 차단 모드 + 신호 모니터링을 설정하십시오

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

3. 선택을 사용하여 이벤트 루프를 시뮬레이션하십시오

여러 소켓에서 청취 해야하는 경우 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 ()는 시간 초과를 설정하거나 정시에 인터럽트 신호에 응답 할 수 있으며, 이는보다 강력한 서버 프로그램을 구현하는 데 적합합니다.

4. 자원 청소 제안

socket_create ()socket_accept () 가 얻은 각 리소스가 쌍을 이루고 socket_close ()를 실행하는지 확인하십시오. 잘 구조화 된 소켓 서비스 수명주기는 다음과 같습니다.

  1. 소켓 초기화

  2. 비 블로킹/선택을 설정하십시오

  3. 기본 루프 및 프로세스 요청에 연결을받습니다

  4. 종료 신호를 캡처하고 루프를 종료하십시오

  5. 모든 열린 연결을 정리하십시오

  6. 마지막으로 청취 소켓을 닫으십시오

V. 완전한 예

다음은 신호 캡처, 비 블로킹 모드 및 리소스 청소를 구현하는 완전한 예입니다.

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

6. 요약

PHP가 제공하는 소켓 프로그래밍 인터페이스는 강력하지만 자원 관리의 복잡성도 제공합니다. 비 블로킹 모드를 설정하고 Socket_Select () 및 기타 방법을 사용하여 종단 신호를 듣고 "우아한 셧다운"을 PHP에서 달성하여 리소스 누출 및 비정상 종료 문제를 효과적으로 피할 수 있습니다. 이러한 방법은 장기 실행 서비스를 구축 할 때 특히 중요합니다.

기억하십시오 : 항상 소켓을 포함하여 열린 모든 리소스를 출시하십시오.