http://blog.naver.com/girl5147/90090674812
[ONVIF] SOAP 라이브러리 + 네트워크
2010/07/05 16:43
http://blog.naver.com/girl5147/90090674812
SOAP 라이브러리 기술 조사
출처 : http://bebop.emstone.com/2009/07/28/soap-library-survey/
Posted on 2009-07-28 by keedi
주의: [SOAP 라이브러리 기술 조사]의 가장 최근 판은 이곳에서 확인할 수 있습니다.
시작하며
ONVIF 명세를 지원하는 IP 카메라의 경우 SOAP 방식의 웹서비스를
기반으로 카메라를 제어하는 기능을 제공합니다.
기존의 IP 카메라가 자체적인 프로토콜을 정의하고 제조사가 제공하는
라이브러리를 이용해야지만 카메라에 접근해서 조작할 수 있었습니다.
이 경우 카메라 서비스의 접근성은 무척 떨어지는데 대표적으로
제조사가 윈도우즈 라이브러리만 제공할 경우 정상적인 방법으로는
타 운영체제에서 카메라를 제어할 수 없는 상황이 발생하기도 합니다.
이와 비교해 표준적인 웹서비스인 SOAP 방식을 지원하는
ONVIF IP 카메라의 경우 프로그래밍 언어와 운영체제 심지어 하드웨어의 특성을
뛰어 넘어 제품을 제어할 수 있다는 측면에서 큰 장점을 가집니다.
단지 HTTP 프로토콜 통신을 지원하고 XML 로 구성된 SOAP 메시지를
파싱할 수 있고 ONVIF 카메라가 정의한 명세만 이해할 수 있으면
어떠한 환경에서도 카메라가 제공하는 서비스를 받을 수 있습니다.
이런 웹서비스는 컴퓨터간 통신을 가능하게 하고 서비스와 자료를
교환할 수 있게 해주는 것에 대한 모든 것을 의미합니다.
웹이 그러하듯이 웹서비스는 개방된 표준을 사용해서
응용을 더 편리하고 표준적인 방법으로 사용할 수 있게 도와줍니다.
SOAP 이란 Simple Object Access Protocol 의 약자로
W3C 가 설계한 표준으로 웹서비스의 가장 기본이 되는 부분입니다.
SOAP 명세는 데이터를 어떻게 표현할지와 통신에 무엇이 필요한지,
통신 계층에서 메시지를 어떻게 연결할 것인지를 포함합니다.
문서에서는 C 언어에서 사용 가능한 SOAP 라이브러리에 대해
알아보고 특징과 장단점을 정리합니다.
SOAP 의 장단점
장점
- SOAP 에서 표준 전송 프로토콜로 사용하는 HTTP 는 기존 원격 기술들에 비해서 프록시와 방화벽에 구애받지 않고 쉽게 통신 가능
- 다른 전송 프로토콜 사용을 허용하므로 표준인 HTTP 외에도 SMTP, FTP 등을 사용 가능
- 플랫폼 독립적
- 프로그래밍 언어에 독립적
- 간단(CORBA 에 비해 상대적으로)하고, 확장 가능
단점
- SOAP 전송을 위해 사용하는 인코딩이 XML 이기 때문에 바이너리 기반 인코딩 기법에 비해 상대적으로 느림
- XML 문서 처리 속도에 대한 비용
- REST (Representational State Transfer) 에 비해 상대적으로 복잡함
SOAP 라이브러리 종류
웹서비스는 개방된 기술을 사용하기 때문에 이기종 간의 통신이 자유로우므로
다양한 프로그래밍 언어를 사용해서 개발할 수 있다는 것 역시 장점입니다.
현존하는 대부분의 언어는 SOAP 을 지원하는 외부 라이브러리를 가지고 있습니다.
Perl 의 경우 SOAP::Lite 라는 라이브러리가 유명하며
Ruby 의 경우 soap4r, Python 의 경우 ZSI 등이 널리 쓰입니다.
C 언어 역시 널리 쓰이는 몇 가지 라이브러리가 있는데
문서에서는 다음 세 가지 C 언어 라이브러리에 대해서 살펴봅니다.
- cSOAP
- libsoup
- gSOAP
테스트용 SOAP 서버
각 라이브러리를 이용한 SOAP 클라이언트를 작성할 때
클라이언트가 제대로 동작하는지 확인을하려면 SOAP 서버가 필요합니다.
SOAP 은 프로그래밍 언어의 제약이 없는 것이 장점이므로
섭씨 및 화씨 온도를 변환하는 간단한 SOAP 서버를
Perl 의 SOAP::Lite 모듈을 이용해서 제작했습니다.
다음은 Perl 로 작성한 SOAP 서버의 예제입니다:
#!/usr/bin/perl
#
# REQUIREMENT:
#
# sudo apt-get install libsoap-lite-perl
#
use strict;
use warnings;
use SOAP::Transport::HTTP;
BEGIN {
package SOAP::Transport::HTTP::Daemon;
no warnings 'redefine';
sub handle {
my $self = shift->new;
while (my $c = $self->accept) {
while (my $r = $c->get_request) {
print STDERR $r->content, "\n";
$self->request($r);
$self->SUPER::handle;
$c->send_response($self->response)
}
# replaced ->close, thanks to Sean Meisner <Sean.Meisner@VerizonWireless.com>
# shutdown() doesn't work on AIX. close() is used in this case. Thanks to Jos Clijmans <jos.clijmans@recyfin.be>
$c->can('shutdown')
? $c->shutdown(2)
: $c->close();
$c->close;
}
}
1;
}
BEGIN {
package My::Handler;
use base qw/SOAP::Transport::HTTP::Daemon/;
1;
}
my $daemon = My::Handler->new(
LocalAddr => 'localhost',
LocalPort => 10000,
);
$daemon
->dispatch_to('Temperatures')
->handle;
BEGIN {
package Temperatures;
use base qw/ Exporter SOAP::Server::Parameters /;
use SOAP::Lite;
sub c2f {
my $self = shift;
my $temp = shift;
my $f = ((9/5) * $temp) + 32;
#sleep 3; # check sync or async
return SOAP::Data->type('string')->name(result => $f),
}
sub f2c {
my $self = shift;
my $temp = shift;
my $c = ($temp - 32) * (5/9);
#sleep 3; # check sync or async
return SOAP::Data->type('string')->name(result => $c),
}
1;
}
소스 코드 중 c2f() 함수와 f2c() 함수에 주석 처리한 sleep 구문은
클라이언트에서 사용한 라이브러리가 비동기 방식의 호출을 지원하는지
확인하기 위한 부분입니다.
비동기 방식의 호출을 지원하는 경우 서버에서 요청받은 원격 메소드
호출을 수행할 때 시간 지연이 있을지라도 클라이언트는 요청 즉시
프로그램의 제어권을 가질 수 있습니다.
이론적으로는 어떤 라이브러리를 사용하든 상기 서버와 통신할 수 있어야 합니다.
클라이언트는 http://localhost:10000/Temperatures 주소를 통해
화씨 및 섭씨 온도 변환 웹서비스를 제공받을 수 있습니다.
cSOAP
cSOAP 은 순수 C 언어로 구현한 클라이언트/서버 SOAP 라이브러리로
SOAP 1.1 명세를 지원하며 라이센스는 LGPL 입니다.
의존하는 라이브러리 목록은 다음과 같습니다:
- libxml2 (libdl, libz, libm 에 의존)
- libssl (libdl, libcrypto, libz 에 의존): 사용여부 선택 가능
- libpthread: nanohttp 서버에서 사용: 윈도우즈 외의 경우 서버에서만 사용
UDP 와 HTTP 프로토콜을 이용한 전송을 지원하며 SSL 도 지원합니다.
HTTP 서버 기능 역시 포함하는데 이 부분은 nanoHTTP 모듈에서 구현합니다.
cSOAP 으로 전송되는 XML 구조는 libxml2 라이브러리를 이용해서 처리합니다.
클라이언트 서버간의 UDP 전송시 멀티캐스트 기능과 SOAP-over-UDP 를 지원하며
MIME 첨부 기능, XML 암호화 및 서명을 통한 보안 메시지를 지원합니다.
WS-Inspection 문서 자동 생성 기능도 지원합니다.
그리고 순수 ANSI C 로 구현했기 때문에 윈도우즈, 유닉스, 리눅스 뿐만 아니라
MacOS, OpenVMS, AIX 등의 아키텍처도 지원합니다.
하지만 cSOAP 이 제공하는 문서 자동 생성 기능은 완벽하게 지원하지 않는
것 같으며 특히 WSDL 문서를 이용해서 코드를 자동 생성하는 기능의 경우
비교적 간단한 WSDL 의 경우 문제가 없었으나 ONVIF 공식 홈페이지에서 제공하는
WSDL 문서의 경우 제대로 코드를 생성하지 못하기도 했습니다.
또한 문서의 내용이 릴리스 버전에 비해 갱신이 되지 않아서 간간히 맞지 않는
부분이 있으므로 문서를 참조할 때 유의해야 합니다.
동기 방식의 호출만 지원하며 문서 상에는 비동기 방식의 호출 API 도 나와있으나
실제로 코드를 살펴보면 헤더에만 정의되어 있고 구현이 되어 있지 않아
결과적으로 비동기 방식의 호출은 지원하지 않습니다.
현재 안정 버전은 1.1.0 이며 마지막 릴리스 날짜는 2006년 3월 6일 입니다.
SOAP 명세 자체가 안정화가 되었고 나온지 비교적 시간이 지난 기술이기 때문에
라이브러리 역시 안정화가 되어서 그런지 릴리스 이후 특별한 변화는 없어
메일링 리스트 등의 활동이 저조하므로 사용시 이런 부분을 감안해야 할 것으로 보입니다.
cSOAP 을 사용한 동기 방식의 클라이언트 예제는 다음과 같습니다:
#include <libcsoap/soap-client.h>
#include <stdio.h>
static char *url = "http://localhost:10000";
static char *urn = "http://localhost:10000/Temperatures";
static char *method = "c2f";
int
main (int argc, char **argv)
{
SoapCtx *request;
SoapCtx *response;
herror_t err;
/**
* Client initialization
*/
if ((err = soap_client_init_args(argc, argv)) != H_OK)
{
printf ("%s():%s [%d]\n", herror_func(err), herror_message(err), herror_code(err));
herror_release (err);
exit (1);
}
/**
* Envelope creation
*/
if ((err = soap_ctx_new_with_method (urn, method, &request)) != H_OK)
{
printf ("%s():%s [%d]\n", herror_func(err), herror_message(err), herror_code(err));
herror_release (err);
soap_client_destroy ();
exit (1);
}
soap_env_add_item (request->env, "xsd:int", "temp", "10");
/**
* Invocation
*/
if ((err = soap_client_invoke (request, &response, url, "")) != H_OK)
{
printf ("%s():%s [%d]\n", herror_func(err), herror_message(err), herror_code(err));
herror_release (err);
soap_ctx_free (request);
soap_client_destroy ();
exit (1);
}
/**
* Printout result
*/
soap_xml_doc_print(response->env->root->doc);
//xmlDocFormatDump (stdout, response->env->root->doc, 1);
/**
* Cleanup
*/
soap_ctx_free (response);
soap_ctx_free (request);
soap_client_destroy ();
exit (0);
}
상기 클라이언트를 빌드하는 방법은 다음과 같습니다:
gcc csoap-client.c -o csoap-client \
`pkg-config --cflags libcsoap` \
`pkg-config --libs libcsoap`
libsoup
libsoup 은 GNOME 환경을 위한 HTTP 클라이언트/서버 라이브러리로 라이센스는 LGPL 입니다.
의존하는 라이브러리 목록은 다음과 같습니다:
- libglib (libgio, libgobject, libgmodule, libgthread)
- libgnutls (libgpg-error, libgcrypt, libz)
- libpthread: gnutls 기능에서 사용, 윈도우즈 외의 경우 사용
libsoup 의 특징은 다음과 같습니다:
- GNOME 환경을 위한 HTTP 클라이언트/서버 라이브러리
- GObject 와 glib 메인 루프를 사용하고, 콜백을 지원하므로 GNOME 응용과 통합하기 쉬움
- 비동기 뿐만 아니라 동기 API 도 가지므로 스레드 프로그램에서도 사용 가능
- 자동 캐시 연결 기능
- GnuTLS 를 이용한 SSL 지원
- 프록시 지원, 인증, SSL 터널링 지원
- 클라이언트의 Digest, NTLM, 기본 인증 지원
- 서버의 Digest, 기본 인증 지원
- 서버 및 클라이언트의 XML-RPC 지원
리눅스 및 윈도우와 MacOS 도 지원을하며
glib 기반으로 메인루프와 콜백 API 를 지원합니다.
gnome 프로젝트의 일부로써 gnome 버전과 함께 업데이트 되며
현재 안정 버전은 그놈 버전과 같은 2.27.4 로 2009 년 6월 13일 마지막
git 커밋 로그가 있는 것을 확인했습니다.
최근까지 활발한 활동이 있는 라이브러리인 만큼 메일링 리스트를 통한
지원을 받기 좋을 것으로 보입니다.
특징에서 볼 수 있듯이 사실 libsoup 은 SOAP 과 직접적으로 상관은 없습니다.
여타 SOAP 관련 라이브러리와 비교해 비동기 통신을 지원한다는 점이 특징적인 장점입니다.
하지만 cSOAP 과 마찬가지로 WSDL 관련 코드 생성 등은 지원하지 않으며
SOAP 메시지 전송을 위해 직접 SOAP 헤더 및 본문을 생성해야 하는 것이 단점입니다.
예전 릴리스 버전은 SOAP 전송을 지원했으나 개발팀이 SOAP 관련 기능을
사용하는 개발자가 많지 않다는 판단을 한 후 SOAP 관련 API 및 기능이
라이브러리에서 모두 제거되었습니다.
즉, 현재는 SOAP 에 특화된 어떤 기능도 제공하지 않으며
간단한 XML-RPC 클라이언트/서버 통신만 지원합니다.
다만 SOAP 의 근간이 되는 HTTP 통신을 지원하므로 SOAP 메시지 생성 및 분해 작업을
제외한 SOAP 메시지 통신에만 사용할 수 있습니다.
libsoup 을 사용한 동기 방식의 클라이언트 예제는 다음과 같습니다:
#include <libsoup/soup.h>
#include <stdio.h>
#include <string.h>
static gchar *urn = "http://localhost:10000/Temperatures";
#define SOAP_BODY "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><c2f xmlns=\"http://localhost:10000/Temperatures\"><c-gensym3 xsi:type=\"xsd:int\">10</c-gensym3></c2f></soap:Body></soap:Envelope>"
/**
* Debug level
* - SOUP_LOGGER_LOG_NONE
* - SOUP_LOGGER_LOG_MINIMAL
* - SOUP_LOGGER_LOG_HEADERS
* - SOUP_LOGGER_LOG_BODY
*/
static SoupLoggerLogLevel debug_level = SOUP_LOGGER_LOG_BODY;
int
main (int argc, char **argv)
{
SoupSession *session;
SoupMessage *msg;
guint status;
g_type_init ();
g_thread_init (NULL);
/**
* Create a SoupSession
* - soup_session_sync_new
* - soup_session_sync_new_with_options
* - soup_session_async_new
* - soup_session_async_new_with_options
*/
session = soup_session_sync_new ();
/**
* Session features
*
* how to add:
* - session-construction time
* - SOUP_SESSION_ADD_FEATURE
* - SOUP_SESSION_ADD_FEATURE_BY_TYPE
* - after session-construction
* - soup_session_add_feature()
*
* available features:
* - SoupLogger
* - SoupCookieJar
* - SoupCookieJarText
*/
if (debug_level)
{
SoupLogger *logger;
logger = soup_logger_new (debug_level, -1);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}
/**
* Creating SoupMessages
*
* SoupMessage: An HTTP request and response
*
* typedef struct {
* const char *method;
*
* guint status_code;
* char *reason_phrase;
*
* SoupMessageBody *request_body;
* SoupMessageHeaders *request_headers;
*
* SoupMessageBody *response_body;
* SoupMessageHeaders *response_headers;
* } SoupMessage;
*
* use soup_message_new()
*
* use SOUP_METHOD_POST for SOAP web-service method request
*
* to modify request or response, use:
* - soup_message_set_request()
* - soup_message_set_response()
*
* to use more complicated headers and body, use:
* - SoupMessage.request_headers with soup_message_headers_append()
* - SoupMessage.request_body with soup_message_body_append()
*
*/
msg = soup_message_new (SOUP_METHOD_POST, urn);
soup_message_headers_append(msg->request_headers,
"Content-Type",
"text/xml;charset=UTF-8");
soup_message_body_append(msg->request_body, SOUP_MEMORY_COPY, SOAP_BODY, strlen (SOAP_BODY));
/**
* Sending a SoupMessage
*
* Synchronously:
* - soup_session_send_message()
* - unref SoupMessage by hand
*
* Asynchronously:
* - soup_session_queue_message() with callback function
* - SoupMessage will be unref-ed by automatically
*
*/
status = soup_session_send_message (session, msg);
/**
* Processing the response
*/
printf ("response(%d): %s\n", status, msg->response_body->data);
/**
* Cleanup code
*/
g_object_unref (G_OBJECT (session));
return 0;
}
상기 클라이언트를 빌드하는 방법은 다음과 같습니다:
gcc libsoup-client-sync.c -o libsoup-client-sync \
`pkg-config --cflags libsoup-2.4` \
`pkg-config --libs libsoup-2.4`
libsoup 을 사용한 비동기 방식의 클라이언트 예제는 다음과 같습니다:
/**
* libsoup-client-async.c:
*
* gcc libsoup-client-async.c -o libsoup-client-async \
* `pkg-config --cflags libsoup-2.4` \
* `pkg-config --libs libsoup-2.4`
*/
#include <libsoup/soup.h>
#include <stdio.h>
#include <string.h>
static GMainLoop *loop;
static gchar *urn = "http://localhost:10000/Temperatures";
#define SOAP_BODY "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" soap:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><c2f xmlns=\"http://localhost:10000/Temperatures\"><c-gensym3 xsi:type=\"xsd:int\">10</c-gensym3></c2f></soap:Body></soap:Envelope>"
/**
* Debug level
* - SOUP_LOGGER_LOG_NONE
* - SOUP_LOGGER_LOG_MINIMAL
* - SOUP_LOGGER_LOG_HEADERS
* - SOUP_LOGGER_LOG_BODY
*/
static SoupLoggerLogLevel debug_level = SOUP_LOGGER_LOG_BODY;
static void
got_response (SoupSession *session, SoupMessage *msg, gpointer user_data)
{
/**
* Processing the response
*/
printf ("response(%u): %s\n", msg->status_code, msg->response_body->data);
g_main_loop_quit (loop);
}
int
main (int argc, char **argv)
{
SoupSession *session;
SoupMessage *msg;
g_thread_init (NULL);
g_type_init ();
/**
* Create a SoupSession
* - soup_session_sync_new
* - soup_session_sync_new_with_options
* - soup_session_async_new
* - soup_session_async_new_with_options
*/
session = soup_session_sync_new ();
/**
* Session features
*
* how to add:
* - session-construction time
* - SOUP_SESSION_ADD_FEATURE
* - SOUP_SESSION_ADD_FEATURE_BY_TYPE
* - after session-construction
* - soup_session_add_feature()
*
* available features:
* - SoupLogger
* - SoupCookieJar
* - SoupCookieJarText
*/
if (debug_level)
{
SoupLogger *logger;
logger = soup_logger_new (debug_level, -1);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}
/**
* Creating SoupMessages
*
* SoupMessage: An HTTP request and response
*
* typedef struct {
* const char *method;
*
* guint status_code;
* char *reason_phrase;
*
* SoupMessageBody *request_body;
* SoupMessageHeaders *request_headers;
*
* SoupMessageBody *response_body;
* SoupMessageHeaders *response_headers;
* } SoupMessage;
*
* use soup_message_new()
*
* use SOUP_METHOD_POST for SOAP web-service method request
*
* to modify request or response, use:
* - soup_message_set_request()
* - soup_message_set_response()
*
* to use more complicated headers and body, use:
* - SoupMessage.request_headers with soup_message_headers_append()
* - SoupMessage.request_body with soup_message_body_append()
*
*/
msg = soup_message_new (SOUP_METHOD_POST, urn);
soup_message_headers_append(msg->request_headers,
"Content-Type",
"text/xml;charset=UTF-8");
soup_message_body_append(msg->request_body, SOUP_MEMORY_COPY, SOAP_BODY, strlen (SOAP_BODY));
/**
* Sending a SoupMessage
*
* Synchronously:
* - soup_session_send_message()
* - unref SoupMessage by hand
*
* Asynchronously:
* - soup_session_queue_message() with callback function
* - SoupMessage will be unref-ed by automatically
*
*/
soup_session_queue_message (session, SOUP_MESSAGE (msg), got_response, NULL);
loop = g_main_loop_new (NULL, TRUE);
g_main_loop_run (loop);
g_main_loop_unref (loop);
return 0;
}
상기 클라이언트를 빌드하는 방법은 다음과 같습니다:
gcc libsoup-client-async.c -o libsoup-client-async \
`pkg-config --cflags libsoup-2.4` \
`pkg-config --libs libsoup-2.4`
gSOAP
gSOAP 은 Robert A. van Engelen 교수가 C++ 로 제작한 SOAP 라이브러리로
SOAP 1.1 및 SOAP 1.2 명세를 충실히 따르는 안정적인 라이브러리입니다.
gSOAP public 라이센스와 GPL 또는 사이트 라이센스 중 하나를 따를 수 있으며
클라이언트 서버를 모두 지원합니다.
리눅스 및 윈도우, MacOS 등의 플랫폼을 지원하며
의존 라이브러리 없이 자체적으로 필요한 기능을 모두 구현했으며
SOAP 메시지 생성 및 분해도 내부적으로 다 처리해줍니다.
또한 다른 라이브러리와는 달리 wsdl 관련 자동 코드 생성 및
클라이언트 및 서버의 스텁 코드를 자동으로 생성해주는 것은 큰 장점입니다.
현재 안정버전은 2.7.13 으로 2009년 03월 21일 릴리스가 가장 최근 버전입니다.
gSOAP 은 SOAP 관련 클라이언트 및 서버 코드를 자동으로 생성해주므로
프로그래머는 웹서비스에 필요한 메소드를 헤더 파일에 정의만 하면 됩니다.
다음과 같이 섭씨 및 화씨 온도를 변환하는 메소드를 정의한
temperatures.h 파일을 생성합니다:
int ns__c2f (int temperature, char **result);
int ns__f2c (int temperature, char **result);
gSOAP 으로 하여금 자동으로 코드를 생성토록 합니다:
soapcpp2 -C -c temperatures.h
-C 옵션은 클라이언트 코드만 생성하도록 하며 -c 옵션은 C++ 가 아니라
C 언어 코드를 생성토록 합니다.
soapcpp2 유틸리티가 자동 생성한 파일 중 ns.nsmap 파일의 내용 중
ns 항목을 접속할 웹서비스에 맞게 수정해합니다:
#include "soapH.h"
SOAP_NMAC struct Namespace namespaces[] =
{
{"SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/", "http://www.w3.org/*/soap-envelope", NULL},
{"SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/", "http://www.w3.org/*/soap-encoding", NULL},
{"xsi", "http://www.w3.org/2001/XMLSchema-instance", "http://www.w3.org/*/XMLSchema-instance", NULL},
{"xsd", "http://www.w3.org/2001/XMLSchema", "http://www.w3.org/*/XMLSchema", NULL},
{"ns", "http://localhost:10000/Temperatures", NULL, NULL},
{NULL, NULL, NULL, NULL}
};
ns 항목을 http://localhost:10000/Temperatures 로 변경하도록 합니다:
{"ns", "http://localhost:10000/Temperatures", NULL, NULL},
gSOAP 을 사용한 비동기 방식의 클라이언트 예제는 다음과 같습니다:
#include "soapH.h"
#include "ns.nsmap"
static char *urn = "http://localhost:10000/Temperatures";
int
main (int argc, char **argv)
{
struct soap soap;
int input;
char *c, *f;
soap_init (&soap);
input = 10;
soap_call_ns__c2f (&soap, urn, "", input, &f);
if (soap.error)
{
soap_print_fault (&soap, stderr);
exit (1);
}
else
printf ("c2f(%d) = %s\n", input, f);
soap_destroy (&soap);
soap_end (&soap);
input = 50;
soap_call_ns__f2c (&soap, urn, "", input, &c);
if (soap.error)
{
soap_print_fault (&soap, stderr);
exit (1);
}
else
printf ("f2c(%d) = %s\n", input, c);
/**
* Cleanup
* - deserialized class instances
*/
soap_destroy (&soap);
/**
* Cleanup
* - deserialized data & temporary data
*/
soap_end (&soap);
/**
* Cleanup
* - Reset and detach context
* - close master/slave sockets and remove callbacks
*/
soap_done (&soap);
return 0;
}
상기 클라이언트를 빌드하는 방법은 다음과 같습니다:
gcc -o gsoap-client-async \
gsoap-client-async.c \
soapC.c \
soapClient.c \
`pkg-config --cflags gsoap` \
`pkg-config --libs gsoap`
gSOAP 은 비동기 방식의 원격 메소드 호출을 지원하지 않습니다.
대신 one-way message passing 이라는 방식의 비동기 이벤트 호출
및 응답 처리와 관련한 기능을 클라이언트와 서버에서 지원합니다.
이 기능을 사용하려면 공식 문서 중 다음 항목을 참조합니다:
- 7.3 How to Use gSOAP for Asynchronous One-Way Message Passing
- 7.4 One-Way Message Passing over HTTP
하지만 이 방식을 사용하려면 클라이언트 및 서버 양쪽이 모두 지원할
수 있도록 양쪽의 코드를 모두 수정해야 하며 libsoup 에서 볼 수 있는
비동기 방식의 원격 메소드 호출과는 거리가 있습니다.
메일링 리스트에서도 비동기 방식으로 호출하는 방법과 관련한
문의가 많았지만 딱 부러지는 간단한 해결책은 없습니다.
이와 관련해 메일링 리스트에서 Engelen 교수는
SOAP 이 원래 비동기 방식을 지원하기 설계된 프로토콜이 아니었다는
언급을 하며 비동기 방식을 지원하려면 SOAP 위에 모델을 만들어서
직접 처리해야 한다고 합니다.
gSOAP 의 서버측은 어느정도 비동기 방식의 구조로 작성할 수 있으나
클라이언트 측에서 이를 구현하려면 고민이 필요할 것으로 보입니다.
실제로 클라이언트에서 원격 메소드를 호출하는 부분에 대한
소켓 통신이 이루어지는 부분은 soap.fsend, soap.frecv 등의 부분입니다.
gSOAP 은 내부 메소드를 오버라이딩하는 기능을 제공하는데
이 기능을 이용해서 상기 메소드를 async 하게 처리하는 등의
작업이 필요할 것으로 보입니다.
gSOAP 의 내부 메소드 오버라이딩 기능은 공식 문서 중 다음 항목을 참조합니다:
- 18.7 Function Callbacks for Customized I/O and HTTP Handling
마지막으로 gSOAP 은 스텁 코드를 자동으로 생성하며 바이너리 빌드시
모두 정적 링크를 하기 때문에 상대적으로 바이너리 용량이 커지는 점이 특징입니다.
정리하며
SOAP 은 비교적 시간이 지난 기술이며 내부 데이터를 XML 로 처리하기 때문에
속도가 느리다는 단점이 있지만 다양한 이기종 환경을 극복하고 동일한 서비스를
요청 또는 제공할 수 있다는 장점 때문에 호환성이 중요한 비지니스 업무에 적당합니다.
IP 카메라의 ONVIF 명세 채택은 이러한 SOAP 의 장점을 적극 활용한 것이라고
볼 수 있는데 IP 카메라가 ONVIF 를 지원함으로써 HTTP 통신이 가능하고
XML 파싱을 할 수 있는 시스템이라면 어떤 환경에서라도 IP 카메라를 제어할
수 있는 접근성에 대한 우위를 가지게 됩니다.
다양한 SOAP 라이브러리 중 C 언어에서 사용할 수 있는
cSOAP, libsoup, gSOAP 을 살펴보았는데 각 라이브러리의 특징은 다음과 같습니다.
cSOAP 은 용량이 작고 사용하기 간편하지만 릴리스 날짜가 오래된 점이 특징입니다.
libsoup 은 직접적으로 SOAP 을 지원하는 것이 아니라 HTTP 통신을 지원하며
유일하게 동기 및 비동기 통신을 지원합니다.
gnome 프로젝트의 일부로 최근까지 릴리스가 되고 있는 점이 특징입니다.
gSOAP 은 밑바닥부터 거의 직접 구현했기 때문에 의존성이 거의 없으며
SOAP 명세를 제대로 지원하며 최근까지도 릴리스가 되고 있습니다.
또한 SOAP 통신을 위한 스텁 코드 및 WSDL 코드를 자동 생성하는 기능을 제공합니다.
다만 비동기 통신을 지원하지 않으며 의존성이 없는 만큼 라이브러리 용량이 크고
정적으로 바이너리에 링크하는 것이 특징입니다.
라이브러리 마다 고유한 특징을 가지고 있기 때문에 업무에 적용하려면
라이브러리의 기능 및 한계, 그리고 극복 방법에 대한 충분한 숙지가 필요할 것으로 보입니다.