코딩하면서 유용하게 사용하고 있는 사이트들이 몇 개 있는데...

자주 사용하다 보니... 그냥 이렇게 정리해 두고 필요할 때 끄집어 내서 사용하면 좋겠다 싶어 정리 ^^

순서는 중요도랑 아무 상관 없이... 그냥...


1. Json online Editor


http://www.jsoneditoronline.org/


json editor 기능도 있고, 이쁘게 정리도 해 주고, object로 접었다 폈다 하면서 구조적으로 잘 볼 수 있도록 해 준다.



2. XML to Json


http://www.utilities-online.info/xmltojson/#.WIbKYFOLRVI


xml의 list 형태가 json으로 변하면 어떻게 변하는지 궁금해서 찾아본 사이트...

요즘은 대부분 json을 많이 쓰지만 xml을 쓰기 원하는 곳도 있으니 간간히 필요한 경우 사용하면 좋을 듯...



3. Online regex tester and debuger


https://regex101.com/


예전에 XE 개발 할 때 오픈소스라서 날마다 해킹 시도가 있어서 뚤리는거 막고 뚤리는거 막고 할 때 정규 표현식을 많이 썼는데, 바로 바로 해당 정규표현식이 잘 적용 되는지 확인할 수 있어서 좋다.



4. URL Decoder, Encoder


http://meyerweb.com/eric/tools/dencoder/


인코딩 된 문자를 디코딩 하거나 디코딩 된 문자를 인코딩 하고 싶을 경우 바로 바로 확인 가능하기에 괜찮은 사이트이다.






저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

이 역시 티몬 구매후기에서 이미지 업로드와 관련된 기능을 적용하면서 나온 내용을 정리 차원에서 적어 둡니다.


구매후기 작성 시 이미지를 선택해서 업로드를 하게 되는데, 기존에는 이미지의 사이즈를 제한하는 기능이 있었고 이로 인해 이미지 제한에 걸려 이미지를 업로드 하지 못하거나, 업로드 된 원본 이미지도 용량이 너무 큰 문제가 있었습니다. (장당 3MB)


아무런 제약 없이 이용자는 이미지를 마음껏 올릴 수 있으려면 이미지 리사이즈가 필요한데, 서버단에서 용량 리사이징을 하게 되면 아까운 네트웍 비용이 들어가게 되므로 클라이언트 단에서 용량을 줄일 필요가 있었습니다.

그래서 사용하게 된 것이 HTML5의 Canvas 기능이죠.

물론 이전에 HTML5를 제대로 사용해 본 적이 없어서...

모든 기능은 최고의 방법이 구글링을 통해 적용했습니다.

그리고 HTML5의 Canvas가 적용되지 않는 브라우저를 위해서라도 (망할 IE -.-;;) 기존 소스 코드는 남겨두고 분기 처리를 해야 했습니다.


기본적으로 jQuery를 사용 했습니다.

아... 그리고 기본적으로 저는 자바스크립트를 잘 못하는 개발자이므로 발코딩이 나와도 이해해 주세요 ^^

간단한 설명은 주석으로 달았습니다.



<html5의 Canvas를 사용해서 이미지 리사이징이 가능한지 판별하는 메소드>


<html form>


<file input을 통해 file을 선택했을 경우 동작하는 메소드>


<String을 Binary로 변환해 주는 메소드. 구글링 결과물로 정확한 내용은 검토해 보지 못함>


<리사이징 된 파일을 업로드 하는 메소드>






저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

필요에 의해 html -> php -> java api로 파일을 전송해야 할 일이 생겼다.

Java Api는 UI에서 바로 사용하면 안되는 상태라서 php를 거쳐 가야 하는데 html에서 올린 파일을 Java API로 전달 하는 과정이 생각보다 쉽지 않아 정리 차원에서 글을 남긴다.


Html에서 php로 파일을 올리게 되면 $_FILES 라는 전역변수 안에 정보가 담기고, 실제 파일은 php.ini에서 지정한 임시 디렉토리에 저장된다.

해당 경로는 $_FILES['업로드한 html form name']['tmp_name'] 에 저장되어 있으며, 해당 파일을 다시 Java API 쪽으로 넘겨 주면 된다.


넘겨주는 방법은 curl을 사용하면 된다.

다음과 같은 방법으로 하면 된다.






Content-Type을 multipart/form-data로 셋팅하는 부분은 많지만, 많이 찾기 힘들었던 부분은 ".';filename='.$file['name'].';type='.$file['type']" 이다.

html에서 php로 업로드 한 파일은 image/png 인데 php에서 Java로 curl을 통해 업로드 하게 되면, 기본적으로 application/octet-stream 타입으로 업로드 되기 때문이다.

이 부분을 html에서 업로드 한 그대로 by pass 하기 위해서는 위 소스와 같이 값을 지정해 주면 된다.


그 후 $response에서 받은 Json 값을 파싱하면 되는데

$response에는 header 정보도 같이 들어오게 되므로 body만 별도로 분리해야 한다.



이렇게 하게 되면 header를 제외한 순서 Json body 부분만 받을 수 있게 된다.

저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

오랜만에 php 코드를 들여다 볼 일이 생겼는데, 알지 못할만한 소스코드들이 있어 헤메다가 정리 차원에서 적어둔다.


1. php의 람다식.


http://php.net/manual/kr/function.array-walk.php

array_walk($data, function(&$value, $key, $joinUsing) { 

$value = $key . $joinUsing . $value; 

}, $glue);


array_walk의 2번째 인자값을 function 변수로 넘길 수도 있지만 위에처럼 람다식으로 그냥 적어버릴 수 있다.

안드로이드에서 Callback 함수로 람다식으로 적는 경우가 많은데, 그런식으로 작성하나 보다. 오랜만에 php 하니 새롭다 ^^;;


2. 가변함수


이런말 첨 들어보는데, 위의 array_walk가 만일 유저 정의 함수라면 함수 내에서 두번째 인자값을 받아다가 사용할 수 있다.

두번째 인자를 $func 이란 변수로 받는다면 함수 자체는 $func() 과 같이 사용할 수 있다.

이와 관련한 것도 php.net에서 간단한 예제를 볼 수 있다.


http://php.net/manual/kr/functions.variable-functions.php

function foo() {

    echo "foo() 안입니다.<br />\n";

}


$func = 'foo';

$func();        // foo()를 호출합니다.


저작자 표시 비영리 변경 금지
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

위도와 경도를 표기하기 위해서 DB Column을 만드는 중 알게 된 내용을 정리해 본다.

정확한 내용이 아닐 수도 있으니 참고 사항으로만 보길...


MySQL에서 float, double 타입을 Create할 때 float(length, decimals) 와 같은 형식으로 만든다.

여기서 length와 decimal이 의미하는 내용이 정확히 무엇인지 알기 위해 구글링을 했지만, 매뉴얼에나 나올법한 알아듣지 못할 소리를 번역기로 돌린 듯한 글만 있...


그래서 좀더 검색해 보니 얻어 걸린 글에서 아래와 같은 내용이 보였다.




https://dev.mysql.com/doc/refman/5.0/en/floating-point-types.html


M(length)는 전체 길이 즉 정수 부분과 소수 부분을 합친 길이를 가리키며, D(Decimals)는 소수 부분을 가리킨다는 말이다.

예제에서도 나온 것처럼 float(7,4)로 Column을 정의 해 두었고 이 Column에 999.00009란 값이 들어가면 전체 길이가 8이고 소수 부분이 5 이므로 00009란 소수 부분이 0001로 바뀌게 된다.


덧. 아무런 의미 없는 매뉴얼 같은 내용을 이해도 하지 못하면서 블로그에 올려두는 것도, 수많은 정보로 제대로 된 지식을 못 찾게 만드는 주범인것 같기도 하다.



저작자 표시 비영리 변경 금지
신고

'Program! > MySQL' 카테고리의 다른 글

MySQL의 Float, Double에서 Length와 Decimal이 의미하는 것...  (0) 2015.06.02

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
XE의 mysqli 환경 가운데서 발생하는 오류를 잡기 위해 테스트 하다가 설치 후 초기 화면 조차 보이지 않는 문제를 당면하게 되었다.

맞닥뜨리게 된 에러는 다음과 같다.


Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 4294967296 bytes) in .../classes/db/DBMysqli.class.php on line 234


4G 메모리 할당을 하다가 오류가 발생했다는 오류였다.

대부분 memeory 할당 오류는 php.ini 또는 ini_set을 통해 해결할 수 있다는 답변 뿐이였다.

예를 들어 http://www.xpressengine.com/qna/20836943 이런 식 ^^;;


그런데 php.ini에서 메모리를 4G 이상 5G를 줘도 문제가 발생했고

메모리의 제한이 없도록 -1을 준 경우에는 서버의 메모리를 다 쓰게 되어서 서버가 멈추는 현상까지 벌어지게 되었다.


따라서 처음에는 XE의 문제인줄 알고 Source code를 수정하고자 Debuging을 열심히 하였지만 도저히 방법이 보이지 않았다.


그러다가 4G라는 크기에 눈이 가게 되었고, 4G는 MySQL의 longtext type의 크기라는 것에 시선이 고정되게 되었다.

이를 기초로 구글링을 해보니 어떤 검색 결과에서는


CAST(`modules`.`content` AS char(20000))


와 같이 longtext type의 column의 type cast를 하라는 말이 나와 있었다.

물론 위와 같이 하니까 문제 없이 해결은 되었지만...

길이가 다르니 내용까지 짤려서 보일 듯 싶어서 왠지 깨름직 했다.


왠지 해결책은 매뉴얼에 있을 듯 싶어 php 매뉴얼을 뒤졌더니


http://kr2.php.net/manual/en/mysqli-stmt.bind-result.php#101543


위와 같이 똑같은 증상을 호소하는 사람이 있었다.

해당 내용에 연결된 버그리포트에는 해당 내용이 버그가 아니며 mysql 버그로 보인다라는 말이 있었다.

따라서 php 재설치 시 다음과 같은 옵션으로 설치해 보면 된다고 했다.


--with-mysql=mysqlnd
--with-mysqli=mysqlnd
--with-pdo-mysql=mysqlnd
--enable-mysqlnd


mysqlnd로 구글링 해 보니 또한 다음과 같은 문서도 나오더라.


http://www.lovelgw.com/Blog/316


애초에 설치 시에는 옵션을


--with-mysql=/usr/local/mysql \
--with-mysqli=/usr/bin/mysql_config \


와 같이 줬는데 mysqlnd로 옵션을 바꿔 설치하니 이상없이 동작을 하게 되었다.


하루종일 이 문제로 구글링 하고 디버깅 했는데 결국 해결을 하게 되어 한시름 놓을 수 있게 되었다.


신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

이진우

프리랜서
프로그래머

            
          


이문서의 배포는 자유로우나 최소한 제작자의 정보는 제외하지 않고 배포해 주세요.

문서가 존재하는 모든곳에 답변을 드릴수 없으므로 질문은 홈페이지(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() 코드를 살펴 보겠습니다.

<?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";
    }
}
?>
                    
부모 프로세스는 $i 값이 1씩, 자식 프로세스는 $i 값이 2씩 증가하는 프로그램 입니다. 결과는 각자 해보시기 바랍니다.


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메시지)
            {
                소켓닫기
                종료
            }
        }
    }
}
            
구조 입니다. 메시지 처리 부분은 지난 강좌(PHP를 이용한 다중 연결 소켓 통신 (2))의 메시지 처리 부분과 동일하며 select처리 대신 fork를 이용한 처리 입니다.

#!/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.2. 클라이언트 만들기

클라이언트는 지난 강좌지난 강좌(PHP를 이용한 다중 연결 소켓 통신 (2))에서 사용한 클라이언트 프로그램을 수정없이 그대로 사용합니다.


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
#]          
(3) 시점에는 fork 3번 실행한 순간이므로 부모 프로세스와 자식 프로세스 3개, 총 4개의 프로세스가 실행되고 있는것을 확인할수 있습니다.

pstree의 경우는 server.php---3*[server.php]처럼 Master Process 한개 Child Process 3개로 표현되어 있습니다. 문론 메시지도 잘 전송 되었구요.. ^^


5. 결론

오늘은 PHP의 Process Control Function을 이용하여 다수의 클라이언트 요청처리를 해보았습니다.

fork방식은 select방식보다 간단한 구조로 구현하기 간편하다는 장점도 있지만, 다중 프로세스 구조라 프로세스간 통신을 위해서 부차적인 IPC를 구현해야 할 상황이 생길수도 있다는 점이 단점이라 할수 있습니다.

다음 강좌에는 mysql과 지금까지 배운 소켓 통신을 가지고 간단한 채팅 클라이언트/서버를 만들어 보겠습니다.

신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

이진우

프리랜서
프로그래머

            
          


이문서의 배포는 자유로우나 최소한 제작자의 정보는 제외하지 않고 배포해 주세요.

문서가 존재하는 모든곳에 답변을 드릴수 없으므로 질문은 홈페이지(http://www.jinoos.com)에서만 받습니다.


1. 소개

저번강좌(PHP를 이용한 다중 연결 소켓 통신 (1))에 간단한 서버/클라이언트 프로그램을 만들어 보았습니다. 하지만 이것은 많은 부분이 부족하다는 생각들을 하셨을껍니다.

오늘 시간에는 socket_select() 함수를 통해서 다중의 클라이언트 요청을 처리하는 프로그램을 짜 보겠습니다.


2. SELECT

저번강좌(PHP를 이용한 다중 연결 소켓 통신 (1))의 server.php 에서

while($cSock = socket_accept($sSock))
{
    $date = date("Y/m/d H:i:s");
    socket_write($cSock, $date);
    socket_close($cSock);
}

위코드는 클라이언트 소켓이 접속하기를 대기하는 동작입니다. 하지만 socket_accept()함수는 호출된뒤에 소켓연결 요청이 들어오면 요청 처리를 하게 됩니다. 프로그램 상에선 메시지를 전송하고 곧바로 클라이언트와 소켓을 종료하고 다시 accept 상태(blocking모드)로 전환되는 형태입니다.

이와같은 경우는 접속된 클라이언트와 소켓 접속이 반드시 끊어져야(아니면 당연히 끊어야 한다던지) 다른 클라이언트의 접속을 처리 할수 있습니다. 하지만 불규칙 적으로 클라이언트가 서버에 요청(소켓접속이 아님)을 하는 상황이라면 문제가 될것이 자명 합니다.

이와 같은 상황을 처리 하기 위해서 다량의 socket묶음을 동시에 감시하는 SELECT가 있습니다. PHP에서는 socket_select() 함수가 바로 그것입니다.


2.1. socket_select() 함수

int socket_select ( resource &read, resource &write, resource &except, int tv_sec [, int tv_usec] )

read 인자에는 읽기 이벤트를 감시할 Socket Array가 참조됩니다. socket_select() 함수는 Array 묶음으로 들어오는 소켓들을 감시하다가 이벤트가 발생하면 해당인자를 읽기 이벤트가 발생한 Socket Array로 변환하고 Blocking 상태가 해제 됩니다.

write 인자역시 read 인자와 같은 역할로 쓰기 이벤트가 발생할시에 사용된다.(하지만. -_- 어디에 필요하단 말인가. 본인은 이부분이 이해가 잘 안간다. 혹시 알고있다면 알려주기 바란다. except 역시 본인이 사용법을 알아내지 못했다.)

중요한 인자인 tv_sec는 select가 block 모드로 대기하고 있는 timeout을 설정합니다. 이것을 유용하게 이용하면 일정간격으로 어떠한 작업을 수행하는것도 가능합니다. NULL로 설정하면 timeout없이 대기 합니다.


3. 프로그램 작성

전장에서 select 함수에 대해 몇가지를 알아 보았습니다. 부족하지만 사용하기엔 크게 문제가 없을 것입니다.

오늘 작성한 프로그램은 저번강좌(PHP를 이용한 다중 연결 소켓 통신 (1)) 에서 작성한 프로그램과 기능은 같지만 약간 복잡한 형태입니다.

서버소켓을 생성한뒤에 클라이언트를 대기하고, 클라이언트가 접속하면 클라이언트 소켓을 클라이언트 소켓 묶음으로 저장한뒤에 감시합니다. 새로운 클라이언트가 접속하면 다시 클라이언트 묶음에 저장하고 감시를 반복하며, 클라이언트에서 time라는 메시지를 수신하면 현재 시간을 바로 송신하고, quit라는 메시지를 수신하면 클라이언트와 접속을 끊습니다.


3.1. 서버 만들기

시작코드는 저번 강좌와 많이 비슷합니다. 다만 무한 루프를 돌면서 서버 소켓과 클라이언트 소켓 묶음을 Array로 생성하여 select의 Read 이벤트를 감시합니다.

...
while(1)
{
    $sockArr = array_merge(array($sSock), $cSock);
    if(socket_select($sockArr, $tWrite = NULL, $tExcept = NULL, _TIMEOUT) > 0)
    {
        ... (1)
    }
}
...
Write 이벤트와 Except 이벤트는 무시하기 위해서 NULL 처리 되었습니다. timeout은 주어졌지만 본 프로그램에선 크게 의미 없는 상황입니다.

위 코드중 socket_select()에서 이벤트가 발생되면 $sockArr 변수에 리턴된 Array를 (1)에서 분석합니다.

        ...
        foreach($sockArr as $sock)
        {
            if($sock == $sSock)
            {
                ... // (2)
            }else
            {
                ... // (3)
            }
        }
        ...
서버 소켓에 클라이언트가 접속을 요청했을때 $sockArr에 서버 소켓이 리턴됩니다. 그리하여 (2)에서 클라이언트 소켓을 socket_accept()함수로 접속을 허용합니다.

만약 리턴된 소켓이 서버 소켓이 아니라면 클라이언트와 통신중인 소켓입니다. 당연하겠죠. array_merge(array($sSock), $cSock) 로 서버 소켓과 클라이언트 소멧만을 merge했으니까요.

(3)에서는 클라이언트에서 보내는 정보를 읽어들이고 이를 분석해서 처리 하는 부분이 들어 갑니다. 유의해서 봐야 할 부분은

                $buf = socket_read($sock, 4096);

                // 접속 종료
                if(!$buf)
                {
                }
                // 메시지 수신 이벤트
                else
                {
                }
$buf 의 내용이 없을때 클라이언트가 접속을 종료함을 의미 한다는 것입니다.

자 그럼 이제 실제 코드를 보겠습니다.

#!/home/dimeclub/www/bin/php/php -q
<?php
set_time_limit(0);

define("_IP",    "111.222.333.12");
define("_PORT",  "65000");
define("_TIMEOUT", 10);


$cSock = array();
$cInfo = array();
$sSock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

socket_bind($sSock, _IP, _PORT);
socket_listen($sSock);

while(1)
{
    $sockArr = array_merge(array($sSock), $cSock);
    if(socket_select($sockArr, $tWrite = NULL, $tExcept = NULL, _TIMEOUT) > 0)
    {
        foreach($sockArr as $sock)
        {
            // Listen 하고 있는 서버 소켓일 경우
            // 새로운 클라이언트의 접속을 의미
            if($sock == $sSock)
            {
                $tSock = socket_accept($sSock);

                socket_getpeername($tSock, $sockIp, $sockPort);

                $cSock[$tSock] = $tSock;
                $cInfo[$tSock] = array('ip'=>$sockIp, 'port'=>$sockPort);

                msg("client connect : ".$sockIp.":".$sockPort."\n");
            }
            // 클라이언트 접속해 있는 소켓중 하나일경우
            // 해당 클라이언트에서 이벤트가 발생함을 의미
            else
            {
                $buf = socket_read($sock, 4096);

                // 접속 종료
                if(!$buf)
                {
                    exceptSocket(&$cSock, &$cInfo, $sock);
                    msg("client connection broken : ".$sockIp.":".$sockPort."\n");
                }
                // 메시지 수신 이벤트
                else
                {
                    msg("recive data : ".$buf."\n");
                    $thisSockInfo = $cInfo[$sock];
                    $cmd = substr($buf, 0, 4);
                    switch($cmd)
                    {
                        // 시간전송
                        case "time":
                            msg("client(".$thisSockInfo['port'].") time data request\n");
                            socket_write($sock, date("Y/m/d H:i:s"));
                            break;

                        // 종료
                        case "quit":
                            msg("client(".$thisSockInfo['port'].") quit request\n");
                            socket_write($sock, "quit");
                            socket_close($sock);
                            exceptSocket(&$cSock, &$cInfo, $sock);
                            break;
                        default:
                            msg("client(".$thisSockInfo['port'].") invalid command $cmd\n");
                            break;
                    }
                }
            }
        }
    }
}

function exceptSocket(&$sockSet, &$infoSet, $sock)
{
    unset($sockSet[$sock]);
    unset($infoSet[$sock]);
    // array_merge 함수에서 error 발생을 막기위한 처리
    if(count($sockSet)==0)
    {
        $sockSet = array();
        $infoSet = array();
    }
}

function msg($msg)
{
    echo "SERVER >> ".$msg;
}
?>
server.php로 저장합니다. 역시 실행권한은 있어야 겠지요.


3.2. 클라이언트 만들기

클라이언트 역시 몇가지를 수정하여 쉘에서 사용자의 입력을 받아 들이도록 처리하였습니다.

클라이언트는 서버와 소켓을 연결한뒤 사용자의 키입력을 기다렸다 time을 입력시 서버에 time메시지를 전송하고 quit입력시 프로그램을 종료 합니다. PHP CGI를 이용해서 사용자 키입력을 받아들이는 역할은 아래 함수처럼 구현되었습니다.

function read_data()
{
    $in = fopen("php://stdin", "r");
    $in_string = fgets($in, 255);
    fclose($in);
    return $in_string;
}
fopen("php://stdin", "r") 로 원래 의미는 리눅스 시스템에서 fopen("/dev/stdin", "r"), fopen("/proc/self/fd/0", "r"), fopen("dev/fd/0", "r") 입니다. 표준입력을 받아들이는 역할로 사용자 키입력을 받아들이죠.

만약 표준출력 또는 표출에러 출력을 받아들이려면 fopen("php://stdout","r"), fopen("php://stderr", "r") 로 구현할수 있습니다.

클라이언트는 time 라는 서버에 시간 데이터를 요청하는 메시지와 quit 라는 종료 두 메시지를 제외하고는 나머지는 무시합니다. 코드상에는 invalid command (not send) 라고 출력하지만 그이상의 어떠한 행동도 하지 않고 무시해 버립니다.

#!/home/dimeclub/www/bin/php/php -q
<?php
define("_IP",    "111.222.333.12");
define("_PORT",  "65000");

$sock      = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, _IP, _PORT);
msg("socket connect to "._IP.":"._PORT."\n");

while(1)
{
    msg("Enter command time or quit : ");

    // 사용자의 명령어를 입력받습니다.
    $stdin = ereg_replace("\n|\r", "", read_data());
    $stdin = substr($stdin, 0, 4);
    
    // time 또는 quit 메시지 말고는 무시 합니다.
    if($stdin == "time" || $stdin == "quit")
    {
        msg("Input command : ".$stdin."\n");
    }else
    {
        msg("invalid command (not send) : ".$stdin."\n");
        continue;
    }

    socket_write($sock, $stdin);
    $sMsg  = socket_read($sock, 4096);
    if(substr($sMsg, 0, 4) == 'quit')
    {
        socket_close($sock);
        exit;
    }else
    {
        msg("recived data : ".$sMsg."\n");
    }
}

// 표준입력을 받아 값을 리턴하는 함수
function read_data()
{
    $in = fopen("php://stdin", "r");
    $in_string = fgets($in, 255);
    fclose($in);
    return $in_string;
}

// 로그를 출력합니다. 디버그용
function msg($msg)
{
    echo "CLIENT >> ".$msg;
}
?>

특별히 난해한 부분은 없을꺼라 생각합니다. 역시 위 소스를 client.php로 저장하고 실행권한을 줍니다.


3.3. 실행하기

서버를 실행하고 클라이언트를 실행합니다. 클라이언트#1을 실행후 time 메시지를 날리면서 클라이언트#2 실행후 종료, 클라이언트#3 실행후 종료, 그리고 다시 클라이언트#1을 종료 하였습니다.

클라이언트#1

#] ./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/09 22:32:29
CLIENT >> Enter command time or quit : time
CLIENT >> Input command : time
CLIENT >> recived data : 2003/05/09 22:32:34
CLIENT >> Enter command time or quit : time
CLIENT >> Input command : time
CLIENT >> recived data : 2003/05/09 22:32:41
CLIENT >> Enter command time or quit : time
CLIENT >> Input command : time
CLIENT >> recived data : 2003/05/09 22:32:50
CLIENT >> Enter command time or quit : time
CLIENT >> Input command : time
CLIENT >> recived data : 2003/05/09 22:32:57
CLIENT >> Enter command time or quit : quit
CLIENT >> Input command : quit
#]

클라이언트#2,#3

#] ./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/09 22:32:31
CLIENT >> Enter command time or quit : quit
CLIENT >> Input command : 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/09 22:32:47
CLIENT >> Enter command time or quit : quit
CLIENT >> Input command : quit
#] 

같은 상황의 서버쪽 실행화면입니다.

#] ./server.php 
SERVER >> client connect : 111.222.333.12:32850        -- (4)
SERVER >> client connect : 111.222.333.12:32868        -- (5)
SERVER >> recive data : time
SERVER >> client(32850) time data request
SERVER >> recive data : time
SERVER >> client(32868) time data request
SERVER >> recive data : time
SERVER >> client(32850) time data request
SERVER >> recive data : quit                          
SERVER >> client(32868) quit request                  -- (6)
SERVER >> recive data : time
SERVER >> client(32850) time data request
SERVER >> client connect : 111.222.333.12:32938        -- (7)
SERVER >> recive data : time
SERVER >> client(32938) time data request
SERVER >> recive data : time
SERVER >> client(32850) time data request
SERVER >> recive data : quit             
SERVER >> client(32938) quit request                  -- (8)
SERVER >> recive data : time
SERVER >> client(32850) time data request
SERVER >> recive data : quit
SERVER >> client(32850) quit request                  -- (9)

(4)에서 클라이언트포트 32850, (5)에서 32868 이렇게 두개의 클라이언트가 접속했습니다. 서버는 두개의 클라이언트에서 time 메시지를 수신하면서 동작하다 (6)에서 하나의 클라이언트가 접속을 종료하고 (7)에서 다시 하나의 클라이언트가 32938 클라이언트 포트로 다시 접속했습니다. 그리고 다시 time 메시지를 주고 받다 (8)에서 하나의 클라이언트가 퇴장하고 그리고 (9)에서 마지막 클라이언트 까지 종료 되었습니다.

32850, 32868 그리고 32938이 각각 클라이언트 #1, #2, #3 입니다.


4. 결론

오늘은 SELECT라는 함수를 이용한 다중 소켓 연결 서버를 만들어 보았습니다. SELECT는 간단한 구조에서 다중의 클라이언트를 받아들일수 있는 방법으로 이용됩니다. 또한 SELECT 기능은 단일소켓연결의 timeout 구현에도 사용할수 있는데 이것은 여러분들이 해보시기 바랍니다.

다음 강좌에서는 PHP의 Process Control Functions을 이용해서 Fork를 이용한 다중 연결 소켓 통신을 해보겠습니다.

신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

from phpschool

이진우

프리랜서
프로그래머

            
          


이문서의 배포는 자유로우나 최소한 제작자의 정보는 제외하지 않고 배포해 주세요.

문서가 존재하는 모든곳에 답변을 드릴수 없으므로 질문은 홈페이지(http://www.jinoos.com)에서만 받습니다.


1. 소개

앞으로 몇번에 걸처서 PHP를 이용한 다중 연결 소켓 통신에 대해서 알아보겠습니다. PHP는 확장 함수로 socket 계열 함수를 지원합니다. 하지만 이것은 컴파일시 --with-socket 옵션을 주어 컴파일 해야 합니다. 클라이언트 소켓에 관한 함수 fsockopen()는 이곳에서 사용하지 않습니다.


1.1. PHP 컴파일 하기

앞으로 설명할 내용들은 일반적인 아파치 모듈로서의 PHP가 아닌 독립적으로 실행 가능한 PHP CGI형태입니다. 이를 위해서 재 컴파일이 필요하며 이때 소켓에 관한 옵션을 추가하게 됩니다.

PHP 4.3.1 버전을 이용했습니다.

#] tar -zxvf php-4.3.1.tar.gz
#] cd php-4.3.1
#] ./configure --with-socket
#] make

make 를 한후 make install 을 하지말고 php-4.3.1 디렉터리를 보면 php 라는 실행파일이 생성된것을 알수 있다. 그럼 이제 프로그램 짤 준비는 다�다. 간단한가? 간단하다. -_-a


1.2. PHP의 소켓 함수

PHP의 소켓 함수 몇가지를 알아보고 넘어 가도록 하자. PHP의 소켓 함수는 C sys/socket.h 에 정의 되어있는 함수들과 매우 유사하다. 참고 하도록 하자.

socket_create ( int domain, int type, int protocol )

소켓의 연결 자원을 생성 합니다. 소켓 함수를 사용할때 기본으로 쓰입니다.

domain 파라메터에는 AF_UNIX 또는 AF_INET 으로 유닉스 도메인 소켓 또는 인터넷 소켓으로 지정합니다.

type 파라메터에는 STREAM, SOCK_DGRAM, SOCK_SEQPACKET, SOCK_RAW, SOCK_RDM, or SOCK_PACKET 등의 소켓의 종류가 설정 됩니다.

protocol 파라메터에는 TCP또는 UDP 설정(SOL_UDP or SOL_TCP)을 합니다.

socket_connect ( resource socket, string address [, int port] )

서버소켓으로 접속할때 사용하는 함수. 소켓 클라이언트에서 사용하는 함수 입니다.

socket_setopt ( resource socket, int level, int optname, int )

소켓에 옵션을 지정합니다. 크게 중요하지 않은 함수 입니다.-_-

socket_bind ( resource socket, string address [, int port] )

서버 소켓에 주소와 포트번호를 부여 합니다. 서버 소켓에서 필수적인 작업입니다.

만약 AF_UNIX로 지정했다면 port 파라메터는 생략이 가능합니다.

socket_listen ( resource socket [, int backlog] )

서버 소켓을 클라이언트 대기 상태로 만듭니다.

socket_listen() 함수를 호출한 이후부터 서버 소켓은 클라이언트가 접속하는지 아닌지 귀를 기울이는 일을 하게 되는겁니다. 서버 소켓 사용에서 필수 적입니다.

backlog 파라메터는 받아들일 클라이언트의 최대 수를 지정합니다. 생략하면 최대 갯수로 설정됩니다.

리눅스에서 소켓의 최대 갯수는 ulimit -a 를 해보시면 open files 항목이 소켓 최대 갯수와 상응합니다.

socket_accept ( resource socket )

클라이언트 접속을 허가 할때 사용하는 함수 입니다.

함수 호출후 새로운 소켓 리소스가 리턴되는데 이것은 기존에 서버 소켓 리소스에서 생성된 클라이 언트와 통신하는 새로운 소켓 리소스 입니다.

socket_getpeername ( resource socket, string &addr [, int &port] )

클라이언트의 접속 정보를 가져 옵니다.

socket_read ( resource socket, int length [, int type] )

소켓에서 length만큼의 정보를 읽어 들입니다.

type 읽어들일 타입을 결정 합니다.

  • PHP_BINARY_READ : 바이너리 타입으로 읽어 들입니다. 4.1.0 포함 버전 이후부터 기본값으로 설정되었습니다.

  • PHP_NORMAL_READ : \r또는 \n이 입력될때 끊어서 읽습니다. 한마디로 라인 단위로 읽는 다는 말이죠. 4.1.0이전 버전에선 기본값.

socket_write ( resource socket, string buffer [, int length] )

소켓에 정보를 써넣는 함수로 마지막 인자를 생략하면 buffer의 길이가 자동으로 대입됩니다.

socket_close ( resource socket )

소켓 연결을 끊는다.


2. 소켓 서버 만들기

이제 실전으로 들어가 보겠다. 오늘 만들것은 간단하게 클라이언트가 접속하면 클라이언트의 접속 정보를 출력하고 클라이언트에게 현재 날자와 시간을 전송한후 종료 합니다.

엄밀히 말하면 오늘 만들 소켓 서버는 다중 사용자를 위한 시스템은 아니다. 하지만 오늘은 간단히 소켓의 개념을 집고 넘어가는 의미로 생각하자.

PHP를 CGI형태로 실행하기 위해서 소스코드 상단에

#!/usr/local/bin/php -q
<?php
..
..
..
?>

처럼 프로그래밍을 합니다. 이것은 Perl스크립트나 기타 쉘 스크립트에서 전처리기를 설정하는것과 같은 방법 입니다. -q 옵션은 http protocol 헤더를 제거 하는 옵션입니다.

#!/usr/local/bin/php -q
<?php
define("_IP",    "123.123.123.12");
define("_PORT",  "65000");

$sSock      = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($sSock, _IP, _PORT);
socket_listen($sSock);
while($cSock = socket_accept($sSock))
{
    socket_getpeername($cSock, $addr, $port);
    echo "SERVER >> client connected $addr:$port \n";
    $date = date("Y/m/d H:i:s");
    socket_write($cSock, $date);
    socket_close($cSock);
    echo "SERVER >> client Close.\n";
}
?>

서버는 지속적으로 클라이언트를 기다리는 형태의 모습을 하고 있습니다.


3. 소켓 클라이언트 만들기 와 실행

클라이언트는 서버에 접속을 하고 서버쪽에서 보내온 메시지를 읽은뒤 출력하고 종료합니다.

주의

프로그램상의 아이피는 가상으로 만든 것입니다. 테스트시 적절한 아이피로 변경해 주세요.

#!/home/dimeclub/www/bin/php/php -q
<?php
define("_IP",    "123.123.123.12");
define("_PORT",  "65000");

$sock      = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, _IP, _PORT);
echo "CLIENT >> socket connect to "._IP.":"._PORT."\n";
$date = socket_read($sock, 4096);
echo "CLIENT >> this time is $date \n";
socket_close($sock);
echo "CLIENT >> socket closed.\n";
?>

너무 간단하다고 돌 날아오겠네요. -_-a. 위 두 파일을 server.php, client.php라 저장하고 실행권한을 준뒤 실행해 보도록 하겠습니다.

아래 화면의 #]는 쉘 커맨드 입니다.

#]
#] ll
total 8
-rwxr-xr-x    1 jinoos   jinoos        387 May  7 04:59 client.php
-rwxr-xr-x    1 jinoos   jinoos        495 May  7 04:59 server.php
#]
#] ./server.php &
[1] 23833
#]
#] ./client.php 
SERVER >> client connected 123.123.123.12:45188 
SERVER >> client Close.
CLIENT >> socket connect to 123.123.123.12:65000
CLIENT >> this time is 2003/05/07 04:59:49 
CLIENT >> socket closed.
#]
#] ./client.php 
SERVER >> client connected 123.123.123.12:45358 
SERVER >> client Close.
CLIENT >> socket connect to 123.123.123.12:65000
CLIENT >> this time is 2003/05/07 05:01:03 
CLIENT >> socket closed.
#]
#]

위 내용은 서버프로그램을 백그라운드로 실행하고, 짧은 시간동안 두번 클라이언트로 서버에 접속한 모습입니다. 원하던데로 출력된것을 확인할수 있습니다.

같은 머신 상에서 접속하게 된다면 서버와 클라이언트에서 같은 IP를 출력하게 될것입니다. 그리고 잘보면 클라이언트의 PORT가 변경되었는데 클라이언트의 머신의 경우 일정 대역의 포트중 사용하지 않는 포트를 자동으로 사용하는 것입니다. 이것을 프로그램상으로 조정하는것은 아닙니다. OS에서 자동적으로 할당된것을 사용하는것입니다.


4. 결론

오늘은 간단하게 PHP를 이용해서 소켓함수를 사용하는 것을 해보았습니다. 다음시간에는 PHP의 Process Control Functions 함수들을 이용해서 Fork를 사용한 오늘 보다 좀더 진보된 다중 접속 서버 소켓을 만들어 보겠습니다.

신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
MySQL에 보면 DATEDIFF나 TIMEDIFF 함수를 쓰면 두 날짜 사이의 차이를 구할 수 있다.
사실 이 함수를 사용하는 것이 더 편하다.
그런데 이 함수의 사용법을 알면서도 착각을 했는지...
PHP로 그만 다시 만들어 버리고 말았다 -.-;;

function dateDiff($sStartDate, $sEndDate)
{
    $sStartTime = strtotime($sStartDate);
    $sEndTime = strtotime($sEndDate);

    if($sStartTime > $sEndTime)
        return false;

    $sDiffTime = $sEndTime - $sStartTime;

    $aReturnValue['d'] = floor($sDiffTime/60/60/24);
    //$aReturnValue['d'] = $sDiffTime/60/60/24;
    $aReturnValue['H'] = sprintf("%02d", ($sDiffTime/60/60)%24);
    $aReturnValue['i'] = sprintf("%02d", ($sDiffTime/60)%60);

    return $aReturnValue;
}

그냥 다음과 같이 쓰면 배열로 리턴한다.

$aDiffTime = dateDiff('2007-01-01 00:00:00', '2007-01-03 23:00:00');
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret

출처 : TestMaster 님의 블로그

시스템 모니터링이라고 하면 뭔가 복잡하고 어려운 것부터 생각되고 실제로 귀찮은 작업이기도 합니다. 이유있는 문제도 있지만 그렇지 않은 경우도 많습니다. 시스템 에러의 원인을 알 수 없을 때는 참 답답합니다. 그렇다면 그 해결책을 무엇일까요? 스스로 간단한 시스템 모니터링 툴을 만들어 보는 것입니다. SNMP에 대한 기본 지식이 있다면 PHP를 이용해 간단하게 만들 수 있습니다. 리눅스에서 PHP와 SNMP를 이용한 간단한 시스템 모니터링에 대해 알아보겠습니다.

more..


신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
처음으로 팁텍을 올리는 군요... ^^;;
이미지 파일일 경우... 파일을 업로드 하기 전에 미리 크기를 체크하는 스크립트입니다.
다른 파일일 경우에는 안 해 봤는데... 아마도 안될꺼 같구요...^^;;
이미지 파일은 잘 되는 거 같습니다.^^;;

다음은 소스입니다...

***************************************************************************

신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
음... 안올릴려다가.. ㅋ
그냥 올립니다. ^^
제목에 허접이라고 달려 있으니 딴지는 반사.. ^^

우선 ERD처럼 만든 다음 CREATE 쿼리 만드는 것은 EXPORT로 하면 쉬운데..
현재 돌고 있는 디비를 ERD처럼 만드는 것은 해보질 않아서..
조금 해멨습니다. ^^;;
다음과 같이 하면 됩니다.

1. DB Designer를 설치합니다. ^^
없으면... http://down.clubbox.co.kr/studyweb/cnr1 에서 다운로드..
이거 클럽박스 링크이니.. 다운로드가 별로 없으면... 삭제 되니 그 이후에는 알아서들 구하시길...(아마도 GPL이니.. 라이센스 문제는 없을 듯..)

2. File > New 해서 빈 문서 하나를 만듭니다.

3. Database > Reverse Engineering 합니다.

4. 접속하고자 하는 디비를 선택합니다. 접속하고자 하는 디비가 없을 경우에는 New Database Connection을 통해 새롭게 커넥션을 만듭니다.

5. 옵션 설정하고... Execute 합니다. ^^;

6. 그럼 화면에 ERD 처럼 죽 뜹니다.

7. 저장합니다.. ㅋ 이미 사용해 보신 분은 아시겠지만 XML 형태로 저장됩니다.


이상 허접팁이었습니다.
언제나 허접한 체리필터.. ^^;;
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

트랙백이 하나이고 , 댓글  2개가 달렸습니다.
  1. 크허허허. 디비디자이너를 쓰고 계시다니.. 갑자기 마구 반갑습니다. ㅜㅡ
    • 앗... 상용에서 안쓰고요..
      전에 잠깐... 테스트 용도로 해보기만 한거에요 ^^;;
      지금은 ERWin이나 SyBase의 PowerDesigner를 씁니다. ^^
secret
우선 부모 프레임이 있는 주소는 http://aaa.com입니다.
그리고 프레임 내에 들어갈 주소는 http://sub.aaa.com입니다.

부모에서 자식을 제어하던, 자식에서 부모를 제어하던... 서로 호스트가 틀리면... 억세스 권한이 없다고 나오죠.. ^^
그럴 경우... 다음과 같이 해주면 됩니다.

1. aaa.com의 문서 내에 다음과 같은 자바스크립트를 넣습니다.

<script language="javascript">
document.domain = "aaa.com";
</script>

2. 그리고 sub.aaa.com에도 다음과 같이 합니다.

function eventLoad() {
document.domain = "aaa.com";

아이프레임 늘리는 소스...
}

물론... 아이프레임 늘리는 소스는 이전 팁들에서 검색하시면 많이 나옵니다. ^^

주의할 점은 아예 다른 도메인간에는 안되며, 같은 도메인, 틀린 호스트에서만 됩니다.
또한 ip로 접속해도 에러가 납니다. ^^
자바스크립트 보안도 그런대로 괜찮네요. ^^ ㅋ

그럼 오늘도 즐프... ^^
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
제가 올리는 팁이 항상 그렇듯이...
실력 좋은 분들은 패쓰해야 하는 허접한 팁입니다.
그점 감안하고 봐 주세요 ^^;;

일반적으로 php 프로그래밍은 길어봐야 수초 내에 끝나는 것이 대부분이다.
하지만, 메일을 보낸다거나 할 경우에는 소스 상단에 set_time_limit(0); 를 추가해서 보내는 경우가 있다.
물론 돈이 많거나, 실력이 뛰어난 프로그래머라면, 좋은 발송기를 사거나, 좋은 프로그램을 짜서 보내면 되지만,
나와 같이 허접한 실력의 프로그래머라면, php로 해결하는 수 밖에 없다 ^^;;;

보통의 경우... 대량 메일을 보내기 위해서는 서버단 설정을 먼저해야 한다.
요즘 나오는 센드메일은 멀티 큐를 지원하고 있고, 큐메일은 예전부터 멀티큐를 지원하기 때문에,
메일 서버 자체에서는 별로 설정할 일이 없다.
만일 메일 서버단의 설정이 궁금하다면, 멀티큐로 검색해 보면 많이 나올 것이다.

오늘 여기서 다루고자 하는 것은...
php 프로그램으로 대량 메일을 어떻게 보내야지만 좋을까 하는 것이다.
사장님의 압력에 몇번의 실패 끝에 알아내게 된 내용을 적어본다.

1. rcpt to를 이용한 방법

처음에 선임자가 짜 놓은 프로그램을 보니 smtp class를 이용해서 보내는 것이었다.
그런데 10만통을 5분에 쏜다고 사장님께서 말씀하시길래 이해가 안가서 소스를 뒤적여 봤더니...
헤더를 조작해서 보내는 것이었다 -.-;;
즉 메일의 rcpt to에다가

aaa@hanmail.com,bbb@hanmail.com,ccc@hanmail.com...........zzz@naver.com

이런식으로 해 놓고, 헤더의 receive에는 그냥 '회원님' 하고 보내는 것이었다.

그러면 받는 사람한테는 rcpt to의 내용이 보이지도 않으면서, php 프로그램 상에서는 메일을 한통만 쏘는 효과가 있었던 것이었다 -.-;;
물론... 센드메일이나 큐메일은 rcpt to에 콤마로 이어놓은 메일 수 만큼 bacrground로 열심히 메일을 뿌리고 있겠지만... ^^;;

하지만 이 방법의 문제점은 header의 to 정보와 rcpt to의 to 정보가 불일치 하기때문에,
대부분의 메일서버가 스팸으로 분류한다는 단점이 있다 -.-;;
결국 보내봐야 소용없는 메일이 되고 만다 -.-;;
따라서 다른 방법을 찾아봐야만 했다.

2. smtp로 직접 접속해서 보내는 방법

smtp로 보내는 방법은 여러가지 설정화 헤더 정보 등을 임의로 입맛에 맞게끔 수정해서 보낼 수 있다.
하지만, 브라우져라면 한번에 한 창을, 실행파일이라면 한번에 한 프로세스만을 띠울 수 밖에 없다.
smtp 프로토콜을 이미 하나의 프로세스가 잡고 있기에, 다른 프로세스는 대기할 수 밖에 없기 때문이다.
따라서 php의 mail() 함수를 사용해서, smtp를 물고 있지 않더라도, MTU에 그냥 메일만 던져주고 빠지는 방식을 쓸 수 밖에 없었다.

3. 콘솔 상에서 보내는 방법

보통 브라우져로 메일을 보내게 되면, 브라우져=>아파치=>php=>MTU 의 단계를 거치게 된다.
또한 브라우져는 http 통신을 하게 되므로 지속적인 연결을 하면서 메일을 발송하는 것에는 조금 불안한 감이 있다.
따라서 php를 binary 버전으로 컴파일 한 후... 아파치와는 독립적으로, perl이나 sh 파일처럼 콘솔 상에서 직접 실행함으로서 조금은 안정적으로 보낼 수 있다.

* 문제점

본인의 경우에는 업체명을 쿼리한 후... 해당 업체에 소속된 회원에게 메일을 발송하는 작업이었다.
메일을 보내는 헤더정보와 메일 내용의 footer 부분에 해당 업체의 정보가 들어가야 하기에 루푸문 안의 쿼리는 어쩔 수 없었다.

업체의 정보와, 회원의 메일 정보를 가져오는 디비쿼리의 경우에는 메일을 보내는 동안 디비 커넥션을 계속 물고 있어야 하기에,
디비 클래스를 조금 수정하여, 해당 정보를 배열로 받고 난 후 접속을 끊어 버리는 형태로 만들었다.
하지만, 이 또한 문제점이 발생하였다.

ex)



위와 같은 소스로 메일을 발송하다보면... 중간에... 다음과 같은 에러가 발생한다



커넥션이 끊어졌다는 것이다. -.-;;
바로 업체 정보를 가져오는 $sComQuery를 실행할 수 없어서 에러가 난 것이다.
그러면서 그 이후의 메일 발송은 바로 stop이 되는 난감한 상태가 발생한 것이다.
그렇다고 해서 connection 타임을 mysql 설정 단에서 늘려주면, 일반 웹서비스로 인해 디비커넥션이 full 되서 디비서버 자체가 죽을 우려가 있었기 때문이다.

조금의 고민 끝에... 커넥션이 끊어 졌으면 pconnect를 써야 겠다 라는 생각에 클래스를 pconnect로 접속하도록 바꿨지만, 이 역시 마찬가지 결과를 가져왔다.

그래서 결국에는 '커넥션 끊어지면, 다시 커넥션 하면 되지 뭐 -.-;;' 라는 생각에...
is_resource 함수를 사용하기로 했다 ^^;;

******* 바꾼 소스... ************


그런데... 다시 생각해 보니 $conn은 원래부터가 resource가 아니였던 것이다. -.-;;
메뉴얼에도 잘 나와 있다시피...



처럼 접속 했을 때 $db_link가 resource인 것이지, new를 통해 생성한 객체는 object 였던 것이다 -.-;;

그래서 결국에는 class에 메쏘드 하나를 추가하기로 했다. -.-;;




그리고 나서 위에 소스도 다음과 같이 수정했다.

******* 최종 소스... *********


아직까지는 잘 돌아가고 있으며, 메일 10만통을 보내는 데 1시간 정도 걸린다.(물론 콘솔을 4개 띄워서 동시에 4개의 프로세스로 돌린 경우이다 ^^;;)
php로 한시간 정도 에러 안나고 돌아가면... 성공한 것 아닌가? ㅋㅋㅋ

어째든... 아무런 에러 없이 계속 돌아가 주기만을 바랄뿐이다 ^^
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글  9개가 달렸습니다.
  1. is_resource 함수 필요 없네요 -.-;;
    역시나 에러 발생합니다.
    그래서 무조건 커넥션 다시 맺게끔 하고 테스트 해 봤더니, 기존에 커넥션 맺어져 있으면, 다시 안맺네요... ㅋ
    끊어 졌을 경우에만 다시 커넥션을 생성하구요...
    그래서 무조건 커넥션 맺도록 수정했습니다. ^^
  2. 저라면 상단에 메일연락처를 별도파일로 작성한후에 해당 파일을 읽으면서 내용수정하고 메일발송하고 발송결과를 해당파일이나 연관파일에 업데이트 하는 방식을 사용하겠습니다.
  3. 똥파리//이미 스쿨에서 그런 내용들이 나왔는데... 그냥 이전 블로그 폭파되서 다시 올려놓은 팁이에요 ^^
    다시 하게 된다면... 좀더 잘 하게 되겠죠 ㅎㅎ
  4. 비밀댓글입니다
  5. 좋은글 잘 읽었습니다^^
    php를 binary 버전으로 컴파일 한 후 콘솔에서 실행시킨다는 게 어떤건지 궁금합니다. 제가 php입문한지 얼마 안되어서요. 좀 자세히 알려 주실 수 있으실련지요?^^
    • 요즘은 그냥 리눅스에서 설치하면 실치 디렉토리 안에 php 실행 파일이 있더군요.
      고놈을 쉘 실행시키듯이 실행하면 됩니다.
      구글링 해보면 많이 나옵니다 ^^
  6. 포스팅 되어 있는 다중연결 소켓에 나오는 방법을 말씀하시는 거죠?^^
    #!/usr/local/bin/php -q
secret
vi 에서 Ctrl+v를 하게 되면 Visual Block으로 되어서 같은 열에 있는 글자들을 잡을 수가 있다.
또한 Ultra Editor에서도 '열모드'라는 버튼을 클릭하면... vi의 Visual Block과 같은 효과를 가진다.

그동안... Edit+ 이 개인적으로 맘에 듦에도 불구하고 Visaul Block이 되지 않는다고 생각해서 아쉬워 했는데...
알고 보니... Visaul Block이 지원되고 있었다. -.-;;
다음과 같이 하면... 된다...


1. 에디트 플러스의 '자동 줄 바꿈' 모드를 off 해 놓는다.

2. 그 후 Alt를 누른 상태에서 마우스로 Visual Block 형태로 잡아본다.

너무나 쉽게 Visual Block이 되는 것을 보고...
또하나 발견하게 된 기쁨을 가지게 되었다 ^^
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
php 프로그램을 짜다 보면... 같은 반복 잡업으로 인해 귀차니즘을 느끼는 경우가 많다.
이럴 경우 그냥... copy & paste 처럼 되는 것은 없나 라고 느끼게 된다.

에디트 플러스에 보면 '자동완성' 기능이란 것이 있다.
나도 거의 사용을 안하다가 오늘에서야 이것 저것 찾아보다가 알게 되었다.

* 사용방법

"도구->기본설정->설정및구문강조->자동완성" 에 자동완성 파일(*.acp)을 선택해 주면 된다.
자동완성 파일은 직접 텍스트로 만들어 주면 되며, 정확한 문법은 모르지만, 기초적인 내용 하나만 알고 있으면 어느정도 적용 가능한 듯 하다.

* example

#T=if
if(^!) {
}
#

if를 쓰고 나서 한칸 띄우면

if() {
}

가 자동으로 타이핑 되며, "("와 ")" 사이에 커서가 위치하게 된다.
즉 "^!"라고 표시된 곳에 커서가 위치하게 된다.

이러한 방법을 알고 직접 적용 시켜 본 예제이다.

#T=foreach
foreach(^!=>) {
}
#

#T=switch
switch(^!) {
    case :
    break;

    default:
}
#

#T=_SERVER
$_SERVER['^!']
#T=_REQUEST
$_REQUEST['^!']
#T=_POST
$_POST['^!']
#T=_GET
$_GET['^!']
#T=_FILES
$_FILES['^!']
#T=_SESSION
$_SESSION['^!']
#T=_COOKIE
$_COOKIE['^!']
#T=_ENV
$_ENV['^!']


editplus.com에 가보면, 이와 관련된 많은 acp 파일들이 있다.
이중 하나를 선택해서 자신의 입맛에 맞게 조금씩 수정해 가면서 사용하면 좋을 듯... ^^
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
자바스크립트에서 폼의 객체 이름을 "objName[]" 식으로 지을 경우가 있다.
보통 php쪽에서 배열로 받기 위해서 위와 같이 짓는데...
이를 자바스크립트에서는 배열로 인식하지 못해 조금 난처한 경우가 많다.

가령 다음과 같은 경우 오류가 난다

<HTML>
<HEAD>
</head>

<script language="JavaScript">
<!--
function objLength() {
   alert(document.testForm.checkObj[].length);
}
//-->
</script>

<form name="testForm">
<input type="checkbox" name="checkObj[]"> aaa
<input type="checkbox" name="checkObj[]"> bbb
<input type="checkbox" name="checkObj[]"> ccc

<a href="javascript:objLength();">체크박스의 개수는?</a>
</form>

이럴 경우에는 다음과 같이 처리해 주면 된다.



<script language="JavaScript">
<!--
function objLength2() {
   alert(document.forms['testForm2']['checkObj[]'].length);
}
//-->
</script>

<form name="testForm2">
<input type="checkbox" name="checkObj[]"> aaa
<input type="checkbox" name="checkObj[]"> bbb
<input type="checkbox" name="checkObj[]"> ccc

<a href="javascript:objLength2();">체크박스의 개수는?</a>
</form>

또한 각 개체로의 접근은 다음과 같이 사용하면 된다...

ex) document.forms['testForm2']['checkObj[]'][0].value


이 문제로 조금 머리 아파 하던 차에... 해결방법을 알게 되어서 올립니다. ^^
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
$_FILES의 error가 2번이 나오는데 왜 그러냐는 질문을 오늘 받았습니다.
그래서 메뉴얼을 살펴 본 결과...
html에서도 max file size를 제어할 수 있다는 것을 알았습니다. ^^

html 폼 안에 hidden으로 다음과 같이 넣어주면 됩니다.


<form enctype="multipart/form-data" action="_URL_" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="30000" />
이 파일을 전송합니다: <input name="userfile" type="file" />
<input type="submit" value="파일 전송" />
</form>


여기서

<input type="hidden" name="MAX_FILE_SIZE" value="30000" />

이 부분이 중요합니다.
요기에다 업로드 max value(M*1024)를 적어 주시면 됩니다.
그러면 서버측에서 에러 메세지($_FILES['userfile']['error'])가 2로 나오거나... php 4.3.0 이상에서는 UPLOAD_ERR_FORM_SIZE 라고 나옵니다.

메뉴얼에는 다음과 같이 나오네요

"MAX_FILE_SIZE는 PHP가 확인하기도 하지만, 브라우저에 대한 권고입니다. 이 값을 변경하는건 매우간단하기에, 크기가 큰 파일을 막기 위해서는 이 기능에 의존해서는 안됩니다. 대신, 최대 크기에 관한 PHP 설정은 속일 수없습니다. 그러나 MAX_FILE_SIZE 폼 변수는 사용자가 파일이 너무 크다는 것을 파악하기 위해서 실제 전송을 하는 동안기다릴 필요를 없애줍니다.

"사용자가 파일이 너무 크다는 것을 파악하기 위해서 실제 전송을 하는 동안 기다릴 필요를 없애줍니다" 라는 말뜻을 정확히 못알아 들어서 직접 테스트 해 보니...
MAX_FILE_SIZE까지만 올리고.. 그 이상 더 업로드가 되면 중지하는 거 같습니다. ^^
업로드 하기 전에 체크 해 주면 더 좋을 텐데요 ^^;;
어째든... 오늘 새로운 것을 알게 되었습니다. ^^
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글이 없습니다.
secret
window open을 할 때 똑같은 사이즈를 줘도,
sp2에서 팝업창 아래 상태바가 있는 경우와 그외의 os에서 상태바가 없는 경우 창 안의 내용들이 달라 보이는 경우가 있다.

또한 앞으로 나올 IE7의 경우에는 창 상단에 주소까지 나와서 많이 틀려보이는 경우가 발생한다.

이때 다음과 같은 스크립트를 사용하여 onload에 넣어주면, 상당히 깔끔한 window resize가 된다.




cf) Dwidth, Dheight를 제대로 못 알아오는 경우가 발생할 때에는...
적당히 조절 해서 해당 수치를 그대로 넣어주는 수 밖에 없는 듯 하다.

special thanks - 행복한고니님... 귀찮게 질문 드려 알아냈습니다 ㅋ
신고

WRITTEN BY
체리필터
프로그램 그리고 인생...

받은 트랙백이 없고 , 댓글  2개가 달렸습니다.
  1. 좋은 정보 감사드립니다.
    두고두고 참고해서 보려고 제 블로그에 출처 표기한 이후에 스크립트 부분만 데리고 갔습니다(__)

    혹시라도 삭제를 원하시면 이 글의 댓글이나 http://databook.tistory.com/11 여기에 댓글을 달아주시면 바로 조치를 취하도록 하겠습니다.

    좋은 정보 감사드립니다. 덕분에 살았습니다 ^^;
secret