gSOAP MTOM


前言

需要准備的知識:wsdl,soap,gSOAP,C++,fidder。

 

首先介紹幾個相關的概念

  1、MTOM基礎概念     

      MTOM(Message Transmission Optimization Mechanism)消息優化傳輸機制。

  它提出的模型適用於大量數據的交互情況。針對Base64編碼情況帶來的開銷提出的解決方案。當數據量小的時候,SOAP依然使用XML進行消息的傳遞。

    消息傳輸優化機制 (MTOM) 標准允許將消息中包含的大型數據元素外部化,並將其作為無任何特殊編碼的二進制數據隨消息一起傳送。MTOM 消息會打包為多部分/相關 MIME 序列,放在SOAP 消息中一起傳送。

    但是在大量數據情況下,如果數據依然進行Base64編碼,會帶來33%的額外開銷,這樣的情況對於大量數據交換的情況是無法容忍的。MTOM 就是針對SOAP 消息傳輸的基礎上提出的改進辦法。對於大量數據的傳遞,不會進行進行Base64編碼,而是直接以附件的二進制原始數據的形式封裝在SOAP消息的 MIME 部分,進行傳輸。SOAP 消息通過指向隨其發送的 MIME 部分來引用二進制內容,另外包括SOAP基本的XML 數據,這些還是Base64編碼。因為此模型與簡單郵件協議SMTP 模型基本一致。

     MTOM通過簡化大量數據的編碼過程,從而提高數據的處理效率。因為SOAP消息等必要的信息,MTOM 也有一些必要的開銷。MTOM僅在二進制數據元素的大小超過大約 1 KB 時,才能體現出其優勢。

     什么是BASE64編碼、MTOM消息優化傳輸機制、MIME。這些對於我們理解MTOM消息優化傳輸機制問題非常的必要。

  2、BASE64編碼 

    BASE64編碼 的原理很簡單,其方法是,將輸入數據流每次取6 bit(每bit代表1位二進制),不足6bit的補0,這樣,每3個8位字節將編碼為4個6位字節(3×8 → 4×6);不滿4個字節的以“=”填充。其實這4個六位字節 仍然是8位,只不過高兩位被設置為0。當一個字節只有6位有效時,它的取值空間為0 到 2的6次方減1 即63,也就是說被轉換的Base64編碼的每一個編碼的取值空間為(0~63)。

  這樣就可以將3個8位字節,轉換為4個字節,這4個轉換的字節都可以映射到字符中。也即數據都可以使用字符編碼代替。 因為轉換后的字符串要比原來的多一個字節,長1/3。因此編碼后的數據長度增加到4/3倍。這里也是為什么使用SOAP消息效率比MTOM低的原因。因為 SOAP使用XML語言進行消息傳遞,XML是基於BASE64編碼的語言。

3、MIME

   MIME表示多用途Internet郵件擴允協議。MIME擴允了基本的面向文本的Internet郵件系統,以便可以在消息中包含二進制附件。MIME(Multipurpose Internet Mail Extentions),一般譯作"多用途的網絡郵件擴充協議"。顧名思義,它可以傳送多媒體文件。 MIME (Multipurpose Internet Mail Extensions,多目的Internet郵件擴展)是創建用於電子郵件交換,網絡文檔,及企業網和Internet上的其他應用程序中的文件格式的規范。

第一節 使用說明

MTOM(Message Transmission Optimization Mechanism)是一種新的(相對MIME、DIME)的SOAP消息傳輸附件的格式。MTOM附件本質上是在SOAP body標簽中引用的標准MIME附件,可以不用MIME附件而是用DIME附件。

MTOM在SOAP 1.2中實現,同時是用XOP命名空間。XOP Include元素xop:include(在SOAP body標簽中)用來引用附件(可以又多個附件)。

由於用MTOM方式強制規定SOAP 消息的body需要引用附件,GSoap是用類似DIME的實現方式實現MTOM和MIME的二進制附件的序列化和反序列化。這個二進制結構事前在 import/xop.h 文件中定義:

//gsoap xop schema import: http://www.w3.org/2004/08/xop/include
struct _xop__Include
{
unsigned char *__ptr;
int __size;
char *id;
char *type;
char *options;
};
typedef struct _xop__Include _xop__Include;

除了 id,type 還有兩個選項__ptr、__size可用。發送和接受MTOPM XOP附件的過程是完全自動的。id 關聯附件(典型的內容標識為CID或UUID)。當 id 為空=NULL時Gsoap會分配一個唯一的CID。type 字段指明二進制數據的MIME類型,同時可選選項可以用來傳輸附件的附加說明。結構體的字段聲明順序時敏感的(也就是結構的變量聲明順序不能變化).

 

可以聲明自己的數據結構體包含 xop.h MTOM的附件定義,例如:

#import ïmport/soap12.h"
/* alternatively, without the import above, use:
//gsoap SOAP-ENV schema namespace: http://www.w3.org/2003/05/soap-envelope
//gsoap SOAP-ENC schema namespace: http://www.w3.org/2003/05/soap-encoding
*/
#import ïmport/xop.h"
#import ïmport/xmime5.h"
//gsoap x schema namespace: http://my.first.mtom.net
struct x__myData
{
   _xop__Include xop__Include; // attachment
   @char *xmime5__contentType; // and its contentType
};
int x__myMTOMtest(struct x__myData *in, struct x__myData *out);

如上所示,在MTOM和DIME的附件在gSOAP的頭文件定義中除了MTOM附件必須時SOAP 1.2 和是用 xop__Include 元素外,沒有任何區別。

當 x_myData 實例序列化時,idtype 字段都不能為NULL,gSOAP的soap結構內容的標識為 SOAP_ENC_MTOM 時附件就會以 MTOM MIME附件方式傳輸。

struct soap *soap = soap_new1(SOAP_ENC_MTOM);

不設置這個標識附加將以 DIME 的方式傳輸。

如果你目前的客戶端和服務都是基於非流 DIME 附件使用SOAP正文引用機制(因此,沒有使用soap_set_dime_attachment函數)或純base64二進制XML數據元素,很容易采用MTOM通過重命名xop__Include和使用的二進制類型 SOAP_ENC_MTOM 標識與SOAP 1.2名稱空間。

 

第二節 流動式接收MTOM/MIME

流動式接收MTOM/MIME是用回調函數的方式實現附件傳輸期間的數據抓取和存儲。三個回調函數實現流動式接收MTOM/MIME的輸出(寫),三個回調函數實現流動式接收MTOM/MIME的輸入(讀)。

如下是輸入(讀取)附件的三個回調函數:

void *(*soap.fmimereadopen)(struct soap *soap, void *handle,const char *id, const char *type, const char *description)

 

size_t (*soap.fmimeread)(struct soap *soap, void *handle, char *buf, size_t len)

 

void(*soap.fmimereadclose)(struct soap *soap, void *handle)

 

如下是輸出(寫入)附加的三個回調函數:

void *(*soap.fmimewriteopen)(struct soap *soap, void *handle,const char *id, const char *type, const char *description,enum soap_mime_encoding encoding)

 

int (*soap.fmimewrite)(struct soap *soap, void *handle,const char *buf, size_t len)

 

void(*soap.fmimewriteclose)(struct soap *soap, void *handle)

此外,一個void *user字段結構soap數據結構可以將用戶定義的數據傳遞給回調函數。通過這種方式,您可以設置soap。用戶指向應用程序數據的回調需要,例如一個文件名。

 

下面的例子說明了客戶端初始化一個圖像附件結構流文件為MTOM附件沒有HTTP分塊:

int main()
{
struct soap soap;
struct xsd__base64Binary image;
   FILE *fd;
struct stat sb;
   soap_init1(&soap, SOAP_ENC_MTOM); // mandatory to enable MTOM
if (!fstat(fileno(fd), &sb) && sb.st_size > 0)
   { // because we can get the length of the file, we can stream it without chunking
      soap.fmimereadopen = mime_read_open;
      soap.fmimereadclose = mime_read_close;
      soap.fmimeread = mime_read;
      image.__ptr = (unsigned char*)fd; // must set to non-NULL (this is our fd handle which we need in the callbacks)
      image.__size = sb.st_size; // must set size
   }
else
   { // don't know the size, so buffer it
      size_t i;
int c;
      image.__ptr = (unsigned char*)soap_malloc(&soap, MAX_FILE_SIZE);
for (i = 0; i < MAX_FILE_SIZE; i++)
      {
if ((c = fgetc(fd)) == EOF)
break;
         image.__ptr[i] = c;
      }
      fclose(fd);
      image.__size = i;
   }
   image.type = "image/jpeg"; // MIME type
   image.options = "This is my picture"; // description of object
   soap_call_ns__method(&soap, ...);
   ...
}
void *mime_read_open(struct soap *soap, void *handle, const char *id, const char *type, const char *description)
{ return handle;
}
void mime_read_close(struct soap *soap, void *handle)
{ fclose((FILE*)handle);
}
size_t mime_read(struct soap *soap, void *handle, char *buf, size_t len)
{ return fread(buf, 1, len, (FILE*)handle);
}

 

面的例子說明了MTOM / MIME的流由一個客戶端存儲在一個文件中:

int main()
{ struct soap soap;
   soap_init(&soap);
   soap.fmimewriteopen = mime_write_open;
   soap.fmimewriteclose = mime_write_close;
   soap.fmimewrite = mime_write;
   soap_call_ns__method(&soap, ...);
   ...
}
void *mime_write_open(struct soap *soap, const char *id, const char *type, const char *description, enum soap_mime_encoding encoding)
{
   FILE *handle = fopen("somefile", "wb");
   // We ignore the MIME content transfer encoding here, but should check
if (!handle)
   {
      soap->error = SOAP_EOF;
      soap->errnum = errno; // get reason
   }
return (void*)handle;
}
void mime_write_close(struct soap *soap, void *handle)
{ fclose((FILE*)handle);
}
int mime_write(struct soap *soap, void *handle, const char *buf, size_t len)
{
   size_t nwritten;
while (len)
   {
      nwritten = fwrite(buf, 1, len, (FILE*)handle);
if (!nwritten)
      {
         soap->errnum = errno; // get reason
return SOAP_EOF;
      }
      len -= nwritten;
      buf += nwritten;
   }
return SOAP_OK;
}

 

服務器端的文件管理同樣取決與回調函數的實現,如下是gSOAP提供的例子程序 mtom-stream 的服務端寫入的代碼

void *mime_server_write_open(struct soap *soap, void *unused_handle, const char *id, const char *type, const char *description, enum soap_mime_encoding encoding)
{
    /* Note: the 'unused_handle' is always NULL */
    /* Return NULL without setting soap->error if we don't want to use the streaming callback for this DIME attachment */
    const char *file;
    struct mime_server_handle *handle = (struct mime_server_handle *)soap_malloc(soap, sizeof(struct mime_server_handle));
    if (!handle)
    { soap->error = SOAP_EOM;
        return NULL;
    }
    /* Create a new file */
   file = tempnam(TMPDIR, "data");

    /* The file name is also the key */
    handle->key = soap_strdup(soap, file);
    handle->fd = fopen(file, "wb");
    free((void*)file);
    if (!handle->fd)
    { soap->error = soap_sender_fault(soap, "Cannot save data to file", handle->key);
        soap->errnum = errno; /* get reason */
        return NULL;
    }
    fprintf(stderr, "Saving file %s type %s\n", handle->key, type?type:"");
    return (void*)handle;
}

   如上紅色的代碼使用 C 函數tempnam產生了一個臨時文件名用於保存附件,在真實的實現中可以根據MIME類型對附件進行分類管理。

第三節 使用SoapUI測試MTOM

如下圖在Attachments中添加若干附件,在soap請求的中的 xop:include 的屬性href中通過 cid 引用附件,

    111

請求的結果為:

222

如上對的例子是以MTOM方式請求,返回的結果是base64編碼的二進制格式。同樣可以使用base64編碼的消息獲取MTOM方式的附件消息格式,如下:

 

3

第四節 用fidder獲得SoapUI報文

用Fidder獲取SoapUI的報文需要用的兩個軟甲的代理功能。

4.1 SoapUI設置

   step 1: 打開菜單 File--->Preferences

4

    step 2: 選擇Proxy Setting選項卡進行如下設置:

5

4.2 Fidder設置

    step 1: Tools-》 Options

6

    step 2:打開選項卡 Connections

7

 

原理上就是SoapUI通過8888端口為代理對外發送請求,而Fidder監聽8888端口的代理事件。如下為用Fidder截取的SoapUI發送的報文:

9


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM