HTTP 환경상 아무리 지랄을 해도 지속적인 연결상태를 유지하면서,
서버가 선택적으로 push를 할 수가 없다.
(있다 하더라도 나는 모른다...-_-)


그래서 착안한 게 long polling.
원문출처 : http://en.wikipedia.org/wiki/Push_technology


Long polling

Long polling is a variation of the traditional polling technique and allows to emulate information push from a server to a client. With long polling, the client requests information from the server in a similar way to a normal poll. However, if the server does not have any information available for the client, instead of sending an empty response, the server holds the request and waits for some information to be available. Once the information becomes available (or after a suitable timeout), a complete response is sent to the client. The client will normally then immediately re-request information from the server, so that the server will almost always have an available waiting request that it can use to deliver data in response to an event.

Long polling is itself not a push technology, but can be used under circumstances where a real push is not possible.




일반적인 polling은 client가 주기적으로 server에 request를 보내고,
server는 어떤 정보가 있든 없든, 없으면 빈값이라도 client에게 response 하는 식이다.
그게 일정한 interval을 가지고 주기적으로 이루어진다.

long polling도 기본적으로 client가 server에 request를 보내고,
서버는 정보를 response 하는 걸로 1 cycle이 완성되는 구조이지만,
다른 점은 유효한 정보가 없다면 빈값을 보내는 게 아니라 client를 지그시 물고 있는 것이다.
그러다가 유효한 정보가 생기면 client 에 보내고,
client는 정보를 받는 즉시 다시 request를 보내는 식으로 해서 마치 영구적인 연결이 이루어진 척 하는 것이다.

주기적으로 발생하는 트래픽을 없앨 수 있다는 점에서 좋은 기술인 것 같다.
하지만 server에서 유효한 정보를 얻는 루프과정이 부하가 심하다면 안하느니만 못하다.
유효한 정보를 얻는 건 daemon이 처리하고 client의 요청을 처리하는 페이지는 단지 결과값만 검사하는 로직이면 좋을 듯.


아래 그림처럼 daemon이 결과값을 주기적으로 모으고,
client 의 요청을 받은 server page는 그 결과값과 client의 요청값을 비교연산해서,
유효한 값이 되었을 때 response 하는 형태라면 좋다고 생각한다.









다음은 가상으로 만들어 본 형태이다.
데몬은 DB의 메일갯수를 읽어와서 사용자ID로 된 디렉토리에 현재 메일 갯수를 파일명으로 가진 파일을 생성시키고, server page는 GET으로 받은 ID와 현재 메일갯수를 이용하여 결과값과 비교하여 변동이 있을 경우 client에 response 한다.
client는 최초 로딩시 현재 메일갯수를 읽어와서 값을 저장해 두고 server page에 ajax 통신을 한다.



daemon
php로 만든 데몬이다...-_-;
getMailCountFromDB() 라는 함수로 정보를 읽어와 touch를 이용해서 파일을 생성해 결과값을 갱신한다.

#!/usr/local/bin/php -q
<?php
$Interval = 3;

while (true) {
    // DB로부터 메일갯수를 읽어옴
    // 연관배열형태로 'ID', 'mailCount' 키를 가지는 다중배열
    $result = getMailCountFromDB();

    foreach ($result as $v) {
        $userDir = $_SERVER['PWD'].'/'.$v['ID'].'/';
        // 디렉토리가 없으면 생성
        if (file_exists($userDir)==false) {
            mkdir($userDir);
            chmod($userDir, 0777);
        }
        system("rm -rf ".$userDir."/*");
        // 메일갯수를 나타낼 결과값
        system("touch ".$userDir.$v['mailCount']);
    }

    sleep($interval);
}
?>






server page
넘겨받은 ID와 이전 메일갯수를 토대로 결과값과 비교하여 변화가 생길 경우 response

<?php
// 시작시간
$startTime = time();

// client를 물고 있을 최대시간
$maxConnectionTime = 60;

$userDir = dirname($_SERVER['SCRIPT_FILENAME']).'/'.$_GET['userID'].'/';

// $maxConnectionTime 초가 지날 때까지 클라이언트를 물고 있음
// 유효한 정보가 생기면 response

while ((time()-$startTime)<$maxConnectionTime) {
    if (file_exists($userDir)==true) {
        // 메일갯수를 가져온다. 단순히 ls로...-_-;
        $mailCount = exec("ls ".$userDir);
        // 만약 이전 메일갯수와 다르다면 response
        if (intval($mailCount) != intval($_GET['mailCount'])) {
            echo $mailCount;
            exit;
        }
    }
    sleep(1);
}
?>





client
초기에 ID, 메일갯수를 세팅하고 long polling 을 이용해 메일갯수를 갱신시킴.
setInterval을 0.1초로 주고 response가 오면 즉시 재접속하는 형태.

<script>
// 사용자ID, 메일갯수
var userID = "<?=$_GET['userID']?>";
var mailCount = <?=$_GET['mailCount']?>;

// Ajax통신 객체 초기화
var ajax = null;
if (window.XMLHttpRequest) {
    ajax = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
    try {
        ajax = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
        ajax = new ActiveXObject("Microsoft.XMLHTTP");
    }
}

// polling
var pollingComplete = true;
function getData() {
    if (pollingComplete==false) return;

    ajax.open("GET", "get.php?userID="+userID+"&mailCount="+mailCount);
    ajax.onreadystatechange = function () {
        // 조건식은 그냥 장난이다...-_-;
        if (typeof ajax.responseText == "string") {
            var mailCountFromServer = ajax.responseText;
            // 만약 메일카운트가 갱신되었다면 적용
            if (mailCountFromServer != mailCount) {
                mailCount = mailCountFromServer;
                document.getElementById("mailCount").innerHTML = mailCount;
            }
            pollingComplete = true;
        }
    }
    ajax.send(null);
    pollingComplete = false;
}

// polling 타이머 가동
window.onload = function() {
    document.getElementById("userID").innerHTML    = userID;
    document.getElementById("mailCount").innerHTML = mailCount;
    setInterval(getData, 100);
}
</script>




ID : <span id="userID"></span>
<p>
mail : <span id="mailCount"></span>



 

Posted by bloodguy
,