- 차례
- 1. 소개
- 2. pcntl_fork() 함수
- 3. PHP 컴파일 하기
- 4. 프로그램 작성
- 5. 결론
이문서의 배포는 자유로우나 최소한 제작자의 정보는 제외하지 않고 배포해 주세요.
문서가 존재하는 모든곳에 답변을 드릴수 없으므로 질문은 홈페이지(http://www.jinoos.com)에서만 받습니다.
1. 소개
이번강좌에는 fork를 이용해서 새로운 프로세스를 생성하여 생성된 자식 서버프로세스가 클라이언트를 담당하는 형태를 구연해 보겠습니다.
PHP에서 fork함수로는 Process Control 함수의 pcntl_fork() 함수가 있습니다. Process Control 함수는 기본함수가 아니기 때문에 컴파일시 옵셥으로 추가시켜야 합니다.
2. pcntl_fork() 함수
- int pcntl_fork ( )
- 함수 호출후 리턴값에 0이면 자식 프로세스이며 >0 이면 부모 프로세스로 자식 프로세스의 PID번호를 리턴 받습니다. error발생시에는 -1 값을 가집니다. - 포크 함수는 포크 함수를 실행한 프로세스와 동일한 자식 프로세스를 생성합니다. 동일한 자식 프로세스라는 의미는 프로세스 계보상의 깊이만 다를뿐 동작은 똑같은 쌍둥이를 만드는 것 입니다. - 자식 프로세스는 부모 프로세스의 메모리를 복사해서 클론을 만들고 리소스(파일 지시자, DB 커넥션, 소켓 커넥션 등)은 공유합니다. - 간단하게 pcntl_fork() 코드를 살펴 보겠습니다. 
 부모 프로세스는 $i 값이 1씩, 자식 프로세스는 $i 값이 2씩 증가하는 프로그램 입니다. 결과는 각자 해보시기 바랍니다.- <?php $i = 0; $pid = pcntl_fork(); // error if($pid == -1) { echo "fork error"; // 부모 프로세스 }elseif($pid > 0) { for(;$i<10;$i++) { echo "Parent Process \$i : $i\n"; } // 자식 프로세스 }elseif($pid == 0) { for(;$i<10;$i+=2) { echo "Child Process \$i : $i\n"; } } ?>
3. PHP 컴파일 하기
첫번째 강좌(PHP를 이용한 다중 연결 소켓 통신 (1)) 에서 소켓 함수를 사용하기 위해 --with-sockets 옵션을 주어 컴파일 하였습니다.
오늘은 소켓 함수와 Process Control 함수를 추가시켜 컴파일 해보겠습니다.
| #] tar -zxvf php-4.3.1.tar.gz #] cd php-4.3.1 #] ./configure --with-sockets --enable-pcntl #] make | 
역시 php 실행파일이 생성됩니다.
4. 프로그램 작성
오늘 작성할 서버와 클라이언트의 구조는 아래와 같습니다.
|                                 ┌───────┐            ┌───┐
                    ┌─(Fork)─┤Child Process ├─(socket)─┤Client│
                    │          └───────┘            └───┘
┌───────┐  │          ┌───────┐            ┌───┐
│Master Process├─┼─(Fork)─┤Child Process ├─(socket)─┤Client│
└───────┘  │          └───────┘            └───┘
                    │          ┌───────┐            ┌───┐
                    └─(Fork)─┤Child Process ├─(socket)─┤Client│
                                └───────┘            └───┘ | 
Child Process 한나가 Client 하나를 독립적으로 마크하는 구조입니다.
연결이 끊어진 Child Process는 바로 소멸됩니다. 새로운 클라이언트가 참여하면 바로 Master Process는 pcntl_fork함수를 이용해서 Child Process를 생성하죠.
4.1. 서버 만들기
서버의 구조를 간단히 살펴보면
| 소켓생성
소켓바인트및 리슨
while(새로운연결수락)
{
    포크
    if(자식프로세스)
    {
        while(메시지수신)
        {
            메시지 처리
            if(quit메시지)
            {
                소켓닫기
                종료
            }
        }
    }
}
             | 
| #!/usr/local/bin/php -q
<?php
set_time_limit(0);
define("_IP",    "111.222.333.12");
define("_PORT",  "65000");
$sSock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($sSock, _IP, _PORT);
socket_listen($sSock);
pcntl_signal(SIGCHLD, SIG_IGN); 
while($sock = socket_accept($sSock))
{
    socket_getpeername($sock, $sockIp, $sockPort);
    msg("client connect : ".$sockIp.":".$sockPort."\n");
    $pid = pcntl_fork();
    msg("fork\n");
    if($pid == -1)
    {
        msg("fork failed\n");
        exit;
    // 자식 프로세스 일때 
    }if($pid == 0)
    {
        while(1)
        {
            $buf = socket_read($sock, 4096);
            // 접속 종료
            if(!$buf)
            {
                msg("client connection broken : ".$sockIp.":".$sockPort."\n");
                exit;
            }
            // 메시지 수신 이벤트
            else
            {
                msg("recive data : ".$buf."\n");
                $cmd = substr($buf, 0, 4);
                switch($cmd)
                {
                    // 시간전송
                    case "time":
                        msg("client(".$sockPort.") time data request\n");
                        socket_write($sock, date("Y/m/d H:i:s"));
                        break;
                    // 종료
                    case "quit":
                        msg("client(".$sockPort.") quit request\n");
                        socket_write($sock, "quit");
                        socket_close($sock);
                        exit;
                        break;
                    default:
                        msg("client(".$sockPort.") invalid command $cmd\n");
                        break;
                }
            }
        }
    }
}
function msg($msg)
{
    echo "SERVER >> ".$msg;
}
?>             | 
역시 server.php로 저장하고 실행권한을 줍니다.
4.3. 실행하기
server.php를 실행후 client.php를 3번 실행하고 프로세스와 프로세스 트리를 확인해보겠습니다.
server.php 실행 화면
| #] ./server.php SERVER >> client connect : 111.222.333.12:38276 -- (1) SERVER >> fork SERVER >> fork SERVER >> recive data : time SERVER >> client(38276) time data request SERVER >> client connect : 111.222.333.12:38396 -- (2) SERVER >> fork SERVER >> fork SERVER >> recive data : time SERVER >> client(38396) time data request SERVER >> client connect : 111.222.333.12:38559 -- (3) SERVER >> fork SERVER >> fork SERVER >> recive data : time SERVER >> client(38559) time data request -- (4) SERVER >> recive data : quit SERVER >> client(38276) quit request -- (5) SERVER >> recive data : quit SERVER >> client(38396) quit request -- (6) SERVER >> recive data : quit SERVER >> client(38559) quit request -- (7) | 
client는 (1), (2), (3)에서 3번 실행하여 동일하게 time 메시지를 송신 및 데이타를 수신하고 하고 quit 했습니다.
| #] ./client.php CLIENT >> socket connect to 111.222.333.12:65000 CLIENT >> Enter command time or quit : time CLIENT >> Input command : time CLIENT >> recived data : 2003/05/21 16:18:34 CLIENT >> Enter command time or quit : quit CLIENT >> Input command : quit #] | 
아래는 (3),(7)시점에서 두번 프로세스 현황을 확인(ps, pstree)한 결과 입니다.
| #] ps -xa | grep server.php
30947 pts/3    S      0:00 /usr/local/bin/php -q ./server.php
31203 pts/3    S      0:00 /usr/local/bin/php -q ./server.php
31287 pts/3    S      0:00 /usr/local/bin/php -q ./server.php
31372 pts/3    S      0:00 /usr/local/bin/php -q ./server.php
31467 pts/7    S      0:00 grep server.php
#] pstree
init-+-crond
    ...
    ...
     |-sshd-+-sshd---bash---server.php---3*[server.php]
     |      |-3*[sshd---bash---client.php]
     |      `-sshd---bash---pstree
    ...
    ...
     `-xinetd
#]
#] ps -xa | grep server.php
30947 pts/3    S      0:00 /usr/local/bin/php -q ./server.php
31521 pts/7    S      0:00 grep server.php
#] pstree           
init-+-crond
    ...
    ...
     |-sshd-+-sshd---bash---su---bash---server.php
     |      |-3*[sshd---bash]
     |      `-sshd---bash---pstree
    ...
    ...
     `-xinetd
#]           | 
pstree의 경우는 server.php---3*[server.php]처럼 Master Process 한개와 Child Process 3개로 표현되어 있습니다. 문론 메시지도 잘 전송 되었구요.. ^^
'Develop! > php' 카테고리의 다른 글
| php의 람다식, 가변 함수 (0) | 2015.08.20 | 
|---|---|
| mysqli bind_result에서 발생하는 memory 오류 대처법 (0) | 2012.08.14 | 
| PHP를 이용한 다중 연결 소켓 통신 (2) (0) | 2011.10.07 | 
| PHP를 이용한 다중 연결 소켓 통신 (1) (0) | 2011.10.07 | 
| 두 날짜 사이의 차이 구하기 (0) | 2007.01.09 |