Здравствуйте, toshik2002, Вы писали:
T>Собственно вопрос — как заставить сервер отвечать по тому же каналу, по которому он получил запрос?
Сталкивался с такой же проблемой. Кстати, она же проявляется, если один из сетевых интерфейсов отваливается — не работает даже loopback-подключение.
Решение, вкратце: нужно определять, откуда пришел запрос, и корректировать адрес server activated-объекта.
Если меня не запинают за велосипедность и не приведут более правильных вариантов, выдам более развернутый ответ.
Здравствуйте, HowardLovekraft, Вы писали:
T>>Собственно вопрос — как заставить сервер отвечать по тому же каналу, по которому он получил запрос?
HL>Сталкивался с такой же проблемой. Кстати, она же проявляется, если один из сетевых интерфейсов отваливается — не работает даже loopback-подключение.
HL>Решение, вкратце: нужно определять, откуда пришел запрос, и корректировать адрес server activated-объекта.
HL>Если меня не запинают за велосипедность и не приведут более правильных вариантов, выдам более развернутый ответ.
Вариант интересный, но не совсем понятна его реализация
Здравствуйте, toshik2002, Вы писали:
T>не совсем понятна его реализация
1. Пишете свою реализацию ITrackingHandler. Конкретней, вас интересует метод ITrackingHandler.MarshaledObject. В этом методе отлавливается факт маршаллинга объекта:
public void MarshaledObject(object obj, ObjRef or)
{
Object serverHostNameOrIp = CallContext.GetData("serverHostNameOrIp");
if (serverHostNameOrIp != null)
{
foreach (Object channelData in or.ChannelInfo.ChannelData)
{
if (channelData is ChannelDataStore)
{
ReplaceHostName((ChannelDataStore)channelData, serverHostNameOrIp.ToString());
}
}
}
}
В контекст вызова помещается (как — см. ниже) имя или адрес ремоутинг-сервера, видимый с клиента.
Т.е., для одного клиента — "127.0.0.1", для другого — "server" и т.п.
Когда объект маршаллится, достаете эту информацию из контекста.
В данных канала ищутся объекты типа ChannelDataStore. Их может быть несколько.
2. Реализация метода ReplaceHostName. Фактически, просто сравнение имен хостов:
private void ReplaceHostName(ChannelDataStore dataStore, String serverHostNameOrIp)
{
for (Int32 i = 0; i < dataStore.ChannelUris.Length; i++)
{
UriBuilder ub = new UriBuilder(dataStore.ChannelUris[i]);
if (String.Compare(ub.Host, serverHostNameOrIp, true) != 0)
{
ub.Host = serverHostNameOrIp;
dataStore.ChannelUris[i] = ub.ToString();
}
}
}
Если имя хоста в URI SAO не соответствует тому, которое клиент видит снаружи, меняете его на нужное значение.
3. Регистрируете свой tracking handler (где-то в коде инициализации вашего сервиса):
TrackingServices.RegisterTrackingHandler(myTrackingHandler);
4. Как поместить "правильное" имя или адрес хоста в контекст вызова. Создаете свои приемники сообщений, клиентский и серверный. Реализация приемников примитивна — задача клиентского положить имя хоста в заголовок запроса, серверного — достать имя хоста и поместить его в контекст вызова (выделено полужирным):
public class MyClientChannelSink : BaseChannelSinkWithProperties, IClientChannelSink
{
//...
private String _serverHostNameOrIp;
//...
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
public void ProcessMessage(IMessage msg, ITransportHeaders requestHeaders, Stream requestStream,
out ITransportHeaders responseHeaders, out Stream responseStream)
{
requestHeaders["serverHostNameOrIp"] = _serverHostNameOrIp;
_nextSink.ProcessMessage(msg, requestHeaders, requestStream,
out responseHeaders, out responseStream);
}
//...
}
public class MyServerChannelSink : BaseChannelSinkWithProperties, IServerChannelSink
{
//...
private Object _serverHostNameOrIp;
//...
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg,
ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg,
out ITransportHeaders responseHeaders, out Stream responseStream)
{
_serverHostNameOrIp = requestHeaders["serverHostNameOrIp"];
if (_serverHostNameOrIp != null)
CallContext.SetData("serverHostNameOrIp", _serverHostNameOrIp);
else
CallContext.FreeNamedDataSlot("serverHostNameOrIp");
sinkStack.Push(this, null);
ServerProcessing status = _nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders,
requestStream, out responseMsg, out responseHeaders, out responseStream);
return status;
}
//...
}
5. Для того, чтобы пользоваться приемниками, нужна соответствующая пара провайдеров (реализуете IClientChannelSinkProvider и IServerChannelSinkProvider). В конструктор клиентского провайдера передаете "правильное" имя или адрес хоста. В свою очередь, клиентский провайдер, создавая приемник, будет передавать это значение собственно приемнику.
6. Использование.
На клиентской стороне создается экземпляр вашего клиентского провайдера и помещается в цепь после провайдера formatter-приемников:
MyClientChannelSinkProvider customSinkProvider =
new MyClientChannelSinkProvider("192.168.0.1");
IClientFormatterSinkProvider formatterSinkProvider = // например, BinaryClientFormatterSinkProvider
formatterSinkProvider.Next = customSinkProvider;
Далее создается и регистрируется клиентский канал. Ему передается formatterSinkProvider.
На серверной стороне выполняется аналогичная работа, с учетом того, что теперь провайдер formatter-приемников будет в цепи после вашего провайдера:
IServerFormatterSinkProvider formatterSinkProvider = // например, BinaryServerFormatterSinkProvider
MyServerChannelSinkProvider customSinkProvider = new MyServerChannelSinkProvider();
customSinkProvider.Next = formatterSinkProvider;
Далее создается и регистрируется серверный канал, которому передается customSinkProvider.
Как-то так.

Пример, скорее всего, не универсален. Но общий принцип такой.