Reputation: 1118
Im trying to build a webservice for clients on a local network. For the service i can target any version of the .NET Framework. The clients are mobile windows devices and i would like to use the universal windows platform (UWP) as the target framework.
The service will run on multiple machines with different network addresses. My goal is that a client can automatically detect the service as soon as he connects to that local network. I want to avoid any typing of ip-addresses by the user. But all samples i can find use a hard-coded service URL. Since i have no DNS-Server i have to enter (or hard-code) the service-ip-address into the clients.
Currently im running a WCF service with a UDPDiscoveryEndpoint which does exactly what i want. But unfortunately that part of WCF (the System.ServiceModel.Discovery namespace) is not available on WinRT and also not supported on the universal windows platform. I dont have to use WCF; any alternative library with service discovery functionality would be perfect.
So here is my question: Is there any way to do service discovery on a local network in a WinRT/ UWP App? I tried ASP.NET Web API and SignalR but it seems that this HTTP based services/frameworks dont support discovery at all.
Thank you!
Upvotes: 2
Views: 1651
Reputation: 113
In UWP, you can use the PeerFinder class, to discover other instances of your applications in a LAN.
I know this is not exactly service discovery, it's just peer discovery, but it should be enough for your scenario. Just use one instance of the application as your "service" that communicates with the other instances.
You can use this to find your peers and create a socket connection:
PeerFinder.DisplayName = "Doru " + Guid.NewGuid().ToString();
PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;
PeerFinder.Start();
private async void PeerFinder_ConnectionRequested(object sender, ConnectionRequestedEventArgs args)
{
PeerInformation peer = args.PeerInformation;
StreamSocket socket = await PeerFinder.ConnectAsync(peer);
}
For a deeper understanding on how Peer Discovery works, checkout this link [minute 6:30].
Upvotes: 0
Reputation: 352
I've managed to do webservice discovery in UWP using socket and broadcast messages.
Plese, check my answer for more details.
EDIT - As sugested by @naveen-vijay, I'm posting a more complete aswer instead of just a link to a solution.
Every WS will listen to a specific port, awaiting for some broadcasted message searching for WS running in the LAN. The WS implementation is win32, and this is the code needed:
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);
}
The client implementation is UWP. I've created the following class to do the search:
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 });
}
}
}
}
}
Upvotes: 1