チャネル レイヤーの概要

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

Message

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

Diagram showing the header and body of a message.

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

Diagram showing several message encoding formats.

チャネル

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

チャネルには、送信時にメッセージを アドレス指定 する方法を説明するデータが関連付けられています。 チャネルにメッセージを送信することは、チャネルをシュートに配置するのと同じです。チャネルには、メッセージを送信する必要がある情報と、そこにメッセージを取得する方法が含まれます。

Diagram showing channels for messages.

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

Diagram showing sessionful and sessionless channel types.

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

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

Diagram showing a list of channel properties.

リスナー

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

Diagram showing channels in the Listener queue.

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

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

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 関数は、シリアル化を使用して本文要素を書き込みます。 データを 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 関数は、シリアル化を使用して本文要素を読み取る。 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が返される受信です (web サービスの戻り値Windows参照)、チャネルで使用できるメッセージがこれ以上ないことを示します。 並列実稼働では、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_REQUESTタイプWS_CHANNEL_TYPE_REPLYのチャネルは、一方向メッセージ (標準の要求/応答パターン) の送受信に使用できることに注意してください。 これは、応答を送信せずに応答チャネルを閉じることで実現されます。 この場合、要求チャネルで受信した応答はありません。 サーバーでWS_SECURITY_CONTEXT_MESSAGE_SECURITY_BINDINGを使用するWS_S_END戻り値は、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 バインディングにはいくつかの制限があります。

  • セキュリティのサポートはありません。
  • メッセージが失われたり、重複したりする可能性があります。
  • サポートされているエンコードは、 WS_ENCODING_XML_UTF8の 1 つだけです。
  • メッセージは基本的に 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_0とWS_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 を使用する場合)。