글
PHP를 이용한 다중 연결 소켓 통신 (3)
- 차례
- 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)) 에서 소켓 함수를 사용하기 위해 --enable-sockets 옵션을 주어 컴파일 하였습니다.
오늘은 소켓 함수와 Process Control 함수를 추가시켜 컴파일 해보겠습니다.
#] tar -zxvf php-4.3.1.tar.gz #] cd php-4.3.1 #] ./configure --enable-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개로 표현되어 있습니다. 문론 메시지도 잘 전송 되었구요.. ^^
5. 결론
오늘은 PHP의 Process Control Function을 이용하여 다수의 클라이언트 요청처리를 해보았습니다.
fork방식은 select방식보다 간단한 구조로 구현하기 간편하다는 장점도 있지만, 다중 프로세스 구조라 프로세스간 통신을 위해서 부차적인 IPC를 구현해야 할 상황이 생길 수도 있다는 점이 단점이라 할 수 있습니다.
다음 강좌에는 mysql과 지금까지 배운 소켓 통신을 가지고 간단한 채팅 클라이언트/서버를 만들어 보겠습니다.
'Programming' 카테고리의 다른 글
웹메일을 구현하자! - 메일확인 (0) | 2006.05.24 |
---|---|
웹메일을 구현하자! (0) | 2006.05.24 |
PHP를 이용한 다중 연결 소켓 통신 (2) (0) | 2006.05.24 |
PHP를 이용한 다중 연결 소켓 통신 (1) (0) | 2006.05.24 |
PHP5의 새로운 기능 (0) | 2006.05.24 |
RECENT COMMENT