C# UWP应用程序中的WCF发现
我已经创建了一个通用的应用程序,它可以连接到内部网的WCF Web服务,而且工作正常,因为服务的主机地址是已知的 出于性能和安全(冗余)原因,系统的体系结构允许多个Web服务在不同的主机上运行。因此,我试图让我的应用程序在给定的合同下发现在同一局域网上运行的所有服务,但我无法做到这一点 我正在尝试与win32应用程序非常相似的方法:C# UWP应用程序中的WCF发现,c#,wcf,uwp,ws-discovery,C#,Wcf,Uwp,Ws Discovery,我已经创建了一个通用的应用程序,它可以连接到内部网的WCF Web服务,而且工作正常,因为服务的主机地址是已知的 出于性能和安全(冗余)原因,系统的体系结构允许多个Web服务在不同的主机上运行。因此,我试图让我的应用程序在给定的合同下发现在同一局域网上运行的所有服务,但我无法做到这一点 我正在尝试与win32应用程序非常相似的方法: var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint()); var findCri
var discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
var findCriteria = new FindCriteria(typeof(INewProdColetorWCFService));
findCriteria.Duration = TimeSpan.FromSeconds(5);
var findResponse = await discoveryClient.FindTaskAsync(findCriteria);
VisualStudio“自动”为我添加所需的引用(System.ServiceModel.Discovery)
在设计时似乎还可以,但当我尝试编译时,会出现以下错误:
在模块System.ServiceModel.dll中找不到类型System.ServiceModel.Configuration.ServiceModelConfigurationElementCollection`1
你们中有人在UWP做过吗?你能帮助我吗?
提前谢谢你,尤里
ps:我也发布了UWP目前不支持WS-Discovery API。详情请参阅。 文档中没有针对UWP应用程序的System.ServiceModel.Discovery API支持。但您可以在win32应用程序中使用它。
如果您需要此功能,您可以将您的想法提交到UserVoice站点:我不知道是否应该回答我自己的问题,但我认为它可能对任何尝试这样做的人都有用,所以就这样吧 因为WS-DiscoveryAPI在UWP中不可用,所以我不得不用另一种方式来实现它。使用套接字是我能找到的最好的选择。因此,每个WS将侦听特定端口,等待广播消息搜索LAN中运行的WS WS-implementation是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 });
}
}
}
}
}
公共类WSDiscoveryClient
{
公共类WSEndpoint
{
公共字符串ID;
公共字符串URL;
}
私有列表_端点;
专用int端口=30000;
private int timeOut=5;//秒
///
///获取可用的Web服务
///
公共异步任务GetAvailableNewsEndpoints()
{
_端点=新列表();
使用(var socket=new DatagramSocket())
{
//设置服务器响应的回调
socket.MessageReceived+=SocketOnMessageReceived;
//开始侦听服务器的响应
等待socket.BindServiceNameAsync(port.ToString());
//发送搜索消息
等待发送消息(套接字);
//等待超时以接收所有服务器的响应
等待任务延迟(TimeSpan.FromSeconds(超时));
}
返回_端点;
}
///
///发送广播消息,搜索可用的Web服务
///
专用异步任务SendMessage(DatagramSocket套接字)
{
使用(var stream=await socket.GetOutputStreamAsync(新主机名(“255.255.255.255”)、port.ToString())
{
使用(var writer=newdatawriter(流))
{
var data=Encoding.UTF8.GetBytes(“SEARCHWS”);
writer.WriteBytes(数据);
等待writer.StoreAsync();
}
}
}
私有异步void SocketOnMessageReceived(DatagramSocket发送方、DatagramSocketMessageReceivedEventArgs)
{
//为传入消息创建读取器
var resultStream=args.GetDataStream().AsStreamForRead(1024);
使用(var reader=newstreamreader(resultStream))
{
//收到消息
string message=wait reader.ReadToEndAsync();
//检查消息是否是来自服务器的响应
if(message.StartsWith(“WSRESPONSE”,StringComparison.CurrentCultureIgnoreCase))
{
//Spected格式:WSRESPONSE;;
var splitedMessage=message.Split(“;”);
if(splitedMessage.Length==3)
{
var id=splitedMessage[1];
var url=splitedMessage[2];
_Add(新的WSEndpoint(){ID=ID,URL=URL});
}
}
}
}
}
如果您发现问题,请随时发表评论,并请告诉我这是否对您有所帮助。我自己在做一些研究时遇到了这个问题。在阅读并使用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));
}
}
公共类WSDiscoveryResponse
{
私有只读字符串
_内容,,
_远程端口;
专用只读主机名
_远程地址;
公共WSDiscoveryResponse(字符串内容、主机名远程地址、字符串远程端口)
{
这._content=内容;
这是。_remoteAddress=remoteAddress;
这个。_remotePort=remotePort;
}
}
公共类WSDiscoveryClient
{
私有常量字符串
SRC_PORT=“0”,//表示“使用任何可用端口”
DEST_IP_WSDISCOVERY=“239.255.255.250”,//广播(ish)
DEST_PORT_WSDISCOVERY=“3702”;
专用时间间隔_timeout=TimeSpan.FromSeconds(5);
私有列表_wsresponses=null;
///
///获取可用的Web服务
///
公共异步任务GetAvailableNewsEndpoints()
{
_wsresponses=新列表();
使用(var socket=new DatagramSocket())
{
尝试
{
socket.MessageReceived+=SocketOnMessageReceived;
//倾听对未来消息的回应
等待socket.BindServiceNameAsync(SRC_端口);
//广播讯问
等待发送发现消息(套接字);
//等待br