Страницы

Поиск по вопросам

среда, 4 марта 2020 г.

При использовании NAT запросы к WCF проходят не корректно

#c_sharp #wcf #nat


Есть служба WCF, она хостится на порту 8888.

При использовании NAT 
outside 8888, inside 8888 - проходит отлично.
outside 2145, inside 8888 - проходит с ошибкой.
outside 5416, inside 8888 - проходит с ошибкой.

Т.е., если использовать одинаковые порты на входе и выходе запросы проходят.
Если они разные, тогда нет.

Служба 

Байндинг службы

  private static WSHttpBinding Setbinding()
    {
        WSHttpBinding binding = new WSHttpBinding();
        binding.Security.Mode = SecurityMode.None;
        return binding;
    }


Клиент

Код запроса 

  int PortNumber =2145;//или 5416 или 8888
  client = new PServiceClient(GetBinding(), 
  new EndpointAddress(string.Format("http://{0}:{1}/PService/", "168.211.65.22",
PortNumber)));
  ...
  //Проверка коннекта
  bool res = client.Connect();


Байндинг клиента

private WSHttpBinding GetBinding()
        {
            WSHttpBinding binding = new WSHttpBinding();
            binding.Security.Mode = SecurityMode.None;
            timeout = TimeOut.GetTimeOut();
            return binding;
        }


Почему у меня не работают корректно запросы к службе при использовании NAT?
    


Ответы

Ответ 1



Вы используете WSHttpBinding - а эта привязка подразумевает использование стандарта WS‑Addressing, который в свою очередь передает адрес принимающей стороны в заголовке To. Честно говоря, я не знаю зачем вообще в веб-сервисах, где отправитель и получатель сообщения всегда известны, использовать WS‑Addressing. Выглядит как глупость, и именно из-за нее SOAP считают довольно "тяжелым" протоколом. Поэтому самый простой способ - отказаться от WSHttpBinding и перейти на BasicHttpBinding, там такой проблемы нет. Если же вам требуется именно WSHttpBinding - то есть три пути. Самый правильный с точки зрения стандартов - рассказать серверу какой у него реальный адрес: var binding = new WSHttpBinding(); var internalUri = new Uri("http://192.168.1.10:8888/PService/"); var externalUri = new Uri("http://168.211.65.22:2145/PService/"); ServiceHost host = new ServiceHost(foo); host.AddServiceEndpoint(typeof(IFoo), binding, internalUri); host.AddServiceEndpoint(typeof(IFoo), binding, address: externalUri, listenUri: internalUri); Я добавил две конечные точки чтобы сервер мог принимать как прямые запросы так и запросы через NAT. Но хардкодить адреса - не лучшая идея, все же такие вещи лучше выносить в код: Тут главное - не перепутайте, address - это то что сервер будет ждать в заголовке To, сюда попадает тот адрес который видит клиент; listenUri же - этот тот адрес который реально слушает сервер. Альтернативный вариант - обучить клиент передавать правильные заголовки. Этот вариант не очень хороший, поскольку раскрывает перед клиентом внутреннюю структуру сети сервера. Но для полноты картины я его тоже приведу. var binding = new WSHttpBinding(); var internalUri = new Uri("http://192.168.1.10:8888/PService/"); var externalUri = new Uri("http://168.211.65.22:2145/PService/"); var client = new FooClient(binding, new EndpointAddress(internalUri)); client.Endpoint.EndpointBehaviors.Add(new ClientViaBehavior(externalUri)); Как я уже писал, адреса предпочтительнее задавать в конфиге. Для клиента это делается так: Как видно, уже чуть сложнее чем для сервера но все еще ничего страшного. Также допустим смешанный вариант. Дело в том, что от адреса конечной точки совершенно не требуется чтобы он был настоящим http-адресом! Все реальные http-адреса указываются в атрибутах listenUri и viaUri, к адресу же главное требование - совпадение у клиента и у сервера. А значит, туда можно написать любой uri, например какой-нибудь urn:local:my-cool-web-service (такой адрес обычно называют логическим адресом сервиса). Вот пример конфигов для сервера и клиента с настроенным логическим адресом: Достоинство этого варианта - ни клиент ни сервер ничего не знают о деталях трансляции адресов. Недостаток же этого варианта заключается в том, что если вы покажите такой конфиг стороннему разработчику, он может, э-э-э... слегка удивиться такому решению. Наконец, самый последний вариант. Самый простой, но костыль. Можно вовсе отключить проверку адреса на стороне сервера: [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)] class Foo : IFoo { }

Комментариев нет:

Отправить комментарий