チャネル レイヤーの概要

チャネル層は、トランスポート チャネルの抽象化と、チャネルで送信されるメッセージを提供します。 また、SOAP 構造体との間で C データ型をシリアル化するための関数も含まれています。 チャネル層を使用すると、送受信されたデータと本文とヘッダーを含むメッセージ、および メッセージ 交換プロトコルを抽象化し、設定をカスタマイズするためのプロパティを提供する チャネル で構成されるメッセージによる通信を完全に制御できます。

Message

メッセージは、ネットワーク データ (具体的には、ネットワーク経由で送受信されるデータ) をカプセル化するオブジェクトです。 メッセージ構造は SOAP によって定義され、ヘッダーとメッセージ本文の個別のセットが含まれます。 ヘッダーはメモリ バッファーに配置され、メッセージ本文はストリーム API を使用して読み取りまたは書き込まれます。

メッセージのヘッダーと本文を示す図。

メッセージのデータ モデルは常に XML データ モデルですが、実際のワイヤ形式は柔軟です。 メッセージを送信する前に、特定のエンコード (Text、Binary、MTOM など) を使用してエンコードされます。 エンコードの詳細については、「 WS_ENCODING 」を参照してください。

いくつかのメッセージ エンコード形式を示す図。

チャネル

チャネルは、2 つ以上のエンドポイント間でネットワーク上のメッセージを送受信するために使用されるオブジェクトです。

チャネルには、送信時にメッセージに 対処 する方法を説明するデータが関連付けられています。 チャネルにメッセージを送信することは、チャテにメッセージを配置するようなものです。チャネルには、メッセージを送信する必要がある情報と、そこにメッセージを取得する方法が含まれます。

メッセージのチャネルを示す図。

チャネルはチャネルの 種類に分類されます。 チャネルの種類は、メッセージが流れる方向を指定します。 チャネルの種類は、チャネルがセッションフルかセッションレスかを識別します。 セッションは、2 つ以上のパーティ間でメッセージを関連付ける抽象的な方法として定義されます。 セッションフル チャネルの例として、TCP 接続を具体的なセッション実装として使用する TCP チャネルがあります。 セッションレス チャネルの例としては、基になるセッション メカニズムがない UDP があります。 HTTP には基になる TCP 接続がありますが、この事実はこの API を介して直接公開されないため、HTTP もセッションレス チャネルと見なされます。

セッションフル チャネルとセッションレス チャネルの種類を示す図。

チャネルの種類はチャネルの方向とセッションの情報を記述しますが、チャネルの実装方法は指定しません。 チャネルはどのプロトコルを使用する必要がありますか? チャネルでメッセージの配信を試みるには、どの程度のハードが必要ですか? どのような種類のセキュリティが使用されますか? シングルキャストまたはマルチキャストですか? これらの設定は、チャネルの "バインド" と呼ばれます。 バインディングは次で構成されます。

  • 使用する転送プロトコル (TCP、UDP、HTTP、NAMEDPIPE) を識別する WS_CHANNEL_BINDING
  • チャネルをセキュリティで保護する方法を指定する WS_SECURITY_DESCRIPTION
  • セット WS_CHANNEL_PROPERTY、追加のオプション設定を指定します。 プロパティ 一覧については、「WS_CHANNEL_PROPERTY_ID」を参照してください。

チャネル プロパティの一覧を示す図。

リスナー

通信を開始するために、クライアントは Channel オブジェクトを作成します。 しかし、サービスはどのように Channel オブジェクトを取得しますか? これを行うには、リスナーを作成 します。 リスナーを作成するには、チャネルの作成に必要なのと同じバインディング情報が必要です。 リスナーが作成されると、アプリケーションはリスナーからチャネルを受け入れます。 アプリケーションはチャネルを受け入れるのに遅れる可能性があるため、リスナーは通常、受け入れる準備ができているチャネルのキューを保持します (一部のクォータまで)。

リスナー キュー内のチャネルを示す図。

通信の開始 (クライアント)

クライアントで通信を開始するには、次のシーケンスを使用します。

WsCreateChannel
for each address being sent to
{
    WsOpenChannel           // open channel to address
    // send and/or receive messages
    WsCloseChannel          // close channel
    WsResetChannel?         // reset if opening again
}
WsFreeChannel

通信の受け入れ (サーバー)

サーバーで受信通信を受け入れるには、次のシーケンスを使用します。

WsCreateListener
WsOpenListener
for each channel being accepted (can be done in parallel)
{
    WsCreateChannelForListener
    for each accept
    {
        WsAcceptChannel     // accept the channel
        // send and/or receive messages
        WsCloseChannel      // close the channel
        WsResetChannel?     // reset if accepting again
    }
    WsFreeChannel
}
WsCloseListener
WsFreeListener

メッセージの送信 (クライアントまたはサーバー)

メッセージを送信するには、次のシーケンスを使用します。

WsCreateMessageForChannel
for each message being sent
{
    WsSendMessage       // send message
    WsResetMessage?     // reset if sending another message
}
WsFreeMessage

WsSendMessage 関数はストリーミングを許可せず、本文に含まれる要素が 1 つだけであることを前提としています。 これらの制約を回避するには、 WsSendMessage の代わりに次のシーケンスを使用します。

WsInitializeMessage     // initialize message to WS_BLANK_MESSAGE
WsSetHeader             // serialize action header into header buffer
WsAddressMessage?       // optionally address message
for each application defined header
{
    WsAddCustomHeader   // serialize application-defined headers into header buffer
}
WsWriteMessageStart     // write out the headers of the message
for each element of the body
{
    WsWriteBody         // serialize the element of the body
    WsFlushBody?        // optionally flush the body
}
WsWriteMessageEnd       // write the end of the message

WsWriteBody 関数は、シリアル化を使用して body 要素を書き込みます。 XML ライターにデータを直接書き込むには、 WsWriteBody の代わりに次のシーケンスを使用します。

WS_MESSAGE_PROPERTY_BODY_WRITER     // get the writer used to write the body
WsWriteStartElement
// use the writer functions to write the body
WsWriteEndElement
// optionally flush the body
WsFlushBody?        

WsAddCustomHeader 関数は、シリアル化を使用してヘッダーをメッセージのヘッダー バッファーに設定します。 XML ライターを使用してヘッダーを書き込むには、 WsAddCustomHeader の代わりに次のシーケンスを使用します。

WS_MESSAGE_PROPERTY_HEADER_BUFFER   // get the header buffer 
WsCreateWriter                      // create an xml writer
WsSetOutputToBuffer                 // specify output of writer should go to buffer
WsMoveWriter*                       // move to inside envelope header element
WsWriteStartElement                 // write application header start element
// use the writer functions to write the header 
WsWriteEndElement                   // write appilcation header end element

メッセージの受信 (クライアントまたはサーバー)

メッセージを受信するには、次のシーケンスを使用します。

WsCreateMessageForChannel
for each message being received
{
    WsReceiveMessage            // receive a message
    WsGetHeader*                // optionally access standard headers such as To or Action
    WsResetMessage              // reset if reading another message
}
WsFreeMessage

WsReceiveMessage 関数はストリーミングを許可せず、本文に含まれる要素が 1 つだけであり、メッセージの種類 (本文のアクションとスキーマ) が前もってわかっていることを前提としています。 これらの制約を回避するには、 WsReceiveMessage の代わりに次のシーケンスを使用します。

WsReadMessageStart              // read all headers into header buffer
for each standard header
{
    WsGetHeader                 // deserialize standard header such as To or Action
}
for each application defined header
{
    WsGetCustomHeader           // deserialize application defined header
}
for each element of the body
{
    WsFillBody?                 // optionally fill the body
    WsReadBody                  // deserialize element of body
}
WsReadMessageEnd                // read end of message

WsReadBody 関数は、シリアル化を使用して body 要素を読み取ります。 XML リーダーからデータを直接読み取る場合は、WsReadBody の代わりに次のシーケンスを使用します。

WS_MESSAGE_PROPERTY_BODY_READER     // get the reader used to read the body
WsFillBody?                         // optionally fill the body
WsReadToStartElement                // read up to the body element
WsReadStartElement                  // consume the start of the body element
// use the read functions to read the contents of the body element
WsReadEndElement                    // consume the end of the body element

WsGetCustomHeader 関数は、シリアル化を使用して、メッセージのヘッダー バッファーからヘッダーを取得します。 XML リーダーを使用してヘッダーを読み取る場合は、WsGetCustomHeader の代わりに次のシーケンスを使用します。

WS_MESSAGE_PROPERTY_HEADER_BUFFER   // get the header buffer 
WsCreateReader                      // create an xml reader
WsSetInputToBuffer                  // specify input of reader should be buffer
WsMoveReader*                       // move to inside header element
while looking for header to read
{
    WsReadToStartElement            // see if the header matches the application header
    if header matched
    {
        WsGetHeaderAttributes?      // get the standard header attributes
        WsReadStartElement          // consume the start of the header element
        // use the read functions to read the contents of the header element
        WsReadEndElement            // consume the end of the header element
    }
    else
    {
        WsSkipNode                  // skip the header element
    }
}                

要求応答 (クライアント)

クライアントで要求/応答を実行するには、次のシーケンスを使用します。

WsCreateMessageForChannel               // create request message 
WsCreateMessageForChannel               // create reply message 
for each request reply
{
    WsRequestReply                      // send request, receive reply
    WsResetMessage?                     // reset request message (if repeating)
    WsResetMessage?                     // reset reply message (if repeating)
}
WsFreeMessage                           // free request message
WsFreeMessage                           // free reply message

WsRequestReply 関数は、要求メッセージと応答メッセージの本文の 1 つの要素を想定し、メッセージの種類 (本文のアクションとスキーマ) が前もって認識されていることを前提としています。 これらの制限を回避するために、次のシーケンスに示すように、要求メッセージと応答メッセージを手動で送信できます。 このシーケンスは、前述の場合を除き、メッセージの送受信に関する以前のシーケンスと一致します。

WsInitializeMessage     // initialize message to WS_BLANK_MESSAGE
WsSetHeader             // serialize action header into header buffer
WsAddressMessage?       // optionally address message

// the following block is specific to sending a request
{
    generate a unique MessageID for request
    WsSetHeader         // set the message ID            
}

for each application defined header
{
    WsAddCustomHeader   // serialize application-defined headers into header buffer
}
WsWriteMessageStart     // write out the headers of the message
for each element of the body
{
    WsWriteBody         // serialize the element of the body
    WsFlushBody?        // optionally flush the body
}
WsWriteMessageEnd       // write the end of the message

WsReadMessageStart      // read all headers into header buffer

// the following is specific to receiving a reply
{
    WsGetHeader         // deserialize RelatesTo ID of reply
    verify request MessageID is equal to RelatesTo ID
}

for each standard header
{
    WsGetHeader         // deserialize standard header such as To or Action
}
for each application defined header
{
    WsGetCustomHeader   // deserialize application defined header
}
for each element of the body
{
    WsFillBody?         // optionally fill the body
    WsReadBody          // deserialize element of body
}
WsReadMessageEnd        // read end of message                

応答の要求 (サーバー)

サーバーで要求メッセージを受信するには、メッセージの受信に関する前のセクションで説明したのと同じシーケンスを使用します。

応答またはエラー メッセージを送信するには、次のシーケンスを使用します。

WsCreateMessageForChannel
for each reply being sent
{
    WsSendReplyMessage | WsSendFaultMessageForError  // send reply or fault message
    WsResetMessage?     // reset if sending another message
}
WsFreeMessage

WsSendReplyMessage 関数は、本文で 1 つの要素を想定しており、ストリーミングを許可しません。 これらの制限を回避するには、次のシーケンスを使用します。 これは、メッセージを送信するための以前のシーケンスと同じですが、初期化時に WS_BLANK_MESSAGE ではなく WS_REPLY_MESSAGE を使用します。

// the following block is specific to sending a reply
{
    WsInitializeMessage // initialize message to WS_REPLY_MESSAGE
}
WsSetHeader             // serialize action header into header buffer                                
WsAddressMessage?       // optionally address message
for each application defined header
{
    WsAddCustomHeader   // serialize application-defined headers into header buffer
}
WsWriteMessageStart     // write out the headers of the message
for each element of the body
{
    WsWriteBody         // serialize the element of the body
    WsFlushBody?        // optionally flush the body
}
WsWriteMessageEnd       // write the end of the message

メッセージ交換パターン

WS_CHANNEL_TYPEは、特定のチャネルで可能なメッセージ交換パターンを指示します。 サポートされる型は、バインドによって次のように異なります。

メッセージ ループ

メッセージ交換パターンごとに、メッセージの送受信に使用できる特定の "ループ" があります。 ループは、複数のメッセージを送受信するために必要な操作の法的順序を記述します。 ループは、文法の実稼働として以下に説明します。 "終了" という用語は、 WS_S_END が返される受信です ( 「Windows Web サービスの戻り値」を参照)、チャネルで使用できるメッセージがなくなったことを示します。 並列運用では、parallel(x & y) の場合、操作 x を y と同時に実行できます。

クライアントでは、次のループが使用されます。

client-loop := client-request-loop | client-duplex-session-loop | client-duplex-loop
client-request-loop := open (send (receive | end))* close // WS_CHANNEL_TYPE_REQUEST
client-duplex-session-loop := open parallel(send* & receive*) parallel(send? & end*) close // WS_CHANNEL_TYPE_DUPLEX_SESSION
client-duplex-loop := open parallel(send & receive)* close // WS_CHANNEL_TYPE_DUPLEX

サーバーでは、次のループが使用されます。

server-loop: server-reply-loop | server-duplex-session-loop | server-duplex-loop
server-reply-loop := accept receive end* send? end* close // WS_CHANNEL_TYPE_REPLY
server-duplex-session-loop := accept parallel(send* & receive*) parallel(send* & end*) close // WS_CHANNEL_TYPE_DUPLEX_SESSION
server-input-loop := accept receive end* close // WS_CHANNEL_TYPE_INPUT

サーバーで WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING を使用するには、WS_CHANNEL_TYPE_DUPLEX_SESSIONの種類のチャネルでも送信が許可される前に、正常な受信 必要です。 最初の受信後。 通常のループが適用されます。

WS_CHANNEL_TYPE_REQUESTWS_CHANNEL_TYPE_REPLYの種類のチャネルは、一方向のメッセージ (および標準の要求/応答パターン) の送受信に使用できることに注意してください。 これは、応答を送信せずに応答チャネルを閉じることによって実現されます。 この場合、要求チャネルで受信した応答はありません。 戻り値 WS_S_END サーバーで WS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDING を使用するには、WS_CHANNEL_TYPE_DUPLEX_SESSION型のチャネルでも送信が許可される前に、正常な受信 必要です。 最初の受信後、通常のループが適用されます。

は、使用可能なメッセージがないことを示す が返されます。

クライアントまたはサーバーのループは、複数のチャネル インスタンスを使用して、相互に並列に実行できます。

parallel-client: parallel(client-loop(channel1) & client-loop(channel2) & ...)
parallel-server: parallel(server-loop(channel1) & server-loop(channel2) & ...)

メッセージのフィルター処理

サーバー チャネルでは、 セキュリティ コンテキストを確立するメッセージなど、アプリケーション用ではない受信メッセージをフィルター処理できます。 その場合 、WS_S_ENDWsReadMessageStart から返され、そのチャネルで使用できるアプリケーション メッセージはありません。 ただし、これは、クライアントがサーバーとの通信を終了することを意図していることを示すものではありません。 他のチャネルで使用できるメッセージが増える場合があります。 「WsShutdownSessionChannel」を参照してください。

キャンセル

WsAbortChannel 関数は、チャネルの保留中の IO を取り消すために使用されます。 この API は、IO 操作が完了するまで待機しません。 詳細については、 WS_CHANNEL_STATE 状態図と WsAbortChannel のドキュメントを参照してください。

WsAbortListener API は、リスナーの保留中の IO を取り消すために使用されます。 この API は、IO 操作が完了するまで待機しません。 リスナーを中止すると、保留中の受け入れも中止されます。 詳細については、 WS_LISTENER_STATE 状態図と WsAbortListener を参照してください。

TCP

WS_TCP_CHANNEL_BINDINGでは、TCP 経由の SOAP がサポートされます。 SOAP over TCP 仕様は、.NET フレーム メカニズムに基づいています。

このバージョンでは、ポート共有はサポートされていません。 開かれる各リスナーは、異なるポート番号を使用する必要があります。

UDP

WS_UDP_CHANNEL_BINDINGでは、UDP 経由の SOAP がサポートされます。

UDP バインディングには、いくつかの制限があります。

  • セキュリティはサポートされません。
  • メッセージが失われたり重複したりする可能性があります。
  • サポートされているエンコードは 1 つだけです: WS_ENCODING_XML_UTF8
  • メッセージは基本的に 64k に制限されており、サイズがネットワークの MTU を超えると失われる可能性が高くなります。

HTTP

WS_HTTP_CHANNEL_BINDINGでは、HTTP 経由の SOAP がサポートされます。

クライアントとサーバーで HTTP 固有のヘッダーを制御するには、「 WS_HTTP_MESSAGE_MAPPING」を参照してください。

サーバーで SOAP 以外のメッセージを送受信するには、WS_CHANNEL_PROPERTY_ENCODINGにWS_ENCODING_RAWを使用します。

NAMEDPIPES

WS_NAMEDPIPE_CHANNEL_BINDINGでは、名前付きパイプに対する SOAP がサポートされており、NetNamedPipeBinding を使用して Windows Communication Foundation (WCF) サービスと通信できます。

要求/応答メッセージの関連付け

要求/応答メッセージは、次の 2 つの方法のいずれかで関連付けます。

  • 関連付けは、チャネルを関連付けメカニズムとして使用して行われます。 たとえば、 WS_ADDRESSING_VERSION_TRANSPORT を使用し、要求メッセージの応答を WS_HTTP_CHANNEL_BINDING する場合は、それが HTTP 応答のエンティティ本文であるという事実によって要求に関連付けられます。
  • 関連付けは、MessageID ヘッダーと RelatesTo ヘッダーを使用して行われます。 このメカニズムは、 (WS_HTTP_CHANNEL_BINDING を使用している場合でも) WS_ADDRESSING_VERSION_1_0WS_ADDRESSING_VERSION_0_9で使用されます。 この場合、要求メッセージには MessageID ヘッダーが含まれます。 応答メッセージには、要求の MessageID ヘッダーの値を持つ RelatesTo ヘッダーが含まれています。 RelatesTo ヘッダーを使用すると、クライアントは応答を送信した要求と関連付けることができます。

次のチャネル レイヤー API は、チャネルの WS_ADDRESSING_VERSION に基づいて適切な関連付けメカニズムを自動的に使用します。

これらの API を使用しない場合は、 WsSetHeader または WsGetHeader を使用してヘッダーを手動で追加してアクセスできます。

カスタム チャネルとリスナー

定義済みのチャネル バインドのセットがアプリケーションのニーズを満たさない場合は、チャネルまたはリスナーの作成時に WS_CUSTOM_CHANNEL_BINDING を指定することで、カスタム チャネルとリスナーの実装を定義できます。 チャネル/リスナーの実際の実装は、 WS_CHANNEL_PROPERTY_CUSTOM_CHANNEL_CALLBACKS または WS_LISTENER_PROPERTY_CUSTOM_LISTENER_CALLBACKS プロパティを使用してコールバックのセットとして指定されます。 カスタム チャネルまたはリスナーが作成されると、結果は既存の API で使用できる WS_CHANNEL または WS_LISTENER オブジェクトになります。

カスタム チャネルとリスナーは、サービス プロキシまたはサービス ホストの作成時に、WS_CHANNEL_BINDING列挙のWS_CUSTOM_CHANNEL_BINDING値と、WS_CHANNEL_PROPERTY_CUSTOM_CHANNEL_CALLBACKSプロパティとWS_LISTENER_PROPERTY_CUSTOM_LISTENER_CALLBACKSプロパティを指定することで、サービス プロキシとサービス ホストで使用することもできます。

セキュリティ

チャネルを使用すると、次のようなプロパティを使用して、操作のさまざまな側面に使用されるメモリの量を制限できます。

これらのプロパティには、ほとんどのシナリオで保守的で安全な既定値があります。 既定値とそれらに対する変更は、リモート ユーザーによるサービス拒否の原因となる可能性がある潜在的な攻撃ベクトルに対して慎重に評価する必要があります。

チャネルでは、次のようなプロパティを使用して、操作のさまざまな側面のタイムアウト値を設定できます。

これらのプロパティには、ほとんどのシナリオで保守的で安全な既定値があります。 タイムアウト値を大きくすると、リモート パーティがローカル リソース (メモリ、ソケット、同期 I/O を実行しているスレッドなど) を保持できる時間が長くなります。 アプリケーションは既定値を評価し、リモート コンピューターからサービス拒否を引き起こす可能性がある潜在的な攻撃ベクトルを開く可能性があるため、タイムアウトを増やす場合は注意が必要です。

WWSAPI チャネル API を使用する場合に慎重に評価する必要があるその他の構成オプションとアプリケーション設計に関する考慮事項の一部を次に示します。

  • チャネル/リスナー レイヤーを使用する場合は、サーバー側でチャネルを作成して受け入れるのはアプリケーションに依存します。 同様に、クライアント側でチャネルを作成して開くのはアプリケーション次第です。 アプリケーションでは、各チャネルがメモリやその他の限られたリソース (ソケットなど) を消費するため、これらの操作に上限を設定する必要があります。 アプリケーションは、リモート パーティによってトリガーされるアクションに応答してチャネルを作成する場合は特に注意する必要があります。
  • チャネルを作成して受け入れるロジックを記述するのは、アプリケーションにかかっています。 各チャネルは、メモリやソケットなどの限られたリソースを消費します。 アプリケーションは、受け入れるチャネルの数に上限を設定する必要があります。または、リモート パーティが多数の接続を行うと、OOM が発生し、サービス拒否が発生する可能性があります。 また、小さなタイムアウトを使用して、これらの接続からメッセージをアクティブに受信する必要もあります。 メッセージが受信されない場合、操作はタイムアウトし、接続を解放する必要があります。
  • ReplyTo または FaultTo SOAP ヘッダーを解釈して、応答またはエラーを送信するのはアプリケーションの責任です。 セキュリティで保護された方法では、"匿名" である ReplyTo ヘッダーまたは FaultTo ヘッダーのみを優先します。つまり、SOAP 応答を送信するために既存の接続 (TCP、HTTP) またはソース IP (UDP) を使用する必要があります。 アプリケーションは、別のアドレスに返信するためにリソース (チャネルなど) を作成する場合は、応答の送信先アドレスを話すことができるパーティによってメッセージが署名されていない限り、細心の注意を払う必要があります。
  • チャネル レイヤーで行われる検証は、セキュリティによって実現されるデータ整合性に代わるものはありません。 アプリケーションは WWSAPI のセキュリティ機能に依存して信頼されたエンティティと通信し、データの整合性を確保するためにセキュリティにも依存する必要があります。

同様に、WWSAPI Message API を使用する場合は慎重に評価する必要があるメッセージ構成オプションとアプリケーション設計に関する考慮事項があります。

  • メッセージのヘッダーを格納するために使用するヒープのサイズは、 WS_MESSAGE_PROPERTY_HEAP_PROPERTIES プロパティを使用して構成できます。 この値を大きくすると、メッセージのヘッダーによって消費されるメモリが増え、OOM が発生する可能性があります。
  • メッセージ オブジェクトのユーザーは、ヘッダー アクセス API がメッセージ内のヘッダーの数に関して O(n) であることを認識する必要があります。これは、重複がチェックされるためです。 メッセージに多くのヘッダーが必要な設計では、CPU 使用率が過剰になる可能性があります。
  • メッセージ内のヘッダーの最大数は、 WS_MESSAGE_PROPERTY_MAX_PROCESSED_HEADERS プロパティを使用して構成できます。 メッセージのヒープのサイズに基づく暗黙的な制限もあります。 これらの両方の値を増やすと、より多くのヘッダーが存在し、ヘッダーを見つけるのに必要な時間が増えます (ヘッダー アクセス API を使用する場合)。