이 테크팁에서는 개발자들이 SIP 프로토콜의 비동기 통신을 위해 JSR 180 API를 사용하는 방법에 대해 설명합니다.

모바일 애플리케이션은 서버로부터, 또는 서버를 통해 다른 모바일 사용자로부터 정보를 전달 받게 됩니다. 모바일 장치와 서버 사이의 통신이 동기식으로 이루어질 때는 모든 것이 문제없이 작동합니다. 사용자는 요청을 보내기 위한 연결을 시작하기만 하면 됩니다. 그러면 서버는 바로 그 연결을 사용하여 응답을 보냅니다.

HTTP 프로토콜은 전 세계 수많은 사람들이 하루에도 수십억 번씩 웹 사이트나 웹 서비스에 액세스할 때 이루어지는 동기 트랜잭션에 알맞은 프로토콜입니다. 그러나 연결이 끊어진 뒤에 웹 서버가 사용자의 웹 브라우저에 접속해야 하는 경우가 발생하면 상황은 복잡해집니다. 마침 요즈음은 북미 지역의 납세 기간이니 다음과 같은 상황을 가정해 보겠습니다.

웹 애플리케이션을 사용하여 세금 고지서를 제출한 사용자는 작성한 내용이 접수되었는지 아니면 거부되었는지를 어떤 방법으로 알 수 있을까요? 이때 사용자는 비동기 프로토콜(예: SMTP, 즉 전자 메일)을 통해 응답을 받거나 동기 프로토콜(예: HTTP)을 이용한 폴링 메커니즘을 구축하여 납세 서류의 상태를 확인하게 됩니다.

여기서 전형적인 문제가 발생합니다. 모바일 애플리케이션으로 이벤트 알림 메시지를 받기 위해서는 동기 프로토콜을 사용하여 폴링하거나 비동기 프로토콜을 사용해야 합니다. 자바 ME를 상당한 기간 동안 사용한 사용자라면 모든 자바 ME 장치는 MIDP 1.0 이후로 HTTP 프로토콜을 지원한다는 사실을 이미 알고 계실 것입니다. 따라서 이 테크팁의 목적은 개발자들이 SIP 프로토콜을 이용하여 비동기 통신을 할 때 JSR 180 API를 사용하도록 하는 것입니다. 다행히 JSR 180이 MSA 표준에 포함된 덕분에 이 API를 지원하는 모바일 장치는 점점 늘어나고 있습니다.

아래의 샘플 코드에는 SIP 프로토콜과 JSR 180 API를 사용하여 비동기 방식으로 통신하므로 사이에 SIP 서버/프록시를 두지 않아도 되는 MIDlet 2개가 나와 있습니다. 이 예제를 실행하는 데 필요한 자료는 다음과 같습니다.

송신 측과 수신 측 애플리케이션을 실행하기 위해 컴퓨터가 두 대나 필요한 것이 아닙니다. 한 컴퓨터에서 두 예제를 동시에 실행하면 됩니다.



SipReceiver.java MIDlet

여기서는 여러분이 이전에 MIDlet을 만들어 본 경험이 있다고 가정하고, MIDlet의 수명 주기나 구성자에 대해서는 자세히 설명하지 않겠습니다. 필요에 따라 학습 과정: MIDlet 수명 주기, 무선 개발 튜토리얼 1부 또는 이동성 자바 기술 소개 등 몇 가지 튜토리얼을 통해 자바 ME에 대한 기본 지식을 빠르게 익힐 수 있습니다. 일단 코드의 첫 행부터 살펴보겠습니다.

import javax.microedition.sip.*;

public class SipReceiver extends MIDlet implements CommandListener{

	public Display display;
	public long startTime;
	public Form form;
	public TextField portTextField;
	public ImageItem userImageItem;
	public Image userImage;
	public Command startCommand;
	public Command exitCommand ;
	public SipConnectionNotifier scn = null;
	public SipServerConnection ssc = null;

SipServerConnection 클래스는 포트에 바인딩하고 수신되는 요청을 청취하는 클래스입니다. 모든 것이 제대로 작동하도록 하기 위해 SipServerConnectionSipConnectionNotifier와 긴밀하게 협력합니다. MIDlet에 SIP 요청이 수신될 때 알림 메시지를 받으려면 이 SipConnectionNotifier라는 인터페이스를 구현해야 합니다. 클래스 선언에서 보듯이 여기서는 SipConnectionNotifier 인터페이스를 구현하지 않았습니다. 저는 모든 I/O를 내부 클래스로 처리하는 편을 선호하기 때문입니다. 아래의 코드 단편에 제가 사용하는 내부 클래스 DataTransfer가 나와 있습니다.

   class DataTransfer extends Thread implements SipServerConnectionListener {

      public void run(){

         try {
            if(scn != null)
               scn.close();
            scn = (SipConnectionNotifier) Connector.open("sip:" + portTextField.getString());
            scn.setListener(this);
            form.append("Listening to port: " + scn.getLocalPort());
         } catch(Exception ex) {
            ex.printStackTrace();
         }
      }

      public void notifyRequest(SipConnectionNotifier scn) {
         try {
            ssc = scn.acceptAndOpen();
            if(ssc.getMethod().equals("MESSAGE")) {
               String contentType = ssc.getHeader("Content-Type");
               String contentLength = ssc.getHeader("Content-Length");
               int length = Integer.parseInt(contentLength);
               if((contentType != null) && contentType.equals("text/plain")) {
                  InputStream is = ssc.openContentInputStream();
                  int i=0;
                  byte testBuffer[] = new byte[length];
                  i = is.read(testBuffer);

                  String message = new String(testBuffer, 0, i);

                  form.append(new StringItem("Subject:", ssc.getHeader("Subject")));
                  form.append(new StringItem("Message:", message));
               }
               ssc.initResponse(200);
               ssc.send();
                }
       
         } catch(IOException ex) {
            form.append("Exception: "+ex.getMessage());
         }
      }
   }

여러분은 다음 두 가지 사실을 눈여겨 보셨을 것입니다.

  • 모든 I/O 차단 작업은 별도의 스레드에서 진행되어야 하므로 제가 사용한 내부 클래스는 스레드를 확장합니다.
  • 그리고 이 내부 클래스는 앞서 설명한 SipConnectionNotifier를 구현합니다. 이 클래스가 SipConnectionNotifier를 구현하므로 새 요청이 들어오면 notifyRequest()를 호출하게 됩니다.

지금까지 SIP 메시지를 수신하는 데 필요한 전체 작업에 대해 알아보았습니다. 이제 메시지 송신에 필요한 코드를 살펴보겠습니다.



SipSender.java MIDlet

위 예제에서와 마찬가지로 복잡한 일은 모두 내부 클래스를 사용하여 처리하려고 합니다. 자, 그럼 어떤 일이 벌어지는지 이 내부 클래스 코드를 가지고 설명하겠습니다.

   class DataTransfer extends Thread implements SipClientConnectionListener{

      public void run() {
         SipClientConnection sc = null;
         try {
            sc = (SipClientConnection) Connector.open(addressTextField.getString());
            sc.setListener(this);
            String message = messageTextField.getString();
            sc.initRequest("MESSAGE", null);
            sc.setHeader("From", addressTextField.getString());
            sc.setHeader("Subject", subjectTextField.getString());
            sc.setHeader("Content-Type", "text/plain");
            sc.setHeader("Content-Length", "" + message.length());
            OutputStream os = sc.openContentOutputStream();
            os.write(message.getBytes());
            os.close(); 
       
         } catch(Exception e) {
            e.printStackTrace();
         }
      }

      public void notifyResponse(SipClientConnection scc) {
         try {
            scc.receive(1);
            form.append("notifyResponse: "+scc.getStatusCode()+" "+scc.getReasonPhrase());
            scc.close();
         } catch(Exception e) {
            form.append(e.getMessage());
         }
      }
   }

여기서도 몇 가지 강조하고 싶은 부분이 있습니다.

  • 이 내부 클래스는 성능상의 이유로 스레드를 확장합니다. 그리고 I/O 요청을 생성할 때 전체 애플리케이션의 속도가 저하되는 일은 없어야 합니다.
  • SipClientConnectionListener 인터페이스를 구현하기 위해 콜백 메소드인 notifyResponse()를 구현해야 합니다. 이것으로 메시지의 상태 코드를 확인할 수 있게 됩니다. SIP 상태 코드는 HTTP와 대단히 유사하며, 200 상태는 사용자의 요청이 성공적으로 수신되고 이해되었음을 의미합니다.

아래 그림은 Sun Wireless Toolkit을 사용하여 양쪽 애플리케이션을 실행하는 모습을 보여 줍니다.

사용자 삽입 이미지























결론

보시다시피 JSR 180 API에서 SIP 프로토콜을 사용하여 메시지를 보내고 받는 것은 그다지 어렵지 않습니다. SIP가 좋은 이유는 HTTP 프로토콜과 비슷하게 응답 코드를 만들었기 때문에 대부분의 사용자가 이 프로토콜을 편하게 사용할 수 있다는 점입니다. 2부에서는 SIP 프로토콜에 대해 더 자세히 설명하고 Registrar 또는 Proxy를 사용할 때의 장점을 알아보겠습니다.


소스 코드

다음은 SipReceiver.javaSipSender.java를 위한 전체 소스 코드입니다.


이 글의 영문 원본은
Asynchronous Communications with Java ME and SIP: Part I
에서 보실 수 있습니다.

"Java ME" 카테고리의 다른 글

2008/04/28 17:06 2008/04/28 17:06

TRACKBACK :: http://blog.sdnkorea.com/blog/trackback/554

댓글을 달아 주세요

[로그인][오픈아이디란?]

◀ Prev 1  ... 113 114 115 116 117 118 119 120 121  ... 641  Next ▶