UWP应用程序中的WCF发现

时间:2016-05-17 19:55:22

标签: c# wcf uwp ws-discovery

我已经创建了一个通用应用程序,它连接到Intranet上的WCF Web服务,它工作得很好,因为服务主机的地址是已知的。

由于性能和安全性(冗余)原因,系统的体系结构允许在不同主机中运行多个Web服务。所以我试图让我的应用程序发现每个服务,使用给定的合同,这是在同一个局域网上运行,但我无法做到这一点。

我正在尝试使用与非常相似的win32应用程序相同的方法:

var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
var findCriteria = new FindCriteria(typeof(INewProdColetorWCFService));
findCriteria.Duration = TimeSpan.FromSeconds(5);
var findResponse = await discoveryClient.FindTaskAsync(findCriteria);

Visual Studio“自动”为我添加所需的引用(System.ServiceModel.Discovery)as seen here

在设计时似乎没问题,但是当我尝试编译时,会出现错误:

  

在模块System.ServiceModel.dll中找不到类型System.ServiceModel.Configuration.ServiceModelConfigurationElementCollection`1。

你们有没有在UWP中做到这一点?你能帮助我吗? 提前谢谢,iuri。

ps:我也发布了this question in MSDN

3 个答案:

答案 0 :(得分:0)

UWP目前不支持WS-Discovery API。详情请见https://msdn.microsoft.com/en-us/library/windows/apps/mt185502.aspx。 文档中没有针对UWP应用程序的System.ServiceModel.Discovery API支持。但是你可以在win32应用程序中使用它。 如果您需要此功能,可以将您的想法提交到UserVoice网站:https://wpdev.uservoice.com/forums/110705-universal-windows-platform

答案 1 :(得分:0)

我不知道我是否应该回答我自己的问题,但我认为对于任何想要做同样事情的人来说这可能是有用的,所以就在这里。

由于WS-Discovery API在UWP中不可用,我必须以另一种方式进行。使用套接字是我能找到的最佳选择。所以每个WS都会监听一个特定的端口,等待一些广播的消息搜索在局域网中运行的WS。

WS实现是win32,这是所需的代码:

private byte[] dataStream = new byte[1024];
private Socket serverSocket;
private void InitializeSocketServer(string id)
{
    // Sets the server ID
    this._id = id;
    // Initialise the socket
    serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    // Initialise the IPEndPoint for the server and listen on port 30000
    IPEndPoint server = new IPEndPoint(IPAddress.Any, 30000);
    // Associate the socket with this IP address and port
    serverSocket.Bind(server);
    // Initialise the IPEndPoint for the clients
    IPEndPoint clients = new IPEndPoint(IPAddress.Any, 0);
    // Initialise the EndPoint for the clients
    EndPoint epSender = (EndPoint)clients;
    // Start listening for incoming data
    serverSocket.BeginReceiveFrom(this.dataStream, 0, this.dataStream.Length, SocketFlags.None, ref epSender, new AsyncCallback(ReceiveData), epSender);
}

private void ReceiveData(IAsyncResult asyncResult)
{
    // Initialise the IPEndPoint for the clients
    IPEndPoint clients = new IPEndPoint(IPAddress.Any, 0);
    // Initialise the EndPoint for the clients
    EndPoint epSender = (EndPoint)clients;
    // Receive all data. Sets epSender to the address of the caller
    serverSocket.EndReceiveFrom(asyncResult, ref epSender);
    // Get the message received
    string message = Encoding.UTF8.GetString(dataStream);
    // Check if it is a search ws message
    if (message.StartsWith("SEARCHWS", StringComparison.CurrentCultureIgnoreCase))
    {
        // Create a response messagem indicating the server ID and it's URL
        byte[] data = Encoding.UTF8.GetBytes($"WSRESPONSE;{this._id};http://{GetIPAddress()}:5055/wsserver");
        // Send the response message to the client who was searching
        serverSocket.BeginSendTo(data, 0, data.Length, SocketFlags.None, epSender, new AsyncCallback(this.SendData), epSender);
    }
    // Listen for more connections again...
    serverSocket.BeginReceiveFrom(this.dataStream, 0, this.dataStream.Length, SocketFlags.None, ref epSender, new AsyncCallback(this.ReceiveData), epSender);
}

private void SendData(IAsyncResult asyncResult)
{
    serverSocket.EndSend(asyncResult);
}

客户端实现是UWP。我创建了以下类来进行搜索:

public class WSDiscoveryClient
{
    public class WSEndpoint
    {
        public string ID;
        public string URL;
    }

    private List<WSEndpoint> _endPoints;
    private int port = 30000;
    private int timeOut = 5; // seconds

    /// <summary>
    /// Get available Webservices
    /// </summary>
    public async Task<List<WSEndpoint>> GetAvailableWSEndpoints()
    {
        _endPoints = new List<WSEndpoint>();

        using (var socket = new DatagramSocket())
        {
            // Set the callback for servers' responses
            socket.MessageReceived += SocketOnMessageReceived;
            // Start listening for servers' responses
            await socket.BindServiceNameAsync(port.ToString());

            // Send a search message
            await SendMessage(socket);
            // Waits the timeout in order to receive all the servers' responses
            await Task.Delay(TimeSpan.FromSeconds(timeOut));
        }
        return _endPoints;
    }

    /// <summary>
    /// Sends a broadcast message searching for available Webservices
    /// </summary>
    private async Task SendMessage(DatagramSocket socket)
    {
        using (var stream = await socket.GetOutputStreamAsync(new HostName("255.255.255.255"), port.ToString()))
        {
            using (var writer = new DataWriter(stream))
            {
                var data = Encoding.UTF8.GetBytes("SEARCHWS");
                writer.WriteBytes(data);
                await writer.StoreAsync();
            }
        }
    }

    private async void SocketOnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
    {
        // Creates a reader for the incoming message
        var resultStream = args.GetDataStream().AsStreamForRead(1024);
        using (var reader = new StreamReader(resultStream))
        {
            // Get the message received
            string message = await reader.ReadToEndAsync();
            // Cheks if the message is a response from a server
            if (message.StartsWith("WSRESPONSE", StringComparison.CurrentCultureIgnoreCase))
            {
                // Spected format: WSRESPONSE;<ID>;<HTTP ADDRESS>
                var splitedMessage = message.Split(';');
                if (splitedMessage.Length == 3)
                {
                    var id = splitedMessage[1];
                    var url = splitedMessage[2];
                    _endPoints.Add(new WSEndpoint() { ID = id, URL = url });
                }
            }
        }
    }
}

如果您发现错误,请随时发表评论,请告诉我它是否对您有所帮助。

答案 2 :(得分:0)

我自己做了一些研究时遇到过这个问题。在阅读https://en.wikipedia.org/wiki/WS-Discovery并使用Wireshark解读一些细节后,我得到了一个支持Microsoft的WS-Discovery规范的基本概念证明,这意味着服务器端不需要进行任何更改。

我现在已经离开了这个项目,但希望有人可以从中获得一些用处:

public class WSDiscoveryResponse
{
    private readonly string
        _content,
        _remotePort;

    private readonly HostName
        _remoteAddress;

    public WSDiscoveryResponse(string content, HostName remoteAddress, string remotePort)
    {
        this._content = content;
        this._remoteAddress = remoteAddress;
        this._remotePort = remotePort;
    }
}

public class WSDiscoveryClient
{
    private const string
        SRC_PORT = "0",//represents 'use any port available'
        DEST_IP_WSDISCOVERY = "239.255.255.250", //broadcast (ish)
        DEST_PORT_WSDISCOVERY = "3702";

    private TimeSpan _timeout = TimeSpan.FromSeconds(5);

    private List<WSDiscoveryResponse> _wsresponses = null;

    /// <summary>
    /// Get available Webservices
    /// </summary>
    public async Task<List<WSDiscoveryResponse>> GetAvailableWSEndpoints()
    {
        _wsresponses = new List<WSDiscoveryResponse>();
        using (var socket = new DatagramSocket())
        {
            try
            {
                socket.MessageReceived += SocketOnMessageReceived;
                //listen for responses to future message
                await socket.BindServiceNameAsync(SRC_PORT);
                //broadcast interrogation
                await SendDiscoveryMessage(socket);
                //wait for broadcast responses
                await Task.Delay(_timeout).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                SocketErrorStatus webErrorStatus = SocketError.GetStatus(ex.GetBaseException().HResult);
            }
        }
        return _wsresponses;
    }

    private string BuildDiscoveryMessage()
    {
        const string outgoingMessageFormat = @"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"" xmlns:a=""http://www.w3.org/2005/08/addressing""><s:Header><a:Action s:mustUnderstand=""1"">http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/Probe</a:Action><a:MessageID>urn:uuid:{0}</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand=""1"">urn:docs-oasis-open-org:ws-dd:ns:discovery:2009:01</a:To></s:Header><s:Body><Probe xmlns=""http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01""><d:Types xmlns:d=""http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01"" xmlns:dp0=""http://tempuri.org/"">dp0:IDiscoveryService</d:Types><Duration xmlns=""http://schemas.microsoft.com/ws/2008/06/discovery"">PT5S</Duration></Probe></s:Body></s:Envelope>";
        string outgoingMessage = string.Format(outgoingMessageFormat, Guid.NewGuid().ToString());
        return outgoingMessage;
    }

    private async Task SendDiscoveryMessage(DatagramSocket socket)
    {
        using (var stream = await socket.GetOutputStreamAsync(new HostName(DEST_IP_WSDISCOVERY), DEST_PORT_WSDISCOVERY))
        {
            string message = BuildDiscoveryMessage();
            var data = Encoding.UTF8.GetBytes(message);
            using (var writer = new DataWriter(stream))
            {
                writer.WriteBytes(data);
                await writer.StoreAsync();
            }
        }
    }

    private void SocketOnMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
    {
        var dr = args.GetDataReader();
        string message = dr.ReadString(dr.UnconsumedBufferLength);

        _wsresponses.Add(new WSDiscoveryResponse(message, args.RemoteAddress, args.RemotePort));
    }
}