【问题标题】:Implement an asynchronous tcp connection with a timeout实现一个带超时的异步 tcp 连接
【发布时间】:2017-11-14 05:10:53
【问题描述】:

我遇到了一个异常:

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

 Additional information: This protocol version is not supported."

当我运行以下代码时。我正在尝试实现一个带有超时的异步 tcp 连接。

我已经看过并阅读了几个堆栈溢出示例,一些使用 TcpClient,一些使用 Socket。我认为前者包含了后者并且更新。我正在尝试使用 TcpClient.BeginConnect

文档没有将 NotSupported 列为此方法可以抛出的异常类型之一。如何找出问题所在?

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();
    }
}

【问题讨论】:

  • 您在什么操作系统上运行代码? NotSupported 通常与操作系统或操作系统配置有关,尽管我不确定这里的情况。
  • Server 2012 R2 Standard with .Net 4.5
  • 以某种方式打印你得到的整个异常,而不仅仅是它的 Message 属性。例如,将 Main 中的代码包装在 try/catch 中:static void Main(string[] args) { try{ Client client = new Integration.Client("127.0.0.1", 24001, 360000); client.Connect(); } } catch (Exception exc){System.Diagnostics.Debug.WriteLine(exc.ToString());}
  • 啊,你可能想多看看你的hostinfo.AddressList。可能您最终得到的是 IPv6 地址(或当前机器上其他不支持的协议)。换句话说,ipAddress 可能不正确。
  • 是的,它给了我一个 ip v6 地址。但为什么 2017 年不支持呢?它在与其获取的 ipv6 地址匹配的网络适配器上启用。

标签: c# sockets asynchronous


【解决方案1】:

默认情况下,TcpClient 假定您使用的是 IPv4 地址。有一个constructor overload 可以让您指定要使用的地址系列,但这意味着在您完成 dns 查找后构建它:

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

或者,您可以在 hostInfo.AddressList 中找到 IPv4 地址并连接到该地址 - 这就是 BeginConnect(string host, ...) 重载为您所做的事情(除非您在构造函数中指定了 IPv6)。

我不知道他们为什么不直接从您传递的IPAddress 中获取AddressFamily,可能是因为底层Socket 是在TcpClient 构造函数中创建的。在TcpClientreference source page中看到以下内容我也很惊讶:

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

我不明白那个评论,但显然有人在选择默认值时考虑了 IPv6。

【讨论】:

  • 有趣,因为是的,如果您将主机名传递给 BeginConnect,它会选择 IPv6(无论如何根据文档)
  • 在具有该重载的机器上,它会选择我在构造函数中指定的地址系列,或者在使用默认构造函数时选择 IPv4。 myTcpClient.Client.AddressFamily 甚至在连接之前也设置为该值。
【解决方案2】:

如果你只在 IP4 本地地址之后尝试这个

using System.Linq;

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

我刚刚尝试了您的示例并且遇到了同样的错误,上面似乎可以正常工作。

【讨论】:

    最近更新 更多