Ниже приведён код, на примере которого будет проводиться демонстрация.
Примечания к коду:
- Для прямого вызова будем выводить SOAP сообщение метода SayHello() контракта IMyService.
- Для Callback вызова будем выводить SOAP сообщение метода Wellcome() контракта IMyCallback.
- Используется привязка WSDualHttpBinding работающая с HTTP транспортом и передающая SOAP сообщения в текстовом виде с настройкой Security=None. Если у вас net.tcp привязка, тогда вам подойдёт только способ 1 и 2.
using System; using System.ServiceModel; using System.ServiceModel.Channels; namespace ConsoleApp { class Program { /// <summary> /// Client callback instance. /// </summary> class MyCallback : IMyCallback { public void Wellcome(string name) { Console.WriteLine(name); } } private const string MyAddress = "http://localhost:9202"; private static readonly Binding MyBinding = new WSDualHttpBinding() { Security = { Mode = WSDualHttpSecurityMode.None} }; static void Main(string[] args) { // Start service StartService(); // Get service client var serviceClient = GetServiceClient(); // Use service serviceClient.SayHello("Sergey Vaulin", 31); Console.ReadKey(); } private static IMyService GetServiceClient() { return DuplexChannelFactory<IMyService>.CreateChannel( new InstanceContext(new MyCallback()), MyBinding, new EndpointAddress(MyAddress)); } private static void StartService() { var host = new ServiceHost(typeof(MyService)); host.AddServiceEndpoint(typeof(IMyService), MyBinding, MyAddress); host.Open(); } } /// <summary> /// 1. My service contract. /// </summary> [ServiceContract(CallbackContract = typeof(IMyCallback))] interface IMyService { [OperationContract] void SayHello(string name, int age); } /// <summary> /// 2. My service callback contract. /// </summary> [ServiceContract] interface IMyCallback { [OperationContract] void Wellcome(string name); } /// <summary> /// 3. My service type. /// </summary> [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)] class MyService : IMyService { public void SayHello(string name, int age) { var message = OperationContext.Current.RequestContext.RequestMessage.ToString(); var callback = OperationContext.Current.GetCallbackChannel<IMyCallback>(); callback.Wellcome(string.Format("You are welcome {0} ({1})", name, age)); } } }
Способ 1. Использование <system.diagnostics> логирование.
Для данного способа достаточно в файле конфигурации добавить секцию настроек.
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.diagnostics> <sources> <source name="System.ServiceModel.MessageLogging"> <listeners> <add name="messages" type="System.Diagnostics.XmlWriterTraceListener" initializeData="WCFLog.svclog" /> </listeners> </source> </sources> </system.diagnostics> <system.serviceModel> <diagnostics> <!-- https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/wcf/messagelogging --> <messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="false" maxMessagesToLog="100" maxSizeOfMessageToLog="5000"/> </diagnostics> </system.serviceModel> </configuration>
После чего можно запустить приложение и рядом с исполняемым файлом появится файл WCFLog.svclog, который можно отрыть утилитой SvcTraceViewer.exe, устанавливаемая с Windows SDK.
Открыв файл программой в списке справа у вас будут отображаться все вызовы сервиса, а так же вкладка Messages у каждого вызова, в которой в теге <Envelope> содержится тело сообщения.
Открыв файл программой в списке справа у вас будут отображаться все вызовы сервиса, а так же вкладка Messages у каждого вызова, в которой в теге <Envelope> содержится тело сообщения.
Внимание! Что вне тега Envelope не является SOAP сообщением.
Комментарии к разбору сообщения:
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope"> <s:Header> <a:Action s:mustUnderstand="1">http://tempuri.org/IMyService/SayHello</a:Action> <a:MessageID>urn:uuid:e3ff2900-02b5-49dd-90bd-1fb76e37d74d</a:MessageID> <a:ReplyTo> <a:Address>http://sap/Temporary_Listen_Addresses/d3703051-d88f-4ffe-b3f3-f0493585664d/2cc9adc0-8283-4c9a-827d-157df8aae4e8</a:Address> </a:ReplyTo> </s:Header> <s:Body> <!-- Вызов метода SayHello --> <SayHello xmlns="http://tempuri.org/"> <!-- Значение параметра name --> <name>Sergey Vaulin</name> <!-- Значение параметра age --> <age>31</age> </SayHello> </s:Body> </s:Envelope>
Все методы в моём примере работают в режиме Request\Responce, а значит после вызова я получаю ответ в виде <SayHelloResponce />
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope"> <s:Header> <a:Action s:mustUnderstand="1">http://tempuri.org/IMyService/SayHelloResponse</a:Action> </s:Header> <s:Body> <SayHelloResponse xmlns="http://tempuri.org/"></SayHelloResponse> </s:Body> </s:Envelope>
Данное сообщение получено не будет если у метода контракта стоит значение IsOneWay=true.
Способ 2. Получение сообщения используя RequestContext.
В теле метода SayHello(), на серверной стороне, необходимо обратиться к свойству:
var message = OperationContext.Current.RequestContext.RequestMessage.ToString();
В котором лежит принятое сообщение:
Способ 3. Использование Traffic Sniffer.
Является один из самых простых способов, но требует умение пользоваться хотя бы одним из снифферов. Лично я использую два самых популярных для Windows:
- WireShark - очень навороченный сниффер, который мониторит весь входящий и исходящий трафик.
- Fiddler - удобный HTTP сниффер, но требующий настройки.
Какой именно Sniffer использовать решать вам, но надо иметь ввиду следущее:
Не все sniffer умеют работать с loopback. Когда общение клиента и сервера происходит на одной машине. В этом случае система игнорирует канальный уровень и маршрутизирует запросы минуя сетевой уровень.
Примеры программ выше умею это делать, продемонстрирую все на примере WireShark версия 3.0.3 в котором есть Npcap Loopback Adapter.
Запускаем и выбираем Loopback адаптер:
Поскольку в моём примере используется порт 9202 я добавлю фильтр по нему, что бы не видеть мусорный трафик.
Фильтр:
http && (tcp.port == 9202 || tcp.dstport == 9202)
Фильтр:
http && (tcp.port == 9202 || tcp.dstport == 9202)
Запускаем и видим наш запрос:
После чего можно сделать Follow - TCP Stream.
И увидеть все запросы в хронологическом порядке:
Комментариев нет:
Отправить комментарий