TcpClient only connects if server starts listening before ConnectAsync called

1

I have a basic TCP client and server both running locally on my machine.

If the server is already listening when I call ConnectAsync() then the client connects to the server, no problem.

If I start up the client and call ConnectAsync(), THEN start the server after to listen to clients, it doesn't establish a connection. ConnectAsync hangs for about 85 seconds before I get the following error: System.Net.Sockets.SocketException (0x80004005): Connection refused. This is despite the fact that the server has already started listening.

Not sure if this affects TcpClient but the client is running in an Xamarin project.

Here is my code:

CLIENT:

public static class DataSource
{
    private static TcpClient client;
    private static NetworkStream networkStream;

    public static bool Connected => client is null ? false : client.Connected;

    public static async Task EstablishTcpConnection(string serverIP, int port)
    {
        CloseTcpConnection();

        try
        {
            client = new TcpClient();
            await client.ConnectAsync(IPAddress.Parse(serverIP), port);
            networkStream = client.GetStream();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"{nameof(EstablishTcpConnection)} Error: {ex}");
        }
    }

    public static void CloseTcpConnection()
    {
        if (networkStream != null)
        {
            networkStream.Close();
            networkStream.Dispose();
            networkStream = null;
        }

        if (client != null)
        {
            client.Close();
            client.Dispose();
            client = null;
        }
    }

    public static async Task SendTcp(string toSend)
    {
        if (client is null) return;
        if (networkStream is null) return;
        if (!Connected) return;

        if (networkStream != null && networkStream.CanWrite)
        {
            byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(toSend);
            await networkStream.WriteAsync(bytesToSend, 0, bytesToSend.Length);
        }
    }

    public static async Task TcpListener()
    {
        while (networkStream != null && networkStream.CanRead)
        {
            if (client is null) return;
            if (networkStream is null) return;
            if (!Connected) return;

            byte[] bytesToRead = new byte[client.ReceiveBufferSize];
            int bytesRead = await networkStream.ReadAsync(bytesToRead, 0, client.ReceiveBufferSize);
            string received = Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
            Console.WriteLine($"Received: {received}");
        }
    }
}

SERVER:

internal class Program
{
    private const string serverIP = "MyServerIp";
    private const int port = myPort;

    private static void Main(string[] args)
    {
        Listener();
        Console.ReadLine();
    }

    public static async Task Listener()
    {
        //---listen at the specified IP and port no.---
        TcpListener listener = new TcpListener(IPAddress.Parse(serverIP), port);
        listener.Start();
        Console.WriteLine("Listening...");

        while (true)
        {
            //---incoming client connected---
            ReceiveClient(await listener.AcceptTcpClientAsync());
        }
    }

    public static async Task ReceiveClient(TcpClient client)
    {
        if (client is null) return;
        Console.WriteLine("Client Connected");

        //---get the incoming data through a network stream---
        NetworkStream networkStream = client.GetStream();
        byte[] buffer = new byte[client.ReceiveBufferSize];

        while (client != null && client.Connected)
        {
            //---read incoming stream---
            int bytesRead = await networkStream.ReadAsync(buffer, 0, client.ReceiveBufferSize);
            if (bytesRead == 0) break;

            //---convert the data received into a string---
            string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Received : " + dataReceived);

            //---write back the text to the client---
            Console.WriteLine("Sending back : " + dataReceived);
            networkStream.Write(buffer, 0, bytesRead);
        }

        client.Close();
        client.Dispose();
        Console.WriteLine("Client Disconnected");
    }
}
c#
xamarin
tcpclient
asked on Stack Overflow Mar 13, 2020 by David Andrew Thorpe • edited Mar 16, 2020 by David Andrew Thorpe

2 Answers

1

If you're running both programs on the same computer, it's likely that the ConnectAsync() request is rejected before the server is initialised. This happens because there’s no process listening on the incoming port. So the OS rejects the connection request.

Check the type of exception being thrown. If it's a SocketException, its SocketErrorCode property will shed more light on why the connection failed.

A complete list of error codes and their descriptions is available on the Microsoft website at https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2.

answered on Stack Overflow Mar 13, 2020 by Pranav Negandhi • edited Mar 13, 2020 by Pranav Negandhi
1

Surely ConnectAsync() should still work, even if the server starts listening after and is within the timeout?

No; that's not what "timeout" means. "Timeout" does not imply a "retry".

When you establish a connection, your client app is reaching out to the server app running on a server computer. The "timeout" is just the amount of time the client app will wait for a response. If the server computer is running, but doesn't have a listening server app for that port, then the server computer will immediately send a response indicating that there is no server app running. This gets sent back to your client app. This is a valid "response", so the timeout doesn't come into play.

If you want to retry connections, waiting for a server, then you'll need to write that logic yourself.

answered on Stack Overflow Mar 13, 2020 by Stephen Cleary

User contributions licensed under CC BY-SA 3.0