Windows UWP Web Service Discovery on LAN - c#

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!

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 });
}
}
}
}
}

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].

Related

c# mvc 4 fire up a udp listener

I'm using c# MVC 4 as server, meaning I have no Views in it and it currently accept Http/s requests to the controllers, process them, query the db and returns a json object to the user.
I would like to enhance my server and add a udp (or tcp, i haven't completely decided yet) listener.
Problem is I haven't seen someone does it, everyone use either Console Application or WCF-like because the listener can be triggered by some sort of action.
I have a standard code from CodeProject
// Create UDP client
UdpClient client = new UdpClient(localPort);
UdpState state = new UdpState(client, remoteSender);
// Start async receiving
client.BeginReceive(new AsyncCallback(DataReceived), state);
private static void DataReceived(IAsyncResult ar)
{
UdpClient c = (UdpClient)((UdpState)ar.AsyncState).c;
IPEndPoint wantedIpEndPoint = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
IPEndPoint receivedIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
Byte[] receiveBytes = c.EndReceive(ar, ref receivedIpEndPoint);
// Check sender
bool isRightHost = (wantedIpEndPoint.Address.Equals(receivedIpEndPoint.Address)
|| wantedIpEndPoint.Address.Equals(IPAddress.Any);
bool isRightPort = (wantedIpEndPoint.Port == receivedIpEndPoint.Port)
|| wantedIpEndPoint.Port == 0;
if (isRightHost && isRightPort)
{
// Convert data to ASCII and print in console
string receivedText = ASCIIEncoding.ASCII.GetString(receiveBytes);
Console.Write(receivedText);
}
// Restart listening for udp data packages
c.BeginReceive(new AsyncCallback(DataReceived), ar.AsyncState);
}
My issue is, How can I set the listener to start working when I run the server?
Framework : .Net framework 4.5
Output type : Class Library

How to get IP addresses of hosts on local network running my program

I have built a peer to peer C# video conferencing application that uses a specific TCP port(17500) for audio communication. Currently, on my application interface, I enter the other IP address which has the program opened in order to communicate. What I want to do is to find the IP addresses automatically.
So, I though the best way to achieve this is to obtain the local IP addresses that are using the same TCP port number, 17500. How can I do that ? or is there any other methods getting IP addresses using the same application ?
As mentioned in comments, you need some kind of peer-discovery protocol.
As many multimedia devices, routers etc. use multicast based discovery protocols like SSDP, I created a similar discovery service sample .
Usage is simple. Just use
Discoverer.PeerJoined = ip => Console.WriteLine("JOINED:" + ip);
Discoverer.PeerLeft= ip => Console.WriteLine("LEFT:" + ip);
Discoverer.Start();
All your clients will use the same code.
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Caching; // add this library from the reference tab
using System.Text;
using System.Threading.Tasks;
namespace SO
{
public class Discoverer
{
static string MULTICAST_IP = "238.212.223.50"; //Random between 224.X.X.X - 239.X.X.X
static int MULTICAST_PORT = 2015; //Random
static UdpClient _UdpClient;
static MemoryCache _Peers = new MemoryCache("_PEERS_");
public static Action<string> PeerJoined = null;
public static Action<string> PeerLeft = null;
public static void Start()
{
_UdpClient = new UdpClient();
_UdpClient.Client.Bind(new IPEndPoint(IPAddress.Any, MULTICAST_PORT));
_UdpClient.JoinMulticastGroup(IPAddress.Parse(MULTICAST_IP));
Task.Run(() => Receiver());
Task.Run(() => Sender());
}
static void Sender()
{
var IamHere = Encoding.UTF8.GetBytes("I AM ALIVE");
IPEndPoint mcastEndPoint = new IPEndPoint(IPAddress.Parse(MULTICAST_IP), MULTICAST_PORT);
while (true)
{
_UdpClient.Send(IamHere, IamHere.Length, mcastEndPoint);
Task.Delay(1000).Wait();
}
}
static void Receiver()
{
var from = new IPEndPoint(0, 0);
while (true)
{
_UdpClient.Receive(ref from);
if (_Peers.Add(new CacheItem(from.Address.ToString(), from),
new CacheItemPolicy() {
SlidingExpiration = TimeSpan.FromSeconds(20),
RemovedCallback = (x) => { if (PeerLeft != null) PeerLeft(x.CacheItem.Key); }
}
)
)
{
if (PeerJoined != null) PeerJoined(from.Address.ToString());
}
Console.WriteLine(from.Address.ToString());
}
}
}
}
Now a little bit about the algorithm:
Every client multicasts a packet every seconds.
if the receiver(every client has it) gets a packet from an IP that isn't in its cache, it will fire PeerJoined method.
Cache will expire in 20 seconds. If a client doesn't receive a packet within that duration from another client in cache, it will fire PeerLeft method.
I believe if you are using a peer to peer application to exchange packets, when you need to know if someone "Is Online and Ready for connection", you need to send a broadcast. We can do it easily using an UDP connection.
I'll post an example where you use two methods: one to ask the entire network for ready clients in a broadcast message, and the other will start a listener to answer back broadcast asking message, or start a connection if a response of type "i am here" comes.
Hope it helps!
public sealed class UdpUtility
{
// Our UDP Port
private const int broadcastPort = 11000;
// Our message to ask if anyone is ready for connection
private const string askMessage = "ARE ANYONE OUT THERE?";
// Our answer message
private const string responseMessage = "I AM HERE!";
// We use this method to look for a client to connect with us.
// It will send a broadcast to the network, asking if any client is ready for connection.
public void SendBroadcastMessage()
{
var udp = new UdpClient(broadcastPort);
var endpoint = new IPEndPoint(IPAddress.Broadcast, broadcastPort);
try
{
var bytes = Encoding.ASCII.GetBytes(askMessage);
udp.Send(bytes, bytes.Length, endpoint);
}
catch (Exception ex)
{
// Treat your connection exceptions here!
}
}
// This method will start a listener on the port.
// The client will listen for the ask message and the ready message.
// It can then, answer back with a ready response, or start the TCP connection.
public void ListenBroadcastMessage()
{
var udp = new UdpClient(broadcastPort);
var endpoint = new IPEndPoint(IPAddress.Broadcast, broadcastPort);
bool received = false;
try
{
while (!received)
{
// We start listening broadcast messages on the broadcast IP Address interface.
// When a message comes, the endpoing IP Address will be updated with the sender IP Address.
// Then we can answer back the response telling that we are here, ready for connection.
var bytes = udp.Receive(ref endpoint);
var message = Encoding.ASCII.GetString(bytes);
if (message == askMessage)
{
// Our client received the ask message. We must answer back!
// When the client receives our response, his endpoint will be updated with our IP Address.
// The other client can, then, start the TCP connection and do the desired stuff.
var responseBytes = Encoding.ASCII.GetBytes(responseMessage);
udp.Send(responseBytes, responseBytes.Length, endpoint);
}
else if (message == responseMessage)
{
// We received a connection ready message! We can stop listening.
received = true;
// We received a response message!
// We can start our TCP connection here and do the desired stuff.
// Remember: The other client IP Address (the thing you want) will be on the
// endpoint object at this point. Just use it and start your TCP connection!
}
}
}
catch (Exception ex)
{
// Treat your connection exceptions here!
}
}
}
Invoke your command prompt to do "netstat -n" and extract the output.
Here is a piece of code taken from a program that I have wrote modified to fit your requirements. You will still need to further process the data to get the IP addresses
Process netP = new Process();
ProcessStartInfo netPI = new ProcessStartInfo();
netPI.FileName = "cmd";
netPI.UseShellExecute = false;
netPI.RedirectStandardOutput = true;
netPI.RedirectStandardInput = true;
netPI.RedirectStandardError = true;
netPI.CreateNoWindow = true;
netP.StartInfo = NetPI;
netP.Start();
while (!netP.Start())
Thread.Sleep(100);
StreamWriter sW = netP.StandardInput;
StreamReader sR = netP.StandardOutput;
sW.WriteLine("netstat -n")
sW.Close();
string data = sR.ReadToEnd();
sR.Close();
//Do some further processing to filter out the addresses and extract

Creating a Socket Connection in Store App not Working

I have just released an app to the Windows Phone Store, after thoroughly debugging, and the functionality when testing in debug and release mode is not the same as when I use the app that was downloaded from the store. When testing in debug and release mode from my solution I have no issues (on device or on emulator) and everything works great. After downloading from the store I only return the SocketError.NetworkDown error?
I am creating a Socket connection to gather network interface information.
private async void UpdateCurrentInterface()
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// To run this application you should specify the name of a server on your network that is running
// the required service.
string serverName = "www.bing.com";
// This identifies the port over which to communicate.
int portNumber = 80;
// Create DnsEndPoint.
DnsEndPoint hostEntry = new DnsEndPoint(serverName, portNumber);
// Create a SocketAsyncEventArgs object to be used in the connection request.
SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs();
socketEventArg.RemoteEndPoint = hostEntry;
socketEventArg.UserToken = socket;
socketEventArg.Completed += ShowNetworkInterfaceInformation1;
// // Make an asynchronous Connect request over the socket.
socket.ConnectAsync(socketEventArg);
}
/// <summary>
/// Display the network information using the GetCurrentNetworkInterface extension method on the socket.
/// </summary>
/// <remarks>This is the callback from the ConnectAsync method.</remarks>
private void ShowNetworkInterfaceInformation1(object s, SocketAsyncEventArgs e)
{
// When ConnectAsync was called it was passed the socket object in
// the UserToken field of the socketEventArg. This context is retrieved once
// the ConnectAsync has completed.
Socket socket = e.UserToken as Socket;
// Only call GetCurrentNetworkInterface if the connection was successful.
if (e.SocketError == SocketError.Success)
{
NetworkInterfaceInfo netInterfaceInfo = socket.GetCurrentNetworkInterface();
// We are making UI updates, so make sure these happen on the UI thread.
Dispatcher.BeginInvoke(() =>
{
currentTypeTextBlock.Text = GetInterfaceTypeString(netInterfaceInfo.InterfaceType);
currentNameTextBlock.Text = name = netInterfaceInfo.InterfaceName;
string change = "";
if (netInterfaceInfo.InterfaceState.ToString() == "Connected")
currentStateTextBlock.Text = "connected";
else
currentStateTextBlock.Text = "disconnected";
});
}
else if (e.SocketError == SocketError.NetworkDown)
{
DisplayMessage("Could not connect.", "Network Down Error", MessageBoxButton.OK);
}
// Close our socket since we no longer need it.
socket.Close();
}
Include ID_CAP_NETWORKING as a capability within WMAppManifest.

WCF web service discovery on network interfaces with multiple IP addresses

I'm trying to do a webservice discovery using WCF's DiscoveryClient using this code:
// Setup the discovery client (WSDiscovery April 2005)
DiscoveryEndpoint discoveryEndpoint = new UdpDiscoveryEndpoint(DiscoveryVersion.WSDiscoveryApril2005);
DiscoveryClient discoveryClient = new DiscoveryClient(discoveryEndpoint);
// Setup the wanted device criteria
FindCriteria criteria = new FindCriteria();
criteria.ScopeMatchBy = new Uri("http://schemas.xmlsoap.org/ws/2005/04/discovery/rfc3986");
criteria.Scopes.Add(new Uri("onvif://www.onvif.org/"));
// Go find!
criteria.Duration = TimeSpan.FromMilliseconds(duration);
discoveryClient.FindAsync(criteria, this);
This works very well on a machine with a single IP address (10.1.4.25) assigned to the single network interface. The broadcast is sent from 10.1.4.25 to 239.255.255.250, and I get responses from 5 devices all on the same subnet.
However, when the machine has multiple IPs on the same interface, it seems to pick a single source IP and sends the request from that.
In this case, I get a reply from a single device giving a 169.254 address.
I have tried setting UdpDiscoveryEndpoint.TransportSettings.MulticastInterfaceId to a suitable interface ID which hasn't helped as it identifies a single interface, not a specific IP.
The UdpDiscoveryEndpoint.ListenUri property also returns the multicast address, and so won't effect the source IP.
UdpDiscoveryEndpoint.Address is the URN for the discovery protocol.
Is there any way I can force it to send from a specific IP address, or ideally, multiple requests on each configured IP?
I have also tried ONVIF Device Manager that seems to have the same problem.
Note that this is not about making a service bind to a specific, or "all address" IP. It is about the IP a discovery request is sent from.
Well, I had the same problem and after some days of research, reading ONVIF documents and learning some tips about multicasting, I developed this code which works fine.
As an example the main IP address on my network adapter is 192.168.80.55 and I also set another IP(192.168.0.10) in advanced settings. With use of this code I can discover device service of a camera with the IP address of 192.168.0.12.
The most important part of this sample is "DeepDiscovery" method which contains the main idea of iteration on network addresses and multicasting proper Probe message.
I recommend deserialization of the response in "GetSocketResponse" method. Currently, just I extract the service URI using Regex.
As mentioned in this article (https://msdn.microsoft.com/en-us/library/dd456791(v=vs.110).aspx):
For WCF Discovery to work correctly, all NICs (Network Interface
Controller) should only have 1 IP address.
I am doing the exact action that WS-Discovery does and using the standard 3702 port, but I myself build the SOAP envelope
and use Socket class for sending the packet for all IP addresses that have been set for the network interface controller.
class Program
{
static readonly List<string> addressList = new List<string>();
static readonly IPAddress multicastAddress = IPAddress.Parse("239.255.255.250");
const int multicastPort = 3702;
const int unicastPort = 0;
static void Main(string[] args)
{
DeepDiscovery();
Console.ReadKey();
}
public static void DeepDiscovery()
{
string probeMessageTemplate = #"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"" xmlns:a=""http://schemas.xmlsoap.org/ws/2004/08/addressing""><s:Header><a:Action s:mustUnderstand=""1"">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action><a:MessageID>urn:uuid:{messageId}</a:MessageID><a:ReplyTo><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand=""1"">urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To></s:Header><s:Body><Probe xmlns=""http://schemas.xmlsoap.org/ws/2005/04/discovery""><d:Types xmlns:d=""http://schemas.xmlsoap.org/ws/2005/04/discovery"" xmlns:dp0=""http://www.onvif.org/ver10/device/wsdl"">dp0:Device</d:Types></Probe></s:Body></s:Envelope>";
foreach (IPAddress localIp in
Dns.GetHostAddresses(Dns.GetHostName()).Where(i => i.AddressFamily == AddressFamily.InterNetwork))
{
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(new IPEndPoint(localIp, unicastPort));
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastAddress, localIp));
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 255);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.MulticastLoopback = true;
var thread = new Thread(() => GetSocketResponse(socket));
var probeMessage = probeMessageTemplate.Replace("{messageId}", Guid.NewGuid().ToString());
var message = Encoding.UTF8.GetBytes(probeMessage);
socket.SendTo(message, 0, message.Length, SocketFlags.None, new IPEndPoint(multicastAddress, multicastPort));
thread.Start();
}
}
public static void GetSocketResponse(Socket socket)
{
try
{
while (true)
{
var response = new byte[3000];
EndPoint ep = socket.LocalEndPoint;
socket.ReceiveFrom(response, ref ep);
var str = Encoding.UTF8.GetString(response);
var matches = Regex.Matches(str, #"http://\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/onvif/device_service");
foreach (var match in matches)
{
var value = match.ToString();
if (!addressList.Contains(value))
{
Console.WriteLine(value);
addressList.Add(value);
}
}
//...
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
//...
}
}
}

Manage several/multiple tcp connections

I have a server application and client application with the functionality already working. Let me show you how I connect my client application to my server app:
//SERVER
// instantiate variables such as tempIp, port etc...
// ...
// ...
server = new TcpListener(tempIp, port); //tempIp is the ip address of the server.
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[MaxChunkSize];
String data = null;
// Enter the listening loop.
while (disconect == false)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
client = server.AcceptTcpClient(); // wait until a client get's connected...
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
stream = client.GetStream();
// now that the connection is established start listening though data
// sent through the stream..
int i;
try
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// etc..
....
ok now on the client side lets say I want to establish a connection then send some data through the stream
//Client
client = new TcpClient(serverIP, port);
// Get a client stream for reading and writing.
stream = client.GetStream();
//then if I wish to send the string hello world to the server I would do:
sendString(stream, "Hello world");
protected void sendString(NetworkStream stream, string str)
{
sendBytes(stream, textToBytes(str));
}
protected void sendBytes(NetworkStream stream, Byte[] data)
{
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
}
protected static Byte[] textToBytes(string text)
{
return System.Text.Encoding.ASCII.GetBytes(text);
}
since I am able to send bytes I am able to send files or everything that I want. the technique that I use is that if I send the string file for example to the server then the server will start listening for a file. It will open a stream and write the received bytes to that file. If a different keyword is send the server will start listening on a different method etc..
So when dealing with one server and one client everything works great. Now I want to add more clients and need them to also connect to the server. I know that several connections can be establish on the same port just like we do it with por 80 on websites... I do not know how to manage several connections. so one thing I was thinking was to leave everything as it is. if a connection is established then tell the server to start another thread listening for other connections on the same port. with this technique I will have several threads running plus I just know the basics of multrythreading. If this technique is my best option I will start implementing it. You guys out there are really knowledgeable about all this so it will be nice if someone can point me on the right direction. Or maybe I should listen on several ports. if the server is already connected on port 7777 for example then do not accept connections from that port and start listening on port 7778 for example. I mean there could be so many different ways of achieving what I need and you guys probably know the best way. I just know the basics of networking...
You could use threading:
var client = server.AcceptTcpClient();
var t = new Thread(new ParameterizedThreadStart(AccentClient));
t.Start(client);
The target method would look like this
public void AccentClient(object clientObj)
{
var client = clientObj as TcpClient;
// Do whatever you need to do with the client
}
If you are not familiar with multithreading, it is important you learn at least the basics first.
You could implement a class that encapsulates TCP behavior. Check this class:
public class SimpleListener
{
private System.Net.Sockets.TcpListener _tcpListen;
//declare delegate to handle new connections
public delegate void _new_client(System.Net.Sockets.TcpClient tcpclient);
//declare a clients container (or something like...). OPTION 1
List<System.Net.Sockets.TcpClient> _connected_clients;
//declare an event and event handler (the same for _new_client) for new connections OPTION 2
public event _new_client new_tcp_client;
//public (The list of connected clients).
public List<System.Net.Sockets.TcpClient> ConnectedClients { get { return _connected_clients; } }
public SimpleListener(string ip, int listenport)
{
System.Net.IPAddress ipAd = System.Net.IPAddress.Parse(ip);
_tcpListen = new System.Net.Sockets.TcpListener(new System.Net.IPEndPoint(ipAd,listenport));
_connected_clients = new List<System.Net.Sockets.TcpClient>();
}
//Fire this method to start listening...
public void Listen()
{
_tcpListen.Start();
_set_listen();
}
//... and this method to stop listener and release resources on listener
public void Stop()
{
_tcpListen.Stop();
}
//This method set the socket on listening mode...
private void _set_listen()
{
//Let's do it asynchronously - Set the method callback, with the same definition as delegate _new_client
_tcpListen.BeginAcceptTcpClient(new AsyncCallback(_on_new_client), null);
}
//This is the callback for new clients
private void _on_new_client(IAsyncResult _async_client)
{
try
{
//Lets get the new client...
System.Net.Sockets.TcpClient _tcp_cl = _tcpListen.EndAcceptTcpClient(_async_client);
//Push the new client to the list
_connected_clients.Add(_tcp_cl);
//OPTION 2 : Fire new_tcp_client Event - Suscribers will do some stuff...
if (new_tcp_client != null)
{
new_tcp_client(_tcp_cl);
}
//Set socket on listening mode again... (and wait for new connections, while we can manage the new client connection)
_set_listen();
}
catch (Exception ex)
{
//Do something...or not
}
}
}
You could use this in your code:
//SERVER
// instantiate variables such as tempIp, port etc...
// ...
// ...
SimpleListener server = new SimpleListener(tempIp, port); //tempIp is the ip address of the server.
//add handler for new client connections
server.new_tcp_client += server_new_tcp_client;
// Start listening for client requests.
server.Listen();
.... //No need to loop. The new connection is handled on server_new_tcp_client method
void server_new_tcp_client(System.Net.Sockets.TcpClient tcpclient)
{
// Buffer for reading data
Byte[] bytes = new Byte[MaxChunkSize];
String data = null;
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
System.IO.Stream stream = tcpclient.GetStream();
// now that the connection is established start listening though data
// sent through the stream..
int i;
try
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// etc..
....
}

Categories

Resources