1

我创建了一个通用应用程序,它连接到 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),如此处所示

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

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

你们有人在 UWP 中做过吗?你能帮助我吗?在此先感谢,iuri。

ps:也在 MSDN中发布过这个问题

4

3 回答 3

1

我自己做一些研究时遇到了这个线程。在阅读了https://en.wikipedia.org/wiki/WS-Discovery并使用 Wireshark 破译了一些细节之后,我得到了一个基本的概念证明,它支持微软的 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));
    }
}
于 2018-01-31T11:44:11.157 回答
0

我不知道我是否应该回答我自己的问题,但我认为它可能对任何尝试这样做的人有用,所以就这样吧。

由于 WS-Discovery API 在 UWP 中不可用,我不得不以另一种方式进行。使用套接字是我能找到的最佳选择。所以每个 WS 都会监听一个特定的端口,等待一些广播消息搜索在 LAN 中运行的 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 });
                }
            }
        }
    }
}

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

于 2016-05-19T14:28:52.873 回答
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

于 2016-05-18T09:51:53.820 回答