Real time Apps #3 - LongPolling


참조 URL
  1. http://techoctave.com/c7/posts/60-simple-long-polling-example-with-javascript-and-jquery
  2. http://tkjeon.tistory.com/entry/Jquery-Jquery-폴링Polling-기법
  3. http://msdn.microsoft.com/ko-kr/library/dd449174.aspx - TaskCompletionSource<TResult>


목차
  1. Polling - 있어?
    [ASP.NET MVC] - Real time Apps - Polling
  2. LongPolling - 있으면 보내줘!
    [ASP.NET MVC] - Real time Apps - LongPolling
  3. SSE ( Server Send Events ) - 있으면 보내줘 기다릴께~ 
    [ASP.NET MVC] - Real time Apps - SSE ( Serve Sent Events )
  4. WebSocket - 어! 왔네~
    [ASP.NET MVC] - Real time Apps - WebSocket
  5. SignalR - over WebSocket ( MS Platform )
    [ASP.NET MVC] - Real time Apps - SignalR over WebSocket


 이 포스트에 있는 내용이 언제나 확실한 정답은 아닙니다. 진실이라고 생각해 왔던 전제가 시간의 지남에 따라 들어나지 않았던 다른 이면 때문에 좋은 방향으로 이끌어 낼 수 있는 역할로 변환 되는게 역사적으로도 많은 증명 있었습니다. 그렇지만 저는 현재 상황에서 최선의 답을 찾고자 노력하였으며 이 글을 읽는 다른 분들에게 다음 길을 갈 수 있도록 도와주는 디딤돌이 되고자 노력하고자 포스팅을 통해 공유하고자 하는 것입니다. 그리고 프로그래머라는 타이틀을 달고 살아야 한다면 "왜"라는 의문을 항상 가지고 다니면서 자신의 위치에 안주하지 않고 항상 노력하는 모습으로 살아 가고자 합니다. 언제든 지적이나 오류가 있으면 피드백 부탁 드리겠습니다.

ing™       


 이번에는 LongPolling에 대해서 알아 보자. 좀전에 Polling에 대해서 알게 되었다면 별로 어렵지 않게 이해를 할 수 있을 것으로 예상한다. 그냥 Polling은 주기적으로 서버에 요청하는 방식이지만 LongPolling 방식은 한번 요청하고 서버에 데이터가 있을때만 반환받는 방식이다. 아래 '그림3'으로 보면 좀더 쉽게 이해할 수 있을 것이다.


[그림3] LongPolling 방식


 1. Request1로 서버에 요청하고 서버에서 Event( or Data )가 발생되면 해당 형식( 여기서는 Message라고 정의 )을 브라우저에 보내주고 브라우저 커넥션은 끊어진다.

 2. 받은 데이터를 처리하고 곧바로 서버에 Request2를 요청하고 데이터가 발생할 때까지 대기한다.

 3. 1단계를 다시 시작한다. 


 이와 같은 단계로 LongPolling 방식이 구동이 된다. Polling와 LongPolling 방식은 전체적으로 비슷한 방식을 개발할 수 있으나 다른점은 'LongPolling'이라는 이름처럼 길게 대기하여 서버에 응답요청을 한다는데 있다. 길게 기다리다가 "있으면 보내줘!"와 같은 컨셉이다. 이제 실제 코드로 알아가 보자.


<!-- Polling 하여 받은 데이터를 반영할 수 있는 DOM -->
<ol id="messages"></ol>
 
@using (Ajax.BeginForm("PostMessage"new AjaxOptions())) { 
    <p>
        Say : @Html.TextBox("messageText")
        <input type="submit" /> 
    </p>
}

<!-- 위 코드는 아래의 HTML Tag로 변환된다. <form action="/LongPolling/PostMessage" data-ajax="true" id="form0" method="post">    <p>         Say : <input id="messageText" name="messageText" type="text" value="" />         <input type="submit" />      </p> </form> -->

@section scripts{ <script type="text/javascript">     $(function () {         // Begin the LongPolling loop         getNextMessage();         // Form의 Submit 이벤트  통제         $("form").submit(function () {             var form = $(this);

            // messageText의 Html tag에 입력된 값을 가져온다.              var txtMessageText = $(form.find('#messageText'));

            // Ajax로 서버에 입력한 값을 넘겨준다.             $.post('@Url.Action("PostMessage")', { 'messageText': txtMessageText.val() }, function () {                 txtMessageText.val('');                 txtMessageText.focus();             });                              // HTML의 Submit가 일어나지 않도록 한다.             return false;         });     });     // 서버에 메시지가 있는지 문의 요청     function getNextMessage() {         $.post("@Url.Action("GetNextMessage")", function (message) {             $("<li>").text(message).appendTo("#messages");             // 응답받은 즉시 다시 요청             getNextMessage();         });     } </script> }

[코드3] LongPolling - Browser의 javascript 코드


 위 코드에서는 '그림3'과 같이 사용자의 입력을 받는 부분이 있다.


[그림4] LongPolling 테스트 대기 화면


 위 '코드3'의 전체적인 흐름을 파악해 보자

- HTML이 완성되면 getNextMessage()를 호출하여 서버에 요청을 한다.

- Html의 form에서 submit 버튼을 눌렀을 때 페이지 이동이 일어나지 않도록 처리한다.

- getNextMessage() 함수는 서버에 GetNextMessage()를 호출하고 반환 받을 때까지 대기한다.

- 사용자에게 데이터 입력을 받고 제출 버튼을 누르면 ajax의 Post 방식으로 PostMessage 함수를 호출하여 넘겨준다.


  이제 서버측 '코드4'에 대해서 알아 보자


/// <summary>
/// 요청이 오면 임의의 시간 대기 후 응답
/// </summary>
/// <returns></returns>
public async Task<string> GetNextMessage()
{
    return await _nextMessage.Task;
}
 
/// <summary>
/// Lock object
/// </summary>
static object _nextMessageLock = new object();

/// <summary>
/// for LongPolling object
/// </summary>
static TaskCompletionSource<string> _nextMessage = new TaskCompletionSource<string>();
 
public void PostMessage(string messageText)
{
    lock (_nextMessageLock)
    {
        // Concurrent 해결 하기 위해 임시 변수에 옮긴다.
        var oldNextMessage = _nextMessage;
        _nextMessage = new TaskCompletionSource<string>();
        oldNextMessage.SetResult(messageText);
    }
}

[코드4] LongPolling - Server side 코드


 1. GetNextMessage 함수가 호출이 되면 return await _nextMessage.Task 구문에서 리턴값이 있을 때까지 비 동기로 대기 한다.

 2. PostMessage 함수가 호출 될 때 위에서 대기하고 있던 객체인 _nextMessage 객체에 SetResult(msg)에 값을 넘겨주면 1에서 대기하던 객체는 해당 값을 반환하도록 해주며 다시 대기하도록 한다. 이때 Lock를 통해 쓰레드 안정성을 확보하도록 하였다.

 3. TaskCompletionSource<string> 객체는 입력값이 string이고 출력 값이 string인 객체로 선언되었으며 비 동기로 동작할 수 있도록 .Net Framework 4에서 부터 제공하는 클래스다.

 4. public async Task<string> GetMessage()는 비 동기로 수행된다고 알려주는 C# 5.0 예약어다. 이 함수 안에는 반드시 await 예약어를 사용하여 비 동기로 사용하는 구문이 있어야 한다. - 이 부분은 node.js의 특징인 비 동기 방식을 .Net에서 지원하는 것이라 이해해도 되겠다.


 이제 LongPolling에 대해 전체적인 흐름을 이해했으리라 생각이 든다. 지금까지 살펴보았다면 꼭 실습으로 한 번씩 테스트를 해 보기를 바라며 정 여건이 되지 않는다면 마지막 편에 제공되는 전체 소스파일을 다운바다 실행해 보기 바란다.


 마지막으로 결과 화면으로 확인해 보자 아래 '그림5'를 통해 확인 할 수 있다.




[그림5] LongPolling 결과 화면


위 화면에서 제출을 누르면 “5.Step #5" 밑에 "6. Step #6"이 추가 됨을 확인 할 수 있을 것이다.


소스 코드 자체에 주석과 직관적인 코딩으로 충분히 파악이 가능할 것으로 예상하므로 별도의 설명을 생략하도록 하겠습니다. 포스트의 내용이 장황한 설명 보다는 주석과 소스코드 자체 만으로도 이해할 수 있도록 하기 위해 노력하였습니다. 실 개발에서도 적용할 수 있도록 간단하면서도 현실적인 예제 프로그램을 통해 각 소스를 만들고 이해 시키고자 하였으며 실무에 필요한 개발요구 사항들을 해결 하는데 도움이 되고자 노력하였습니다. 그리고 소스와 같이 있는 주석을 이용해 nDoc이나 별도의 자동 Document 제작 유틸로 API 문서를 만드는 데에도 도움이 되었으면 한다. 
※ DOC에 대한 프로그램 정보 Util link

ing™       



+ Recent posts