重用异步套接字:后续连接尝试失败

Kir*_*ril 7 c# sockets asynchronous http httpclient

我正在尝试在异步HTTP客户端中重用套接字,但是第二次我无法连接到主机.我基本上将异步HTTP客户端视为具有以下状态的状态机:

  • 可用:插座可供使用
  • 连接:套接字连接到端点
  • 发送:套接字正在向端点发送数据
  • 接收:套接字正在从端点接收数据
  • 失败:存在套接字故障
  • 清理:清理插座状态

在连接状态下我打电话BeginConnect:

private void BeginConnect()
{
    lock (_sync) // re-entrant lock
    {
        IPAddress[] addersses = Dns.GetHostEntry(_asyncTask.Host).AddressList;

        // Connect to any available address
        IAsyncResult result = _reusableSocket.BeginConnect(addersses, _asyncTask.Port, new AsyncCallback(ConnectCallback), null);
    }
}
Run Code Online (Sandbox Code Playgroud)

Sending一旦建立成功连接,回调方法就会将状态更改为:

private void ConnectCallback(IAsyncResult result)
{
    lock (_sync) // re-entrant lock
    {
        try
        {
            _reusableSocket.EndConnect(result);

            ChangeState(EClientState.Sending);
        }
        catch (SocketException e)
        {
            Console.WriteLine("Can't connect to: " + _asyncTask.Host);
            Console.WriteLine("SocketException: {0} Error Code: {1}", e.Message, e.NativeErrorCode);
            ThreadPool.QueueUserWorkItem(o =>
            {
                // An attempt was made to get the page so perform a callback
                ChangeState(EClientState.Failed);
            });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在清理我Shutdown的套接字和Disconnect重用标志:

private void CleanUp()
{
    lock (_sync) // re-entrant lock
    {
        // Perform cleanup 
        if (_reusableSocket.Connected)
        {
            _reusableSocket.Shutdown(SocketShutdown.Both);
            _reusableSocket.Disconnect(true);
        }
        ChangeState(EClientState.Available);
    }
}
Run Code Online (Sandbox Code Playgroud)

后续调用BeginConnect导致超时和异常:

SocketException:连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机无法响应XX.XXX.XX.XX:80

错误代码:10060

这是状态跟踪:

Initializing...
Change State: Connecting
Change State: Sending
Change State: Receiving
Change State: CleanUp
Callback:     Received data from client 0 // <--- Received the first data 
Change State: Available
Change State: Connecting // <--- Timeout when I try to reuse the socket to connect to a different endpoint
Run Code Online (Sandbox Code Playgroud)

为了能够重用套接字连接到不同的主机,我该怎么办?

注意:我没有尝试重新连接到同一主机,但我认为发生了同样的事情(即无法连接).

更新
我在BeginConnect文档中找到了以下注释:

如果此套接字先前已断开连接,则必须在线程上调用BeginConnect,该线程在操作完成之前不会退出.这是底层提供者的限制.此外,使用的EndPoint必须不同.

我开始怀疑我的问题是否与此有关...我正在连接到另一个EndPoint,但它们是什么意思是我们称之为BeginConnect的线程在操作完成之前不能退出?

更新2.0:
我问了一个相关的问题,我尝试使用"Async family"调用而不是"Begin family"调用,但是我遇到了同样的问题!

Len*_*ate 2

我对这个问题发表了评论:What is Benefit from socket reuse in C#关于套接字重用的使用Disconnect(true)/DisconnectEx(),这可能对您有帮助。

就我个人而言,我认为客户端代码的优化太过分了。

重新更新 1 你的问题;不,如果是这种情况,您会收到 AbortedOperation 异常(请参阅此处:部署时的 VB.NET 3.5 SocketException 但不在开发计算机上),并且如果您在 Vista 或更高版本上运行,则文档是错误的,因为它没有强制执行先前操作系统强制执行的“线程必须存在直至重叠 I/O 完成”规则。

正如我在回答链接问题时已经说过的;使用此功能来建立出站连接没有什么意义。它最初可能被添加到 Winsock API 中,以支持AcceptEx()入站连接的套接字重用,其中,在一个非常繁忙的 Web 服务器上,该服务器用于TransmitFile()向客户端发送文件(这似乎是断开重用的起源)。文档指出它不能很好地发挥作用TIME_WAIT,因此将它用于启动主动关闭的连接(从而将套接字放入TIME_WAIT,请参阅此处)并没有真正意义。

您能解释一下为什么您认为这种微观优化在您的情况下实际上是必要的吗?