- •1. Язык программирования c# 3
- •2. Базовые элементы .Net Framework 67
- •3. ТЕхнология .Net Remoting 144
- •Введение
- •1. Язык программирования c#
- •1.1. Платформа .Net – обзор архитектуры
- •1.2. Язык c# - общие концепции синтаксиса
- •1.3. Система типов языка c#
- •1.4. Преобразования типов
- •1.5. Идентификаторы, ключевые слова и литералы
- •1.6. Объявление переменных, полей и констант
- •1.7. Выражения и операции
- •1.8. Операторы языка c#
- •1.9. Объявление и вызов методов
- •1.10. Массивы в c#
- •1.11. Работа с символами и строками в c#
- •1.12. Синтаксис объявления класса, Поля и методы класса
- •1.13. Свойства и индексаторы
- •1.14. Конструкторы класса и Жизненный цикл объекта
- •1.15. Наследование классов
- •1.16. Перегрузка операЦий
- •1.17. Делегаты
- •1.18. События
- •1.19. Интерфейсы
- •1.20. Структуры и перечисления
- •1.21. Пространства имен
- •1.22. Генерация и обработка исключительных ситуаций
- •1.23. Нововведения в языке c# 2.0
- •1.24. Обобщенные типы (generics)
- •2. Базовые элементы .Net Framework
- •2.1. Метаданные и механизм отражения
- •2.2. Пользовательские и встроенные атрибуты
- •2.3. Пространство имен system.Collections
- •2.4. Работа с файлами и директориями
- •2.5. Использование потоков данных
- •2.6. Сериализация
- •2.7. Сериализация объектов в нестандартном формате
- •2.8. Введение в xml
- •2.9. Работа с xml-документами в .Net framework
- •2.10. МНогопоточное программирование
- •2.11. Синхронизация потоков
- •2.12. Асинхронный вызов методов
- •2.13. Состав и взаимодействие сборок
- •2.14. Конфигурирование сборок
- •3. ТЕхнология .Net Remoting
- •3.1. Домены приложений
- •3.2. Архитектура .Net Remoting
- •3.3. Активация удаленных объектов и их время жизни
- •3.4. Программная настройка Remoting
- •3.5. Удаленные Объекты с клиентской активацией
- •3.6. Настройка Remoting при помощи конфигурационных файлов
- •3.7. Хостинг распределенных приложений
- •3.8. Объекты-сообщения
- •3.9. Пользовательские канальные приемники
- •4.1. Архитектура ado.Net
- •4.2. Учебная база cd Rent
- •4.3. Соединение с базой данных
- •4.4. Выполнение команд и запросов к базе данных
- •4.5. Чтение данных и объект DataReader
- •4.6. Параметризированные запросы
- •4.7. Рассоединенный набор данных
- •4.8. Заполнение Рассоединенного набора данных
- •4.9. Объект класса DataColumn – колонка таблицы
- •4.10. Объекты класса DataRow – строки таблицы
- •4.11. Работа с объектом класса DataTable
- •4.12. DataSet и схема рассоединенного набора данных
- •4.13. Типизированные DataSet
- •4.14. Поиск и фильтрация данных в DataSet
- •4.15. Класс DataView
- •4.16. СиНхронизация набора данных и базы
- •5.1. Архитектура и общие концепции asp.Net
- •5.2. Пример aspx-страницы. Структура страницы
- •5.3. Директивы страницы
- •5.4. Класс System.Web.Ui.Page. События страницы
- •5.5. Серверные элементы управления
- •5.6. Элементы управления Web Controls
- •5.7. Проверочные элементы управления
- •5.8. Списковые элементы управления
- •5.9. Связывание данных
- •5.11. Управление состояниями в web-приложениях
- •5.12. Кэширование
- •5.13. Безопасность в web-приложениях
- •5.14. Создание пользовательских элементов управления
- •Литература
3.8. Объекты-сообщения
При удаленном взаимодействии исполняющая среда манипулирует не ассемблерным кодом вызова методов, а специальными объектами-сообщениями. Для поддержки сообщений предоставлено несколько стандартных интерфейсов. Базовым является интерфейс IMessage (пространство имен System.Runtime.Remoting.Messaging). Любое сообщение – это объект, реализующий данный интерфейс. Интерфейс IMessage устроен просто. Он содержит единственное свойство Properties типа IDictionary для помещения в сообщение любых данных и идентифицирующих их ключей. Для удобства в .NET Framework описаны еще несколько интерфейсов – наследников IMessage. Схема наследования представлена на рисунке 8.
Рис. 8. Схема наследования интерфейсов
В таблице 20 указано назначение интерфейсов и перечислены некоторые их элементы.
Таблица 20
Интерфейсы для сообщений и их элементы
Интерфейс или класс |
Имя элемента |
Описание |
IMessage |
|
Реализуется любым сообщением |
IMethodMessage |
|
Сообщения, описывающие работу с методами |
Args |
Массив объектов, соответствующих аргументам метода |
|
MethodName |
Имя метода (строка) |
|
Uri |
Универсальный идентификатор (URI) объекта, которому направляется сообщение |
|
TypeName |
Строка с полным именем типа объекта, к которому направляется сообщение |
|
IMethodReturnMessage |
|
Сообщение, описывающее результат метода |
Exception |
Объект исключительной ситуации, если она сгенерирована удаленным объектом |
|
OutArgs |
Массив объектов, представляющих выходные параметры метода |
|
ReturnValue |
Объект, содержащий значение работы метода |
|
IMethodCallMessage |
|
Сообщение, соответствующее запуску метода |
InArgs |
Массив объектов, представляющих входные параметры метода |
|
IConstructionCall-Message |
|
Сообщение является первым направляемым к удаленному объекту, и определяет свойства для создания объекта |
ActivationType |
Тип удаленного объекта |
|
Activator |
Свойство для работы с активаторами объекта |
|
IConstructionReturnMessage |
|
Сообщение, реализующее интерфейс, посылается как ответ на IConstructionCallMessage |
3.9. Пользовательские канальные приемники
Одна из возможностей расширения .NET Remoting – написание собственного канального приемника. Канальный приемник (channel message sink) выступает в роли перехватчика сообщений или потока данных на стороне клиента или сервера. Укажем некоторые сценарии, требующие использования канальных приемников:
Шифрование данных. Необходимы канальные приемники для обработки потока данных на стороне клиента и сервера. На стороне клиента производится шифровка отсылаемого потока, на стороне сервера – расшифровка.
Авторизация доступа. На стороне клиента канальный приемник добавляет в сообщение дополнительную информацию, идентифицирующую клиента (имя/пароль). На стороне сервера приемник извлекает данную информацию и в зависимости от ее содержания обрабатывает или отклоняет сообщение.
Протоколирование сообщений. На стороне сервера канальный приемник заносит информацию об обработанных сообщениях в специальный файл или базу данных.
В .NET Framework канальные приемники описываются как классы, реализующие интерфейс IClientChannelSink (для клиента) или IServerChannelSink (для сервера). Канальные приемники соединены в цепочку (message sink chain). Каждый приемник хранит информацию о следующем элементе цепочки. Канальный приемник производит предварительную обработку сообщения, передает сообщение следующему приемнику в цепочке, после этого производится пост-обработка исходящего сообщения.
Установкой и настройкой канальных приемников занимаются специальные классы – провайдеры приемников. Это классы, реализующие интерфейс IClientChannelSinkProvider (для клиента) или IServerChannelSinkProvider (для сервера).
Созданные провайдеры подключаются к каналу при помощи конфигурационного файла1. Например, пусть имеется следующий конфигурационный файл (фрагмент):
<channels>
<channel ref="http">
<clientProviders>
<provider type="MySinks.MessageSinkProvider, Client" />
<formatter ref="soap" />
<provider
type="MySinks.ClientChannelSinkProvider, Client" />
</clientProviders>
</channel>
</channels>
В этом случае в структуре http-канала будут находиться такие провайдеры (и соответствующие им приемники):
Рис. 9. Провайдеры в структуре http-канала
Обратите внимание: среди провайдеров имеется стандартный, отвечающий за SOAP-форматирование, а последний провайдер – это элемент http-канала. Подчеркнем, что порядок объявления провайдеров имеет значение.
Работу с канальными приемниками рассмотрим на нескольких примерах. В первом примере создадим серверный приемник для протоколирования сообщений. Начнем с рассмотрения интерфейса System.Runtime.Remoting.Channels.IServerChannelSink.
public interface IServerChannelSink {
IServerChannelSink NextChannelSink { get; }
ServerProcessing ProcessMessage(
IServerChannelSinkStack sinkStack,
IMessage requestMsg,
ITransportHeaders requestHeaders,
Stream requestStream,
out IMessage responseMsg,
out ITransportHeaders responseHeaders,
out Stream responseStream);
void AsyncProcessResponse(
IServerResponseChannelSinkStack sinkStack,
object state,
IMessage msg,
ITransportHeaders headers,
Stream stream);
Stream GetResponseStream(
IServerResponseChannelSinkStack sinkStack,
object state,
IMessage msg,
ITransportHeaders headers);
}
Свойство NextChannelSink содержит ссылку на следующий канальный приемник в цепочке. Оно устанавливается конструктором класса-приемника.
Метод ProcessMessage() – основной метод при синхронной обработке сообщений и потоков. Его параметры:
sinkStack – стек серверных канальных приемников, предшествующих вызываемому;
requestMsg – сообщение-запрос;
requestHeaders – транспортные заголовки сообщения-запроса. Это таблица пар «ключ-значение». При помощи заголовка к сообщению можно присоединять дополнительную описательную информацию;
requestStream – поток, который следует обработать и направить в десериализатор;
responseMsg – после выполнения метода удаленного объекта этот параметр содержит возвращаемый IMessage;
responseHeaders – после выполнения метода удаленного объекта этот параметр содержит транспортные заголовки возвращаемого сообщения (если они есть);
responseStream – параметр содержит поток, направляемый клиенту после выполнения метода удаленного объекта.
При реализации тела метода ProcessMessage() нужно действовать по такой схеме. Вначале производится предварительная обработка сообщений или потоков1. После этого обработчик помещается в стек (sinkStack). Стек используется для асинхронной обработки. Затем следует вызвать метод обработки у следующего приемника в цепочке. Далее производится пост-обработка сообщений или потока.
Метод ProcessMessage() возвращает значение из перечисления ServerProcessing:
Async – вызов обработан асинхронно, и приемник должен сохранить возвращаемые данные в стеке для дальнейшей обработки;
Complete – сервер обработал сообщение в нормальном, синхронном режиме;
OneWay – сообщение было обработано, ответ высылаться не будет.
Метод AsyncProcessResponse() возвращает ответ сервера при обработке асинхронного сообщения. Параметры метода:
sinkStack – стек серверных канальных приемников, предшествующих серверному транспортному приемнику;
state – дополнительная информация, помещенная в стек вместе с объектом-обработчиком;
msg – сообщение-ответ;
headers – транспортные заголовки возвращаемого сообщения;
stream – поток, направляемый в транспортный приемник.
Метод GetResponseStream() создает объект Stream, содержащий объект IMessage, а также нужные пары «ключ-значение» из объекта ITransportHeaders.
Вернемся к нашему примеру. Представим код серверного канального приемника. Для краткости в листинге опущены параметры методов:
using System;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Collections;
using System.IO;
namespace MySink {
public class LogSink : BaseChannelSinkWithProperties,
IServerChannelSink {
private IServerChannelSink nextSink;
public LogSink(IServerChannelSink next) {
nextSink = next;
}
public IServerChannelSink NextChannelSink {
get { return nextSink; }
}
public void AsyncProcessResponse(. . .) {
IMethodReturnMessage msg_ret =
(IMethodReturnMessage)msg;
Console.WriteLine("[{0}]: {1} - return {2}",
DateTime.Now.ToString(),
msg_ret.MethodName,
msg_ret.ReturnValue);
sinkStack.AsyncProcessResponse(msg,headers,stream);
}
public Stream GetResponseStream(...) { return null; }
public ServerProcessing ProcessMessage(. . .) {
IMethodCallMessage msg =
(IMethodCallMessage) requestMsg;
Console.WriteLine("[{0}]: {1}",
DateTime.Now.ToString(),
msg.MethodName);
foreach (DictionaryEntry de in requestHeaders) {
Console.WriteLine("KEY: {0} VALUE: {1}",
de.Key, de.Value);
}
sinkStack.Push(this,null);
ServerProcessing srvProc =
nextSink.ProcessMessage(sinkStack,
requestMsg,
requestHeaders,
requestStream,
out responseMsg,
out responseHeaders,
out responseStream);
IMethodReturnMessage msg_ret =
(IMethodReturnMessage) responseMsg;
Console.WriteLine("[{0}]: {1} - return {2}",
DateTime.Now.ToString(),
msg_ret.MethodName,
msg_ret.ReturnValue);
return srvProc;
}
}
}
Дадим комментарии по коду класса. Наш класс LogSink является наследникам класса BaseChannelSinkWithProperties. Дело в том, что интерфейс IServerChannelSink (как и IClientChannelSink) наследуется от интерфейса IChannelSinkBase, имеющего единственное свойство – словарь Properties. Абстрактный класс BaseChannelSinkWithProperties предоставляет реализацию данного свойства.
Конструктор класса LogSink не выполняет никаких особых действий. Он просто устанавливает указатель на следующий канальный приемник. Вызывать данный конструктор будет провайдер нашего приемника, который и передаст требуемое значение параметра.
Основная логика работы класса сосредоточена в методе ProcessMessage(). Получив сообщений, метод производит вывод на консоль информации о нем. Кроме этого, выводятся данные транспортных заголовков сообщения. Затем объект-приемник помещается в стек. После обработки сообщение передается в следующий канальный приемник. Если этот канальный приемник завершил свою работу, то это значит, что можно производит обработку возвращаемого клиенту сообщения. Об этом сообщении также выводится некоторая информация на консоль.
Метод AsyncProcessResponse() по сути содержит логику пост-обработки сообщения, а затем передает сообщение дальше по цепочке. Так как наш приемник не создает особого потока по сообщению, метод GetResponseStream() просто возвращает null.
Для установки серверного канального приемника необходимо написать класс, реализующий IServerChannelSinkProvider:
public interface IServerChannelSinkProvider {
IServerChannelSinkProvider Next { get; set; }
IServerChannelSink CreateSink(IChannelReceiver channel);
void GetChannelData(IChannelDataStore channelData);
}
Свойство Next хранит ссылку на следующий провайдер в цепочке. В методе CreateSink() создается объект, соответствующий пользовательскому канальному приемнику. При этом в начале вызывается метод для создания стека приемников, а затем пользовательский приемник помещается на вершину стека. При помощи метода GetChannelData() можно получить различные характеристики того канала, с которым ассоциирован приемник.
Создадим провайдер приемника для нашего примера. Поместим его в ту же сборку, что и класс-приемник. Код провайдера приведен ниже:
namespace MySink {
public class LogSinkProvider: IServerChannelSinkProvider {
private IServerChannelSinkProvider nextProvider;
public LogSinkProvider(IDictionary properties,
ICollection providerData) { }
public IServerChannelSinkProvider Next {
get { return nextProvider; }
set { nextProvider = value; }
}
public IServerChannelSink CreateSink(
IChannelReceiver channel) {
IServerChannelSink next =
nextProvider.CreateSink(channel);
return new LogSink(next);
}
public void GetChannelData(
IChannelDataStore channelData) { }
}
}
В классе LogSinkProvider задан пустой конструктор. Наличие у провайдера конструктора с подобной сигнатурой является обязательным требованием. При помощи таких конструкторов можно извлечь данные для настройки провайдера, хранящиеся, например, в конфигурационном файле. Реализация остальных методов типична для провайдера.
Использование указанного канального приемника происходит при помощи следующего конфигурационного файла сервера. Для провайдера указывается его тип (включая имя пространства имен) и имя сборки.
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http" port="1234">
<serverProviders>
<formatter ref="soap" />
<provider type="MySink.LogSinkProvider, MySink" />
</serverProviders>
</channel>
</channels>
<service>
<wellknown mode="SingleCall"
type="BSUIR.Calculator, Calc"
objectUri="Calculator.soap" />
</service>
</application>
</system.runtime.remoting>
</configuration>
Сервер и клиент для данного примера можно написать по аналогии с рассматриваемыми в параграфе 4. Вот что выводит на консоль сервер:
Press [enter] to stop server...
[02.10.2005 16:33:04]: Add
KEY: __ConnectionId VALUE: 1
KEY: __IPAddress VALUE: 127.0.0.1
KEY: __RequestUri VALUE: /Calculator.soap
KEY: Content-Type VALUE: text/xml; charset="utf-8"
KEY: __RequestVerb VALUE: POST
KEY: __HttpVersion VALUE: HTTP/1.1
KEY: User-Agent VALUE: Mozilla/4.0+(compatible; MSIE 6.0;
Windows 5.1.2600.0; MS .NET Remoting;
MS .NET CLR 1.1.4322.573 )
KEY: SOAPAction VALUE:
"http://schemas.microsoft.com/clr/nsassem/BSUIR.Calculator/calc#Add"
KEY: Host VALUE: localhost:1234
KEY: __CustomErrorsEnabled VALUE: False
[02.10.2005 16:33:04]: Add - return 7
Рассмотрим другой пример. Пусть требуется производить шифрование потока, передаваемого от сервера к клиенту и наоборот. Для этого требуется реализовать канальные приемники, работающие с потоком, на стороне клиента и на стороне сервера. В нашем учебном примере «шифрование» будет выполняться как применение к каждому байту потока побитовой операции XOR с некой константой.
Начнем с реализации клиентского канального приемника. Рассмотрим интерфейс IClientChannelSink.
public interface IClientChannelSink {
IClientChannelSink NextChannelSink { get; }
void AsyncProcessRequest(IClientChannelSinkStack sinkStack,
IMessage msg,
ITransportHeaders headers,
Stream stream);
void AsyncProcessResponse(
IClientResponseChannelSinkStack sinkStack,
object state, ITransportHeaders headers,
Stream stream);
Stream GetRequestStream(IMessage msg,
ITransportHeaders headers);
void ProcessMessage(IMessage msg,
ITransportHeaders requestHeaders,
Stream requestStream,
out ITransportHeaders responseHeaders,
out Stream responseStream);
}
Основное отличие этого интерфейса от интерфейса IServerChannelSink заключается в наличие отдельных методов для осуществления асинхронного запроса и получения асинхронного ответа. Кроме этого, метод ProcessMessage() не работает со стеком объектов-приемников. В остальном (включая схему использования) интерфейс практически аналогичен IServerChannelSink.
Код клиентского канального приемника EncClientSink представлен ниже (для краткости опущены формальные параметры методов):
using System;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.IO;
using System.Collections;
namespace Encryption {
public class EncClientSink: BaseChannelSinkWithProperties,
IClientChannelSink {
private IClientChannelSink nextSink;
public EncClientSink(IClientChannelSink next) {
nextSink = next;
}
public IClientChannelSink NextChannelSink {
get { return nextSink; }
}
public void AsyncProcessRequest(. . .) {
stream =
EncriptionHelper.GetEncryptedStreamCopy(stream);
sinkStack.Push(this,null);
nextSink.AsyncProcessRequest(sinkStack,msg,headers,stream);
}
public void AsyncProcessResponse(. . .) {
stream =
EncriptionHelper.GetDecryptedStreamCopy(stream);
sinkStack.AsyncProcessResponse(headers,stream);
}
public Stream GetRequestStream(. . .) {
return nextSink.GetRequestStream(msg, headers);
}
public void ProcessMessage(. . .) {
requestStream =
EncriptionHelper.GetEncryptedStreamCopy(requestStream);
nextSink.ProcessMessage(msg, requestHeaders,
requestStream,
out responseHeaders,
out responseStream);
responseStream =
EncriptionHelper.GetDecryptedStreamCopy(responseStream);
}
}
}
Для осуществления шифрования и дешифровки клиентский канальный приемник (равно как и серверный) используют методы вспомогательного класса EncriptionHelper.
namespace Encryption {
public class EncriptionHelper {
public static Stream GetEncryptedStreamCopy(Stream inStream) {
Stream outStream = new MemoryStream();
byte[] buf = new byte[1000];
int cnt = inStream.Read(buf,0,1000);
while (cnt > 0) {
for (int i = 0; i < cnt; i++) { buf[i] ^= 123; }
outStream.Write(buf,0,cnt);
cnt = inStream.Read(buf,0,1000);
}
outStream.Position = 0;
return outStream;
}
public static Stream GetDecryptedStreamCopy(Stream inStream) {
// В нашем случае дешифровка и шифровка симметричны
return GetEncryptedStreamCopy(inStream);
}
}
}
Нам необходим клиентский канальный провайдер. Это должен быть класс, реализующий интерфейс IClientChannelSinkProvider:
public interface IClientChannelSinkProvider {
IClientChannelSinkProvider Next { get; set; }
IClientChannelSink CreateSink(IChannelSender channel,
string url,
object remoteChannelData);
}
Код класса для клиентского провайдера тривиален:
public class EncClientSinkProvider:
IClientChannelSinkProvider {
private IClientChannelSinkProvider nextProvider;
public EncClientSinkProvider(IDictionary properties,
ICollection providerData) { }
public IClientChannelSinkProvider Next {
get { return nextProvider; }
set { nextProvider = value; }
}
public IClientChannelSink CreateSink(IChannelSender channel,
string url,
object remoteChannelData)
{
IClientChannelSink next =
nextProvider.CreateSink(channel,url,remoteChannelData);
return new EncClientSink(next);
}
}
Далее представлен листинг серверного приемника канала и провайдера (размещенных в пространстве имен Encryption):
public class EncServerSink : BaseChannelSinkWithProperties,
IServerChannelSink {
private IServerChannelSink nextSink;
public EncServerSink(IServerChannelSink next) {
nextSink = next;
}
public IServerChannelSink NextChannelSink {
get { return nextSink; }
}
public void AsyncProcessResponse(. . .) {
stream =
EncriptionHelper.GetEncryptedStreamCopy(stream);
sinkStack.AsyncProcessResponse(msg,headers,stream);
}
public Stream GetResponseStream(. . .) { return null; }
public ServerProcessing ProcessMessage(. . .) {
requestStream =
EncriptionHelper.GetDecryptedStreamCopy(requestStream);
sinkStack.Push(this,null);
ServerProcessing srvProc =
nextSink.ProcessMessage(sinkStack,
requestMsg,
requestHeaders,
requestStream,
out responseMsg,
out responseHeaders,
out responseStream);
responseStream =
EncriptionHelper.GetEncryptedStreamCopy(responseStream);
return srvProc;
}
}
public class EncServerSinkProvider : IServerChannelSinkProvider {
private IServerChannelSinkProvider nextProvider;
public EncServerSinkProvider(IDictionary properties,
ICollection providerData) { }
public IServerChannelSinkProvider Next {
get { return nextProvider; }
set { nextProvider = value; }
}
public IServerChannelSink CreateSink(IChannelReceiver channel) {
IServerChannelSink next =
nextProvider.CreateSink(channel);
return new EncServerSink(next);
}
public void GetChannelData(IChannelDataStore channelData) { }
}
Выполним подключение разработанных канальных приемников при помощи конфигурационных файлов. Для сервера файл имеет следующий вид:
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http" port="1234">
<serverProviders>
<provider type =
"Encryption.EncServerSinkProvider, EncSink" />
<formatter ref="soap" />
<provider type="MySink.LogSinkProvider, MySink" />
</serverProviders>
</channel>
</channels>
<service>
<wellknown mode="SingleCall"
type="BSUIR.Calculator, Calc"
objectUri="Calculator.soap" />
</service>
</application>
</system.runtime.remoting>
</configuration>
Клиентский конфигурационный файл:
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http" port="0">
<clientProviders>
<formatter ref="soap" />
<provider type =
"Encryption.EncClientSinkProvider, EncSink"/>
</clientProviders>
</channel>
</channels>
<client>
<wellknown type="BSUIR.Calculator, Calc"
url="http://localhost:1234/Calculator.soap" />
</client>
</application>
</system.runtime.remoting>
</configuration>
Обратите внимание на порядок, в котором провайдеры размещены на клиенте и сервере.
Рассмотрим некоторые возможности расширения предыдущего примера. Представим ситуацию, согласно которой не все клиенты реализуют шифрование. Как на стороне сервера различать клиентов? Решение может быть следующим. Клиент с шифрованием внедряет в заголовок транспортного протокола некоторую служебную информацию, а сервер анализирует ее и либо выполняет дешифровку, либо нет.
Работа с заголовком транспортного протокола выполняется как с таблицей «ключ-значение». По соглашению имена пользовательских ключей в заголовке принято начинаться с "X". Вот как могут выглядеть измененные методы на клиенте:
public void AsyncProcessRequest(. . .) {
headers["X-Encrypted"] = "yes";
stream = EncriptionHelper.GetEncryptedStreamCopy(stream);
sinkStack.Push(this,null);
nextSink.AsyncProcessRequest(sinkStack,msg,headers,stream);
}
public void AsyncProcessResponse(. . .) {
string xencrypted = (string)headers["X-Encrypted"];
if(xencrypted != null && xencrypted == "yes") {
stream =
EncriptionHelper.GetDecryptedStreamCopy(stream);
}
sinkStack.AsyncProcessResponse(headers,stream);
}
public void ProcessMessage(. . .) {
requestStream =
EncriptionHelper.GetEncryptedStreamCopy(requestStream);
requestHeaders["X-Encrypted"] = "yes";
nextSink.ProcessMessage(. . .);
string xencrypted = (string)responseHeaders["X-Encrypted"];
if(xencrypted != null && xencrypted == "yes") {
responseStream =
EncriptionHelper.GetDecryptedStreamCopy(responseStream);
}
}
В коде серверного приемника метод ProcessMessage() должен поместить информацию о том, является ли поток зашифрованным в стек обработчиков, чтобы она могла быть извлечена при асинхронной обработке:
public ServerProcessing ProcessMessage(. . .) {
bool isEncrypted = false;
string xencrypted = (string)requestHeaders["X-Encrypted"];
if(xencrypted != null && xencrypted == "yes") {
requestStream =
EncriptionHelper.GetDecryptedStreamCopy(requestStream);
isEncrypted = true;
}
sinkStack.Push(this, isEncrypted);
ServerProcessing srvProc = nextSink.ProcessMessage(. . .);
if(srvProc == ServerProcessing.Complete ) {
if(isEncrypted) {
responseStream =
EncriptionHelper.GetEncryptedStreamCopy(responseStream);
responseHeaders["X-Encrypted"] = "yes";
}
}
return srvProc;
}
В методе AsyncProcessResponse() на сервере анализируется значение параметра state. Если этот параметр указывает на то, что принятое сообщение было зашифровано, ответ шифруется и в транспортный заголовок помещается необходимая информация:
public void AsyncProcessResponse(. . .) {
bool hasBeenEncrypted = (bool) state;
if(hasBeenEncrypted) {
stream = EncriptionHelper.GetEncryptedStreamCopy(stream);
headers["X-Encrypted"] = "yes";
}
sinkStack.AsyncProcessResponse(msg,headers,stream);
}
4. ADO.NET