Christopher Pisz
Christopher Pisz

Reputation: 4010

Implement an asynchronous tcp connection with a timeout

I am getting an exception:

"An unhandled exception of type 'System.NotSupportedException' occurred in 
 System.dll

 Additional information: This protocol version is not supported."

when I run the following code. I am trying to implement an asynchronous tcp connection with a timeout.

I've seen and read several stack overflow examples, some using TcpClient and some using Socket. I assume the former wraps up the latter and is newer. I am trying with TcpClient.BeginConnect

The documentation does not list NotSupported as one of the exception types this method can throw. How do I track down what the problem is?

public class Client
{
    private string m_host;
    private uint m_port;
    private uint m_timeoutMilliseconds;
    private TcpClient m_client;

    public Client(string host, uint port, uint timeoutMilliseconds)
    {
        m_host = host;
        m_port = port;
        m_timeoutMilliseconds = timeoutMilliseconds;
        m_client = new TcpClient();
    }

    public bool Connect()
    {
        IPHostEntry hostInfo = Dns.GetHostEntry(m_host);
        IPAddress ipAddress = hostInfo.AddressList[0];
        IPEndPoint endpoint = new IPEndPoint(ipAddress, (int)m_port);

        IAsyncResult result = m_client.BeginConnect(ipAddress, (int)m_port, new AsyncCallback(OnConnect), m_client);
        result.AsyncWaitHandle.WaitOne((int)m_timeoutMilliseconds, true);

        // SNIP

        return true;
    }

    public void Disconnect()
    {
        throw new NotImplementedException();
    }

    public void MakeRequest(string symbol)
    {
        throw new NotImplementedException();
    }


    private static void OnConnect(IAsyncResult asyncResult)
    {
        Socket socket = (Socket)asyncResult.AsyncState;
        socket.EndConnect(asyncResult);

        Console.WriteLine("Socket connected to {0}"
                        , socket.RemoteEndPoint.ToString());

    }
}

class Program
{
    static void Main(string[] args)
    {
        Client client = new Integration.Client("127.0.0.1", 24001, 360000);
        client.Connect();
    }
}

Upvotes: 0

Views: 421

Answers (2)

jack_tux
jack_tux

Reputation: 429

If you are only after an IP4 local address try this

using System.Linq;

IPAddress ipAddress = Dns.GetHostEntry(m_host).AddressList.FirstOrDefault(x => x.IsIPv6LinkLocal == false);

I just tried your example and had the same error, with the above it seemed to work.

Upvotes: 0

C.Evenhuis
C.Evenhuis

Reputation: 26446

By default, TcpClient assumes you're using an IPv4 address. There's a constructor overload that lets you specify which address family to use though, but this would mean constructing it after you've done the dns lookup:

m_client = new TcpClient(ipAddress.AddressFamily);
IAsyncResult result = m_client.BeginConnect(ipAddress, (int)m_port, new AsyncCallback(OnConnect), m_client);

Alternatively you could find the IPv4 address in the hostInfo.AddressList and connect to that - that's what the BeginConnect(string host, ...) overload does for you under the hood (unless you have specified IPv6 in the constructor).

I have no idea why they didn't just take the AddressFamily from the IPAddress you're passing, perhaps because the underlying Socket is created in the TcpClient constructor. I was also suprised to see the following in the reference source page of TcpClient:

//
// IPv6: Maintain address family for the client
//
AddressFamily m_Family = AddressFamily.InterNetwork;

I don't understand that comment, but apparently someone did have IPv6 in mind when choosing the default.

Upvotes: 1

Related Questions