在BeginConnect之后调用EndConnect

时间:2017-06-15 13:43:55

标签: c# sockets

根据this MSDN article,应在原始socket.EndConnect来电中提供的AsyncCallback代理中调用socket.BeginConnect方法。

不清楚(并且MSDN文章在这里是静默的)是在超时后是否应该调用EndConnect(并且套接字未连接)。 socket.EndConnect在这种情况下抛出异常。

超时后要遵循的正确程序是什么?如果未调用EndConnect(无论是在成功连接之后还是在没有连接的情况下超时),会有什么后果?我的代码似乎无需调用EndConnect即可正常工作。

以下是一些示例代码,涵盖了问题中的主要观点:

// Member variables
private static ManualResetEvent m_event;
private static Socket m_socket;

// Constructor of class
public static CMyTestConnection()
{
    // Create an event that can be used to wake this thread when the connection completes
    m_event = new ManualResetEvent(false);
}

private static void TestConnection(object sender, EventArgs e)
{
    // Create connection endpoint
    IPAddress ip = IPAddress.Parse("200.1.2.3");    // Deliberately incorrect
    IPEndPoint ipep = new IPEndPoint(ip, 12345);    // Also deliberately incorrect
    EndPoint ep = (EndPoint)ipep;

    // Attempt connection
    m_event.Reset();
    m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
    m_socket.BeginConnect(ep, ConnectCompletedCallback, m_socket);
}

private static void ConnectCompletedCallback(IAsyncResult ar)
{
    // The asynchronous connection call has completed. Either we have connected (success) or
    // timed out without being able to connect (failure).
    m_event.Set();
    Socket s = (Socket)ar.AsyncState;
    if (s.Connected)
    {
        // Success...should EndConnect only be called here?
        s.EndConnect(ar);
    }
    else
    {
        // Or should EndConnect also be called here (in a try/catch block)?
        s.Close();  
    }
}

1 个答案:

答案 0 :(得分:0)

您邀请我this chat room。我假设这是你所指的问题,但我很难确切地知道。您在聊天室中的消息没有真实的URL。我在你的个人资料中查看了你的问题链接,我唯一知道的是这一个,目前尚未关闭。所以没有必要投票重新开放。

也就是说,答案仍然与评论中已经提供的答案相同:当您致电EndXXX时,总是调用BeginXXX方法(少数已知)例外不适用于此处)。即使在最近的编辑之后,您的问题中也没有任何内容可以表明您需要更多内容。

您没有显示超时是如何实现的,因此甚至没有足够的信息来理解您发布的代码。但是如果你正在关闭套接字,从而导致在EndConnect()将引发异常的地方调用你的回调,你应该调用EndConnect()。如果不这样做,可能会使非托管资源悬而未决,最终会耗尽,或者至少会导致性能问题。

.NET的源代码随时可用,因此您可以自己轻松地检查the implementation。在Socket.EndConnect()的情况下,我们可以看到,对于当前的实现,如果已经处理了套接字,那么所有发生的事情都会抛出异常。因此,理论上,您可以忽略已经关闭的套接字。即在特定的“套接字已经关闭”的情况下,这是对资源悬空的普遍关注的例外。但是,如果通过关闭套接字来实现超时,

这里有一些问题,与竞争条件有关:

  1. 根据超时的实现方式(您没有共享该部分,因此问题仍然不完整),您可能拥有的代码已经开始调用Socket.Close(),但尚未设置被处置的旗帜。您将处理即将断开连接的已连接套接字,并且您需要try / catch来处理该方案。
  2. 您的回调假设(似乎......再次,您的问题中没有足够的上下文)Connected属性是检测已超时的可靠方法,但Connected属性理论上可以连接后重置为false,但在回调执行之前(例如套接字上的其他类型的错误)。
  3. 至于在成功连接上调用EndConnect()的问题,这一点要清楚得多:必须这样做。如果你的代码看起来没有用,那就是你很幸运。我们可以在实现中看到EndConnect()方法在成功连接后调用时配置套接字状态有用,所以如果调用方法失败,套接字将处于某种不确定的,未完全配置的状态。

    当然,如果您的超时是以其他方式实现的,那么在调用回调之前套接字已关闭,那么您处于与连接完成时相同的情况,并且您必须调用EndConnect()以确保发生适当的清理和套接字配置。即这与“成功连接”场景相同。

    最重要的是,如果发生基于关闭/置备的超时,会对<{1}}进行的好处。唯一的假设好处可能是你可以避免EndConnect() / try,但如果没有这个,你就无法逃脱,因为存在竞争条件。如果没有这样的超时,不仅没有调用该方法的好处,那么无法调用它会有真正的危害。


    在相关的说明中,您的问题中没有足够的上下文来对代码的其余部分进行任何实际评估(因为您没有说明如何实现超时,也没有说明如何处理其余的网络I / O )。但我要说的是,在大多数情况下,“重用地址”选项是不必要的,并且应该使用。大多数人最终使用它是因为他们遇到了一种情况,他们在以某种方式停止了前一个套接字后无法启动新的监听套接字,但这个问题只出现在第一个监听套接字和/或相关的连接套接字没有已正确关闭或关闭。在这种情况下,正确的方法是正确处理套接字关闭/关闭,而不是通过设置“重用地址”来添加问题。