Поделиться через


Обзор уровня канала

Уровень канала предоставляет абстракцию транспортного канала, а также сообщения, отправленные на канал. Она также включает функции сериализации типов данных C в структуры SOAP и из нее. Уровень каналов обеспечивает полный контроль над обменом сообщениями с помощью сообщений, состоящих из отправленных или полученных данных и содержащих тела и заголовки, а также каналы , которые абстрагируют протоколы обмена сообщениями и предоставляют свойства для настройки параметров.

Message

Сообщение — это объект, инкапсулирующий сетевые данные, в частности данные, передаваемые или полученные по сети. Структура сообщений определяется SOAP с дискретным набором заголовков и текстом сообщения. Заголовки помещаются в буфер памяти, а текст сообщения считывается или записывается с помощью API потока.

Diagram showing the header and body of a message.

Хотя модель данных сообщения всегда является моделью данных XML, фактический формат провода является гибким. Перед передачей сообщения кодируется с помощью определенной кодировки (например, text, binary или MTOM). Дополнительные сведения о кодировках см . в WS_ENCODING .

Diagram showing several message encoding formats.

Канал

Канал — это объект, используемый для отправки и получения сообщений в сети между двумя или более конечными точками.

Каналы имеют связанные данные, описывающие, как устранить сообщение при отправке. Отправка сообщения на канал похожа на размещение его в чулке — канал содержит сведения о том, где должно идти сообщение и как его получить.

Diagram showing channels for messages.

Каналы классифицируются по типам каналов. Тип канала указывает, какое направление сообщений может выполняться. Тип канала также определяет, является ли канал сеансным или без сеанса. Сеанс определяется как абстрактный способ корреляции сообщений между двумя или несколькими сторонами. Примером сеансового канала является TCP-канал, который использует TCP-подключение в качестве конкретной реализации сеанса. Примером канала без сеанса является UDP, который не имеет базового механизма сеанса. Хотя HTTP имеет базовые TCP-подключения, этот факт не предоставляется напрямую через этот API, поэтому HTTP также считается бессерверным каналом.

Diagram showing sessionful and sessionless channel types.

Хотя типы каналов описывают сведения о направлении и сеансе для канала, они не указывают, как реализуется канал. Какой протокол должен использовать канал? Насколько трудно каналу попытаться доставить сообщение? Какой тип безопасности используется? Это одноадресная рассылка или многоадресная рассылка? Эти параметры называются привязкой канала. Привязка состоит из следующих элементов:

  • WS_CHANNEL_BINDING, определяющий используемый протокол передачи (TCP, UDP, HTTP, NAMEDPIPE).
  • WS_SECURITY_DESCRIPTION, который указывает, как защитить канал.
  • Набор WS_CHANNEL_PROPERTYs, указывающий дополнительные необязательные параметры. Список свойств см . в 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 не допускает потоковую передачу и предполагает, что текст содержит только один элемент. Чтобы избежать этих ограничений, используйте следующую последовательность вместо 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 application 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 не допускает потоковую передачу и предполагает, что текст содержит только один элемент, и что тип сообщения (действие и схема тела) известен заранее. Чтобы избежать этих ограничений, используйте следующую последовательность вместо 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 предполагает один элемент для текста сообщений запроса и ответа, а также то, что тип сообщения (действие и схема тела) известны заранее. Чтобы избежать этих ограничений, запрос и ответное сообщение можно отправить вручную, как показано в следующей последовательности. Эта последовательность соответствует предыдущей последовательности для отправки и получения сообщения, за исключением отмеченного.

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 предполагает один элемент в тексте и не разрешает потоковую передачу. Чтобы избежать этих ограничений, используйте следующую последовательность. Эта последовательность аналогична предыдущей последовательности отправки сообщения, но использует WS_REPLY_MESSAGE вместо WS_BLANK_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_HTTP_CHANNEL_BINDING поддерживает WS_CHANNEL_TYPE_REQUEST на клиенте и WS_CHANNEL_TYPE_REPLY на сервере.
  • WS_TCP_CHANNEL_BINDING поддерживает WS_CHANNEL_TYPE_DUPLEX_SESSION на клиенте и WS_CHANNEL_TYPE_DUPLEX_SESSION на сервере.
  • WS_UDP_CHANNEL_BINDING поддерживает WS_CHANNEL_TYPE_DUPLEX на клиенте и WS_CHANNEL_TYPE_INPUT на сервере.
  • WS_NAMEDPIPE_CHANNEL_BINDING поддерживает WS_CHANNEL_TYPE_DUPLEX на клиенте и WS_CHANNEL_TYPE_INPUT на сервере.

Циклы сообщений

Для каждого шаблона обмена сообщениями существует определенный цикл, который можно использовать для отправки или получения сообщений. Цикл описывает юридический порядок операций, необходимых для отправки и получения нескольких сообщений. Циклы описаны ниже как грамматические рабочие области. Термин "end" — это получение, в котором возвращается WS_S_END (см . значения возвращаемых веб-служб 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_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_END будут возвращены из WsReadMessageStart, и сообщения приложения не будут доступны в этом канале. Однако это не означает, что клиент намерен завершить связь с сервером. Дополнительные сообщения могут быть доступны на другом канале. См. раздел WsShutdownSessionChannel.

Отмена

Функция WsAbortChannel используется для отмены ожидающих операций ввода-вывода для канала. Этот API не ожидает завершения операций ввода-вывода. Дополнительные сведения см. в схеме состояния WS_CHANNEL_STATE и документации по WsAbortChannel.

API WsAbortListener используется для отмены ожидающих операций ввода-вывода для прослушивателя. Этот API не ожидает завершения операций ввода-вывода. Прерывание прослушивателя приведет к прерыванию любых ожидающих приемов. Дополнительные сведения см. на схеме состояния WS_LISTENER_STATE и WsAbortListener.

TCP

WS_TCP_CHANNEL_BINDING поддерживает SOAP по протоколу TCP. Спецификация SOAP по протоколу TCP основана на механизме обрамления .NET.

Общий доступ к портам не поддерживается в этой версии. Каждый открытый прослушиватель должен использовать другой номер порта.

UDP

WS_UDP_CHANNEL_BINDING поддерживает SOAP по протоколу UDP.

Существует ряд ограничений при привязке UDP:

  • Нет поддержки безопасности.
  • Сообщения могут быть потеряны или дублируются.
  • Поддерживается только одна кодировка: WS_ENCODING_XML_UTF8.
  • Сообщения в основном ограничены 64k и часто имеют больший шанс потеряться, если размер превышает MTU сети.

HTTP

WS_HTTP_CHANNEL_BINDING поддерживает SOAP по протоколу HTTP.

Сведения об управлении определенными заголовками HTTP на клиенте и сервере см . в WS_HTTP_MESSAGE_MAPPING.

Чтобы отправлять и получать сообщения, отличные от SOAP на сервере, используйте WS_ENCODING_RAW для WS_CHANNEL_PROPERTY_ENCODING.

NAMEDPIPES

WS_NAMEDPIPE_CHANNEL_BINDING поддерживает SOAP через именованные каналы, позволяя обмен данными со службой Windows Communication Foundation (WCF) с помощью NetNamedPipeBinding.

Сопоставление сообщений запросов и ответов

Сообщения запроса и ответа коррелируются одним из двух способов:

  • Корреляция выполняется с помощью канала в качестве механизма корреляции. Например, при использовании WS_ADDRESSING_VERSION_TRANSPORT и WS_HTTP_CHANNEL_BINDING ответ на сообщение запроса сопоставляется с запросом тем фактом, что это тело сущности http-ответа.
  • Корреляция выполняется с помощью заголовков MessageID и RelatesTo. Этот механизм используется с WS_ADDRESSING_VERSION_1_0 и WS_ADDRESSING_VERSION_0_9 (даже при использовании WS_HTTP_CHANNEL_BINDING). В этом случае сообщение запроса содержит заголовок MessageID. В ответном сообщении содержится заголовок RelatesTo, имеющий значение заголовка MessageID запроса. Заголовок RelatesTo позволяет клиенту сопоставлять ответ с отправленным запросом.

Следующие API уровня каналов автоматически используют соответствующие механизмы корреляции на основе WS_ADDRESSING_VERSION канала.

Если эти API не используются, заголовки можно добавлять вручную и получать к ней доступ с помощью WsSetHeader или WsGetHeader.

Пользовательские каналы и прослушиватели

Если предопределенный набор привязок каналов не соответствует потребностям приложения, можно определить пользовательскую реализацию канала и прослушивателя, указав WS_CUSTOM_CHANNEL_BINDING при создании канала или прослушивателя. Фактическая реализация канала или прослушивателя указывается как набор обратных вызовов с помощью WS_CHANNEL_PROPERTY_CUSTOM_CHANNEL_CALLBACKS или WS_LISTENER_PROPERTY_CUSTOM_LISTENER_CALLBACKS свойств. После создания пользовательского канала или прослушивателя результатом является объект WS_CHANNEL или WS_LISTENER , который можно использовать с существующими API.

Настраиваемый канал и прослушиватель также можно использовать с прокси-сервером службы и узлом службы, указав WS_CUSTOM_CHANNEL_BINDING значение в перечислении WS_CHANNEL_BINDING, а также свойства WS_CHANNEL_PROPERTY_CUSTOM_CHANNEL_CALLBACKS и WS_LISTENER_PROPERTY_CUSTOM_LISTENER_CALLBACKS при создании прокси-сервера или узла службы.

Безопасность

Канал позволяет ограничить объем памяти, используемой для различных аспектов операций с помощью таких свойств, как:

  • WS_CHANNEL_PROPERTY_MAX_BUFFERED_MESSAGE_SIZE,
  • WS_CHANNEL_PROPERTY_MAX_STREAMED_MESSAGE_SIZE,
  • WS_CHANNEL_PROPERTY_MAX_STREAMED_START_SIZE,
  • WS_CHANNEL_PROPERTY_MAX_HTTP_REQUEST_HEADERS_BUFFER_SIZE,
  • WS_CHANNEL_PROPERTY_MAX_SESSION_DICTIONARY_SIZE.

Эти свойства имеют значения по умолчанию, которые являются консервативными и безопасными для большинства сценариев. Значения по умолчанию и любые изменения должны тщательно оцениваться в отношении потенциальных векторов атак, которые могут привести к отказу в обслуживании удаленным пользователем.

Канал позволяет задавать значения времени ожидания для различных аспектов операций с помощью таких свойств, как:

  • WS_CHANNEL_PROPERTY_CONNECT_TIMEOUT,
  • WS_CHANNEL_PROPERTY_SEND_TIMEOUT,
  • WS_CHANNEL_PROPERTY_RECEIVE_RESPONSE_TIMEOUT,
  • WS_CHANNEL_PROPERTY_RECEIVE_TIMEOUT,
  • WS_CHANNEL_PROPERTY_RESOLVE_TIMEOUT,
  • WS_CHANNEL_PROPERTY_CLOSE_TIMEOUT.

Эти свойства имеют значения по умолчанию, которые являются консервативными и безопасными для большинства сценариев. Увеличение значений времени ожидания увеличивает время, которое удаленный участник может хранить локальный ресурс в живых, например память, сокеты и потоки, выполняющие синхронные операции ввода-вывода. Приложение должно оценивать значения по умолчанию и использовать осторожность при увеличении времени ожидания, так как это может открыть потенциальные векторы атак, которые могут привести к отказу в обслуживании с удаленного компьютера.

Некоторые другие параметры конфигурации и рекомендации по проектированию приложений, которые следует тщательно оценивать при использовании API канала WWSAPI:

  • При использовании уровня канала или прослушивателя приложение может создавать и принимать каналы на стороне сервера. Аналогичным образом приложение может создавать и открывать каналы на стороне клиента. Приложение должно поместить верхнюю границу в эти операции, так как каждый канал потребляет память и другие ограниченные ресурсы, такие как сокеты. Приложение должно быть особенно осторожным при создании канала в ответ на любые действия, инициируемые удаленной стороной.
  • Это до приложения, чтобы написать логику для создания каналов и принятия их. Каждый канал потребляет ограниченные ресурсы, такие как память и сокеты. Приложение должно иметь верхнюю границу по количеству каналов, которые он готов принять, или удаленная сторона может сделать много подключений, что приводит к OOM и, следовательно, отказ в обслуживании. Он также должен активно получать сообщения от этих подключений с помощью небольшого времени ожидания. Если сообщения не получены, операция завершится и подключение должно быть выпущено.
  • Приложение может отправлять ответ или ошибку, интерпретируя заголовки ReplyTo или FaultTo SOAP. Безопасная практика заключается в том, чтобы учитывать только заголовок ReplyTo или FaultTo, который является анонимным, то есть существующее подключение (TCP, HTTP) или исходный IP-адрес (UDP) следует использовать для отправки ответа SOAP. Приложения должны проявлять крайнюю осторожность при создании ресурсов (например, канала), чтобы ответить на другой адрес, если сообщение не было подписано стороной, которая может говорить за адрес, на который отправляется ответ.
  • Проверка, выполненная на уровне канала, не является заменой целостности данных, достигнутой с помощью безопасности. Приложение должно полагаться на функции безопасности WWSAPI, чтобы обеспечить связь с доверенной сущностью, а также полагаться на безопасность для обеспечения целостности данных.

Аналогичным образом существуют параметры конфигурации сообщений и рекомендации по проектированию приложений, которые следует тщательно оценивать при использовании API сообщений WWSAPI:

  • Размер кучи, используемой для хранения заголовков сообщения, можно настроить с помощью свойства WS_MESSAGE_PROPERTY_HEAP_PROPERTIES. Увеличение этого значения позволяет использовать больше памяти заголовками сообщения, что может привести к OOM.
  • Пользователь объекта сообщения должен понимать, что API доступа к заголовкам являются O(n) относительно количества заголовков в сообщении, так как они проверка для дубликатов. Проекты, требующие большого количества заголовков в сообщении, могут привести к чрезмерному использованию ЦП.
  • Максимальное количество заголовков в сообщении можно настроить с помощью свойства WS_MESSAGE_PROPERTY_MAX_PROCESSED_HEADERS. Существует также неявное ограничение на основе размера кучи сообщения. Увеличение обоих этих значений позволяет использовать больше заголовков, что приводит к соединению времени, необходимого для поиска заголовка (при использовании API доступа к заголовкам).