Alan Wills
Alan Wills

Reputation: 41

Unable to connect to a TCPClient on another computer

So I have two laptops connected to the same wifi network with one running a very simple server and the other a very simple client connecting to it. When I run both the server and the client on one laptop they connect without a problem, but when running one on each laptop the client cannot connect to the server.

The code for the server is this:

using System;
using System.Net;
using System.Net.Sockets;

namespace Utils
{
    /// <summary>
    /// A base server which handles listening for client connections and has simple API to communicate back and forth
    /// </summary>
    public class BaseServer
    {
        #region Properties and Fields

        /// <summary>
        /// The listener we can use to detect incoming connections from clients to the server
        /// </summary>
        private TcpListener Listener { get; set; }

        /// <summary>
        /// Our interface to the single client we are supporting for now
        /// </summary>
        public Comms ClientComms { get; private set; }

        /// <summary>
        /// Determines whether we have clients connected
        /// </summary>
        public bool Connections { get; private set; }

        #endregion

        public BaseServer()
        {
            Listener = new TcpListener(IPAddress.Any, 1490);
            Listener.Start();

            ListenForNewClient();
        }

        /// <summary>
        /// Starts an asynchronous check for new connections
        /// </summary>
        private void ListenForNewClient()
        {
            Listener.BeginAcceptTcpClient(AcceptClient, null);
        }

        /// <summary>
        /// Callback for when a new client connects to the server
        /// </summary>
        /// <param name="asyncResult"></param>
        protected virtual void AcceptClient(IAsyncResult asyncResult)
        {
            ClientComms = new Comms(Listener.EndAcceptTcpClient(asyncResult));
            ClientComms.OnDataReceived += ProcessMessage;

            ListenForNewClient();
        }

        #region Message Callbacks

        /// <summary>
        /// A function which is called when the Client sends a message to the server.
        /// Override to perform custom message handling
        /// </summary>
        /// <param name="data"></param>
        protected virtual void ProcessMessage(byte[] data) { }

        #endregion

    }
}

And the code for the client is this:

using System;
using System.Net.Sockets;

namespace Utils
{
    /// <summary>
    /// A base client class which connects and communicates with a remote server
    /// </summary>
    public class BaseClient
    {
        #region Properties and Fields

        /// <summary>
        /// The interface to the server
        /// </summary>
        public Comms ServerComms { get; private set; }

        #endregion

        public BaseClient(string ipAddress, int portNumber = 1490)
        {
            // Attempt to connect
            try
            {
                ServerComms = new Comms(new TcpClient(ipAddress, portNumber));
                ServerComms.OnDataReceived += OnMessageReceived;
                ServerComms.OnDisconnect += OnServerDisconnect;
            }
            catch (Exception e)
            {
                Console.WriteLine("Connection failed");
            }
        }

        #region Callbacks

        /// <summary>
        /// A function which is called when this client receives a message.
        /// Override to perform behaviour when custom messages arrive.
        /// </summary>
        /// <param name="data"></param>
        protected virtual void OnMessageReceived(byte[] data) { }

        /// <summary>
        /// A function called when this client can no longer communicate to the server it is connected to
        /// </summary>
        protected virtual void OnServerDisconnect() { }

        #endregion
    }
}

The server is started from the main loop like this:

using System;

namespace BuildServer
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseServer server = new BaseServer();
            while (true)
            {

            }
        }
    }
}

and the Client is started like this:

using System;
using Utils;

namespace BuildServerClient
{
    class Program
    {
        static void Main(string[] args)
        {
            BaseClient client = new BaseClient();

            while (true)
            {
                Console.WriteLine("Ready");
                string message = Console.ReadLine();
                client.ServerComms.Send(message);
            }
        }
    }
}

One final class is a Comms class which is really a wrapper around the TCPClient and not really used currently, but I am adding it for the same of completeness.

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using static Utils.Delegates;

namespace Utils
{
    /// <summary>
    /// An interface to a client.
    /// Hides the nuts and bolts and provides a public interface of just data input and output from a data sender/receiver.
    /// </summary>
    public class Comms
    {
        #region Properties and Fields

        private TcpClient Client { get; set; }
        private MemoryStream ReadStream { get; set; }
        private MemoryStream WriteStream { get; set; }
        private BinaryReader Reader { get; set; }
        private BinaryWriter Writer { get; set; }

        /// <summary>
        /// Useful buffer for reading packeted messages from the server
        /// </summary>
        private byte[] ReadBuffer { get; set; }

        /// <summary>
        /// An event that is fired when this Comms receives a message
        /// </summary>
        public event OnDataReceived OnDataReceived;

        /// <summary>
        /// An event that is fired when this Comms can no longer communicate with the client sending it messages
        /// </summary>
        public event OnDisconnect OnDisconnect;

        #endregion

        public Comms(TcpClient client)
        {
            Client = client;
            ReadStream = new MemoryStream();
            WriteStream = new MemoryStream();
            Reader = new BinaryReader(ReadStream);
            Writer = new BinaryWriter(WriteStream);

            ReadBuffer = new byte[2048];
            Client.NoDelay = true;

            StartListening();
        }

        #region Data Sending Functions

        /// <summary>
        /// Convert a string to a byte array and then send to our client
        /// </summary>
        /// <param name="client"></param>
        /// <param name="str"></param>
        public void Send(string str)
        {
            SendByteArray(Encoding.UTF8.GetBytes(str));
        }

        /// <summary>
        /// Send a byte array to our client
        /// </summary>
        /// <param name="client"></param>
        /// <param name="bytes"></param>
        protected void SendByteArray(byte[] bytes)
        {
            Writer.Write(bytes);

            int bytesWritten = (int)WriteStream.Position;
            byte[] result = new byte[bytesWritten];

            WriteStream.Position = 0;
            WriteStream.Read(result, 0, bytesWritten);
            WriteStream.Position = 0;

            Client.GetStream().BeginWrite(result, 0, result.Length, null, null);
            Writer.Flush();
        }

        #endregion

        #region Data Receiving Functions

        /// <summary>
        /// Start listening for messages from the server
        /// </summary>
        private void StartListening()
        {
            try
            {
                Client.GetStream().BeginRead(ReadBuffer, 0, 2048, StreamReceived, null);
            }
            catch
            {
                OnDisconnect?.Invoke();
            }
        }

        /// <summary>
        /// Callback which processes a message sent from the server
        /// </summary>
        /// <param name="ar"></param>
        private void StreamReceived(IAsyncResult ar)
        {
            int bytesRead = 0;
            try
            {
                lock (Client.GetStream())
                {
                    bytesRead = Client.GetStream().EndRead(ar);
                }
            }
            catch { }

            //Create the byte array with the number of bytes read
            byte[] data = new byte[bytesRead];

            //Populate the array
            for (int i = 0; i < bytesRead; i++)
            {
                data[i] = ReadBuffer[i];
            }

            OnDataReceived?.Invoke(data);

            //Listen for new data
            StartListening();
        }

        #endregion
    }
}

The IP Addresses and Port numbers are correct and since it works when running on the same machine, I was thinking it might be a firewall issue or something? Does anyone have any ideas as to what might be causing this problem?

Upvotes: 0

Views: 2467

Answers (2)

Alan Wills
Alan Wills

Reputation: 41

David's answer was correct. I previously tried it with just the firewall disabled for private networks. However, I disabled the firewall for guest and public networks and it worked a treat.

The method of testing proposed by codenoir was also very effective at ruling out my client. I suspected it was something to do with the firewall, but once you rule out the impossible...

Thanks to both

Upvotes: 0

Xavier J
Xavier J

Reputation: 4724

Make sure the firewall (Windows Firewall, or whatever's there) is turned off on the server machine, or there's a firewall exception for your port number.

Upvotes: 3

Related Questions