Setting up a TCP/IP Client and Server to communicate over a network
Asked Answered
O

2

7

I am trying to learn a little about socket programming and I have stumbled across TcpListener and TcpClient to use as I read they are slightly easier for beginners. The basic jist of what I want to accomplish is to have a small form that can be run on my laptop and another laptop on the same network and for them to be able to communicate i.e. send a string of text to each other. Once I have this I will hopefully develop it further :)

So far I have created both a Client and a Server program using msdn and various guides found on the internet. I can get them to communicate when both running on one laptop however when I move the client to another laptop I get nowhere. I think my main issue is I do not understand quite how the client finds the Servers IP as I think I could hard code it but when I come back at another time I'm sure the IP will have changed. Is there any way to get the two to connect in a more dynamic way to encompass the changing IP? My current Client code:

    public void msg(string mesg)
    {
        lstProgress.Items.Add(">> " + mesg);
    }

    private void btnConnect_Click(object sender, EventArgs e)
    {
        string message = "Test";
        try
        {
            // Create a TcpClient.
            // Note, for this client to work you need to have a TcpServer 
            // connected to the same address as specified by the server, port
            // combination.
            Int32 port = 1333;
            TcpClient client = new TcpClient(<not sure>, port); //Unsure of IP to use.

            // Translate the passed message into ASCII and store it as a Byte array.
            Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);

            // Get a client stream for reading and writing.
            //  Stream stream = client.GetStream();

            NetworkStream stream = client.GetStream();

            // Send the message to the connected TcpServer. 
            stream.Write(data, 0, data.Length);

            lstProgress.Items.Add(String.Format("Sent: {0}", message));

            // Receive the TcpServer.response.

            // Buffer to store the response bytes.
            data = new Byte[256];

            // String to store the response ASCII representation.
            String responseData = String.Empty;

            // Read the first batch of the TcpServer response bytes.
            Int32 bytes = stream.Read(data, 0, data.Length);
            responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
            lstProgress.Items.Add(String.Format("Received: {0}", responseData));

            // Close everything.
            stream.Close();
            client.Close();
        }
        catch (ArgumentNullException an)
        {
            lstProgress.Items.Add(String.Format("ArgumentNullException: {0}", an));
        }
        catch (SocketException se)
        {
            lstProgress.Items.Add(String.Format("SocketException: {0}", se));
        }
    }

My current Server code:

    private void Prog_Load(object sender, EventArgs e)
    {
        bw.WorkerSupportsCancellation = true;
        bw.WorkerReportsProgress = true;
        bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);

        if (bw.IsBusy != true)
        {
            bw.RunWorkerAsync();
        }
    }

    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        lstProgress.Items.Add(e.UserState);
    }

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;

        if ((worker.CancellationPending == true))
        {
            e.Cancel = true;
        }
        else
        {
            try
            {
                // Set the TcpListener on port 1333.
                Int32 port = 1333;
                //IPAddress localAddr = IPAddress.Parse("127.0.0.1");
                TcpListener server = new TcpListener(IPAddress.Any, port);

                // Start listening for client requests.
                server.Start();

                // Buffer for reading data
                Byte[] bytes = new Byte[256];
                String data = null;

                // Enter the listening loop.
                while (true)
                {
                    bw.ReportProgress(0, "Waiting for a connection... ");
                    // Perform a blocking call to accept requests.
                    // You could also user server.AcceptSocket() here.
                    TcpClient client = server.AcceptTcpClient();
                    bw.ReportProgress(0, "Connected!");

                    data = null;

                    // Get a stream object for reading and writing
                    NetworkStream stream = client.GetStream();

                    int i;

                    // 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);
                        bw.ReportProgress(0, String.Format("Received: {0}", data));

                        // Process the data sent by the client.
                        data = String.Format("I Have Received Your Message: {0}", data);

                        byte[] mssg = System.Text.Encoding.ASCII.GetBytes(data);

                        // Send back a response.
                        stream.Write(mssg, 0, mssg.Length);
                        bw.ReportProgress(0, String.Format("Sent: {0}", data));
                    }

                    // Shutdown and end connection
                    client.Close();
                }
            }
            catch (SocketException se)
            {
                bw.ReportProgress(0, String.Format("SocketException: {0}", se));
            }
        }
    }

As you can probably tell I am brand new to this so if there is a better way to implement this I am more than happy to learn! Thanks for any help in advance :)

My solution thanks to the answers below:

private String IPAddressCheck()
    {
        var IPAddr = Dns.GetHostEntry("HostName");
        IPAddress ipString = null;

        foreach (var IP in IPAddr.AddressList)
        {
            if(IPAddress.TryParse(IP.ToString(), out ipString) && IP.AddressFamily == AddressFamily.InterNetwork)
            {
                break;
            }
        }
        return ipString.ToString();
    }

    private void btnConnect_Click(object sender, EventArgs e)
    {
        string message = "Test";
        try
        {
            Int32 port = 1337;  
            string IPAddr = IPAddressCheck();
            TcpClient client = new TcpClient(IPAddr, port);

I'm not sure if it is the neatest solution but it is working well so thank you for the answers :)

Oculo answered 12/2, 2013 at 17:38 Comment(2)
If you're looking for a general code review, I'd suggest posting to CodeReview.SE instead. If you're looking for the answer to a specific question ("Is there any way to get the two to connect in a more dynamic way to encompass the changing IP?"), you should reduce the code to just the area you're having problems with.Infantine
Apologies Bobson I will keep my code snippets shorter and more relevant in the future! Thanks for the advice :)Oculo
L
2

Not quite sure what you mean by 'more dynamic way to encompass the changing ip'. Taking a guess for starters where you currently have:

TcpClient client = new TcpClient(<not sure>, port); //Unsure of IP to use.

You can run both client & server on the same machine and use the local loopback IP address:

IPAddress.Parse("127.0.0.1")

If they are running on different machines just replace 127.0.0.1 with whatever IP address the server is using (this assumes no NAT or firewalls in the way).

If you don't want to use IP addresses you could always use hostnames (these might be considered more 'dynamic') but this would require a suitably configured DNS setup (for local systems):

TcpClient client = new TcpClient("testMachine1", 1333);

It's great to learn socket programming. I am the developer of a network library, networkcomms.net, so should you also want to work backwards from a work example at the same time as learning yourself please checkout this wpf chat example.

Lipman answered 12/2, 2013 at 17:57 Comment(1)
Thank you very much, as seen in my edit to op I have used the hostname to obtain the IP and point to that. Cheers for pointing me in the right direction and I will definitely take a look at your worked example to learn some more!Oculo
K
1

If you know the name of the computer you want to connect to then you can find its IP quite easily with System.Net.DNS.

var ip = System.Net.Dns.GetHostEntry("JacksLaptop"); 
string ipString = ip.AddressList[0].ToString();

The IP you think you're using may not be the one in location 0 of that array so watch out for that.

Kandi answered 12/2, 2013 at 18:1 Comment(1)
Thank you for your help I also used your advice in my solution, I think I have implemented a way to ensure that I get the correct IP from the AddressList you can see my attempt in my edit to op :)Oculo

© 2022 - 2024 — McMap. All rights reserved.