Remoting и 2 канала
От: toshik2002  
Дата: 20.04.09 14:11
Оценка:
Приветствую!

Есть приложение (сервер), оно слушает через .NET Remoting по двум интерфейсам (скажем, для примера 192.168.0.1 и 195.40.50.60)
Если клиент подключается по 192.168.0.1 — то все работает нормально
Если же клиент подключается на 195.40.50.60, то сервер ему отвечает с указанием обратного канала на 192.168.0.1, по которому клиент впоследствии и пытается работать.

Т.е. выглядит это примерно так:
0. Имеем клиент в сети 195.40.50.* (соответственно про 192.168.0.1 он ничего не знает)
1. Клиент пытается соединиться по адресу 195.40.50.60
2. На сервере видим приход/расход трафика
3. Клиент выдает эксепшн вида: Невозможно установить соединение с 192.168.0.1

Собственно вопрос — как заставить сервер отвечать по тому же каналу, по которому он получил запрос?
remoting каналы адрес
Re: Remoting и 2 канала
От: HowardLovekraft  
Дата: 20.04.09 14:32
Оценка:
Здравствуйте, toshik2002, Вы писали:

T>Собственно вопрос — как заставить сервер отвечать по тому же каналу, по которому он получил запрос?


Сталкивался с такой же проблемой. Кстати, она же проявляется, если один из сетевых интерфейсов отваливается — не работает даже loopback-подключение.

Решение, вкратце: нужно определять, откуда пришел запрос, и корректировать адрес server activated-объекта.
Если меня не запинают за велосипедность и не приведут более правильных вариантов, выдам более развернутый ответ.
Re[2]: Remoting и 2 канала
От: samius Япония http://sams-tricks.blogspot.com
Дата: 20.04.09 14:37
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Решение, вкратце: нужно определять, откуда пришел запрос, и корректировать адрес server activated-объекта.

Или просто избегать server-activated объектов
Re[3]: Remoting и 2 канала
От: HowardLovekraft  
Дата: 20.04.09 18:21
Оценка:
Здравствуйте, samius, Вы писали:

S>Или просто избегать server-activated объектов

Тоже вариант. Но он далеко не всегда возможен/удобен.
Re[2]: Remoting и 2 канала
От: toshik2002  
Дата: 21.04.09 08:05
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

T>>Собственно вопрос — как заставить сервер отвечать по тому же каналу, по которому он получил запрос?


HL>Сталкивался с такой же проблемой. Кстати, она же проявляется, если один из сетевых интерфейсов отваливается — не работает даже loopback-подключение.


HL>Решение, вкратце: нужно определять, откуда пришел запрос, и корректировать адрес server activated-объекта.

HL>Если меня не запинают за велосипедность и не приведут более правильных вариантов, выдам более развернутый ответ.

Вариант интересный, но не совсем понятна его реализация
Re[3]: Remoting и 2 канала
От: HowardLovekraft  
Дата: 21.04.09 12:32
Оценка: 99 (2)
Здравствуйте, 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.

Как-то так.
Пример, скорее всего, не универсален. Но общий принцип такой.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.