1

I've created two apps (A client and a server) which can communicate with each other as long as I input the local IP address of the machine the server app is running on into the client app (in code).

I would like the client app to automatically discover the local IP address of the machine running the server app and connect to it, so they can be run on any network without the need to enter the IP in code.

Both of these apps with be running on the same network (ie. Over WiFi, not the Internet)

Here is what I have so far in my client app:

// COMMUNICATE WITH SERVER

    private TcpClient client = new TcpClient();
    private IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("192.168.2.35"), 8888);

    public Console()
    {
        InitializeComponent();
        client.Connect(serverEndPoint);
    }

    private void SendMessage(string msg)
    {
        NetworkStream clientStream = client.GetStream();

        ASCIIEncoding encoder = new ASCIIEncoding();
        byte[] buffer = encoder.GetBytes(msg);

        clientStream.Write(buffer, 0, buffer.Length);
        clientStream.Flush();
    }

In this example I can only connect to a server running on "192.168.2.35", I would like it to be able to find the server running on port 8888 on any machine on the network.

Alternatively, if this isn't possible I would like the server to broadcast its IP as a message (of some sort) and have the client receive this message and verify it is the server and connect to it.

I think my second example is the proper way to do this, but I can't seem to wrap my head around how to get it working (I'm fairly new to C#), and any other examples I've found I can't seem to get to work with my applications.

Here is my server code if it helps answer my question:

private void Server()
    {
        this.tcpListener = new TcpListener(IPAddress.Any, 8888);
        this.listenThread = new Thread(new ThreadStart(ListenForClients));
        this.listenThread.Start();
    }

    private void ListenForClients()
    {
        this.tcpListener.Start();

        while (true)
        {
            TcpClient client = this.tcpListener.AcceptTcpClient();


            connectedClients++;
            lblNumberOfConnections.Text = connectedClients.ToString();

            Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
            clientThread.Start(client);

        }

Thanks



EDIT: I've tried adding THIS to my project, but being so new I'm unsure how to implement it properly, and it didn't get me anywhere.


2nd EDIT: I've tried implementing a UDP broadcast a few times now, with no luck on any attempt yet. My latest attempt was at implementing THIS (minus the chat parts). I just can't seem to get a UDP broadcast working at all with my project, as it seems to be way over my head at my current skill level. Unfortunately, having my client automatically connect to the server is 100% necessary for my project to function...

My other problem, which is maybe best to start a separate question for, but somewhat correlates to this issue is: My client GUI consists of a panel that switches between multiple custom classes, each containing different buttons, etc. (works similar to tab pages) that communicate to the server I'm trying to connect to. Once I get the UDP broadcast figured out, will I need to code that into every class separately? or is there a way of having all classes running in my panel connect to the same server?

Community
  • 1
  • 1
Patrick
  • 430
  • 6
  • 21
  • Anyone have any pointers on this? It's the main issue holding me back from an RC on this project... – Patrick Dec 24 '15 at 02:45

1 Answers1

1

A simple, but possibly costly(in terms of network traffic) solution would be for your server application to broadcast over UDP it's application and connection info. Your client could listen for all broadcast packets that have your servers custom header. Assuming a connection is made you could stop the broadcast. The downside is you would have to be broadcasting constantly if a client is not connected and this can clog your network if there aren't limits placed on the broadcast speed.

EDIT: Here is a boiled down explanation generated from the MSDN article https://msdn.microsoft.com/en-us/library/tst0kwb1(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

EDIT #2: I've expanded on this answer on my blog, as well as provided downloadable example projects. the article can be found at http://martialdeveloper.com/wordpress/?p=21

1. Find your network's broadcast IP

A special “Broadcast Address” must be used when using UDP for the purpose of sending a datagram to all machines connected to a given network. For example, the typical home network host/gateway of 192.168.0.1 has a broadcast address of 192.168.0.255. If your network differs from this you can use an IPv4 broadcast address calculator like the one found here http://jodies.de/ipcalc Or read the introductory section on MSDN describing the broadcast address. https://msdn.microsoft.com/en-us/library/tst0kwb1(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

2. Select a listening/broadcast port

Any port that is free on your client & server is fine. The MSDN example uses 11000. This port number is used in your broadcaster, and listener.

3. Code for the Listener

Note to the reader. All error handling has been omitted for clarity of the example.

int listenPort = 11000;
bool done = false;

UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any,listenPort);


while (!done) // This loop listens for your broadcast packets
{
    Console.WriteLine("Waiting for broadcast");
    byte[] bytes = listener.Receive( ref groupEP);

    Console.WriteLine("Received broadcast from {0} :\n {1}\n",
            groupEP.ToString(),
            Encoding.ASCII.GetString(bytes,0,bytes.Length));
}
listener.Close();

Note: The third parameter to Console.WriteLine, "Encoding.ASCII..." represents the string value sent over UDP in the datagram packet. This contains the desired negotiation information for a discovery situation, such as the IP address of the client or server you wish to connect to.

4. Code for the Broadcaster

Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
            ProtocolType.Udp);

IPAddress broadcast = IPAddress.Parse("This string should be the broadcast IP address");  //NOTE: Broadcast IP goes here!!

byte[] sendbuf = Encoding.ASCII.GetBytes("This is the message string to be broadcast"); //Your message to the client/server goes here, I.E. an
         // app/client name or ID and an IP to connect with over TCP

IPEndPoint ep = new IPEndPoint(broadcast, 11000);

s.SendTo(sendbuf, ep);

Console.WriteLine("Message sent to the broadcast address");

NOTE: This is a very simple example. The broadcaster may need to rebroadcast for a period of time to make sure the Listener receives it. Even after the UDP datagram is sent/received there will need to be some negotiation to ensure the TCP connection is made properly.

Aaron Thomas
  • 1,770
  • 2
  • 13
  • 14
  • Is there a way to make the server only broadcast it's IP when it see's the client? ie. have the client broadcast a message when it's opened, have the server listen for this message and then send back it's IP address to the client when it hears the message. If that makes sense... – Patrick Dec 24 '15 at 19:15
  • Sure, the direction the UDP packets are going Server->Client or Client->Server doesn't matter other than for the purposes of who needs to be listening. You could easily broadcast a client UDP packet telling the server to open a Tcp connection with the client. – Aaron Thomas Dec 24 '15 at 19:33
  • Any chance of you providing me with an example of this, or explaining how to implement it into my project's code? I'm at a loss with this stuff right now.. – Patrick Dec 24 '15 at 19:58
  • Rather than having my server app continuously broadcasting (and clogging up my network), Ideally I'd like it kind of how I explained in my other comment: Server sits idle in system tray -> User opens client on another machine -> Client sends IP address request to server -> Server sends it's IP to client -> client connects to server using received IP address – Patrick Dec 24 '15 at 20:03
  • @Patrick Here is a perfect example for you. https://msdn.microsoft.com/en-us/library/tst0kwb1(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1 your client would be the broadcaster in this example, and the server would be the listener. – Aaron Thomas Dec 24 '15 at 23:59
  • I already have a server that listens to a tcp client. That example you linked to looks like a very similar setup, but udp instead of tcp. That example still needs you to programmatically set the ip address though, which is the issue I'm having with my current app. It works fine as long as I manually specify the ip, but I need the server's ip to be discoverable by the client – Patrick Dec 25 '15 at 02:55
  • UDP functions a bit differently than TCP. For a UDP broadcast you have to specify a broadcast IP, and a port. The broadcast IP is not a single machine's IP, there is a good explanation here https://en.m.wikipedia.org/wiki/Broadcast_address so for any given network your client or server can calculate the broadcast IP based on the IP address of the machines own NIC. – Aaron Thomas Dec 25 '15 at 17:37
  • So for the previous example, your client would calculate the networks broadcast IP, then broadcast its specific IP and application specofic data inside a UDP datagram to the whole network via the broadcast IP, then the server would see the UDP datagram and respond by making a TCP connection to the client using the information in the UDP datagram that the client sent. – Aaron Thomas Dec 25 '15 at 17:39
  • I tried following an example from [THIS](http://stackoverflow.com/questions/22852781/c-sharp-how-to-do-network-discovery-using-udp-broadcast) question a while back, but I've been unable to implement it in my code properly. It seems it's a little over my head code-wise. Unfortunately, it's the only thing holding me back from a proper release of my application.. If someone wouldn't mind walking me through how to implement it into my client, that would be great. – Patrick Dec 25 '15 at 23:03
  • Aaron, your example seems like it should do exactly what I need, unfortunately it seems to be above my skill level. Would you mind breaking it down in an example I could put into my code to get it working? I've been trying to do this for a couple months now with no results so far. It seems like it should be simple enough, but I can't seem to get it to work in my code, so something that was split into separate snippets for my client and server would be ideal. I have some basic understanding of it, so I can make minor adjustments if need be, I'm just not sure on placement in my code. – Patrick Dec 26 '15 at 21:34
  • I'll put a basic working example together in the next few days. Unless someone beats me too it. – Aaron Thomas Dec 27 '15 at 19:39
  • Thanks so much. I hate to be a bother, it's just this has been driving me nuts for quite some time now since it's holding my project back so much – Patrick Dec 27 '15 at 19:45
  • @Patrick I've updated my previous answer with some additional detail. Hope this helps! – Aaron Thomas Dec 28 '15 at 19:05
  • Thanks Aaron, that's a lot of great info for me to go over. I'll post some updates in my question when I've had time to try to implement your example. I'm assuming since the broadcast IP of the server app can vary depending on who's system it's running on, that I'll need to implement a function to get the broadcast IP of the server's system programatically and convert that to a string for `IPAddress broadcast = IPAddress.Parse("This string should be the broadcast IP address");` as well as doing the same for the local IP for TCP connection. – Patrick Dec 28 '15 at 19:52
  • One thing I'm not sure on; can the message sent to the client be JUST the local IP address of the server? or does it need to contain an app name or ID as well? – Patrick Dec 28 '15 at 19:53
  • Another question, does my UDP broadcast need to be on a separate port from my TCP broadcast? ie. your example uses port 11000, and my TCP client communicates over port 8888 – Patrick Dec 28 '15 at 19:55
  • Yes, the broadcast IP will have to be calculated for each network your client/server is running on. It can contain just an IP, but that means you have no idea who is sending your server that IP. Ideally you'd have some sort of encrypted key or authorization code that identifies the client to the server before connecting, but in the simple case, you just need the IP to connect to if you aren't expecting any other UDP traffic. Your UDP and TCP traffic don't have to be on different ports, but I'd recommend it. – Aaron Thomas Dec 28 '15 at 19:57
  • Okay thanks, I'll see what I can do with the info you've given me – Patrick Dec 28 '15 at 20:38