不能在Socket.BeginConnect()的回调函数中使用断点

本文关键字:函数 断点 回调 Socket BeginConnect 不能 | 更新日期: 2023-09-27 17:49:36

    public bool Connect (string address, int remotePort)
    {
        if (_socket != null && _socket.Connected)
            return true;

        IPHostEntry hostEntry = Dns.GetHostEntry (address);
        foreach (IPAddress ip in hostEntry.AddressList) {
            try {
                IPEndPoint ipe = new IPEndPoint (ip, remotePort);
                _socket = new Socket (ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                _socket.BeginConnect (ipe, new System.AsyncCallback (ConnectionCallback), _socket);
                break;
            } catch (System.Exception e) {
                PushPacket ((ushort)MsgIds.Id.CONNECTION_ATTEMPT_FAILED, e.Message);
                return false;
            }
        }
        return true;
    }
    void ConnectionCallback (System.IAsyncResult ar)
    {
        NetBitStream stream = new NetBitStream ();
        stream._socket = (Socket)ar.AsyncState;
        try {
            _socket.EndConnect (ar);
            _socket.SendTimeout = _sendTimeout;
            _socket.ReceiveTimeout = _revTimeout;
            PushPacket ((ushort)MsgIds.Id.CONNECTION_REQUEST_ACCEPTED, "");
            _socket.BeginReceive (stream.BYTES, 0, NetBitStream.HEADER_LENGTH, SocketFlags.None, new System.AsyncCallback (ReceiveHeader), stream);

        } catch (System.Exception e) {
            if (e.GetType () == typeof(SocketException)) {
                if (((SocketException)e).SocketErrorCode == SocketError.ConnectionRefused) {
                    PushPacket ((ushort)MsgIds.Id.CONNECTION_ATTEMPT_FAILED, e.Message);
                } else
                    PushPacket ((ushort)MsgIds.Id.CONNECTION_LOST, e.Message);
            }
            Disconnect (0);
        }
    }

这里有两个函数。当我调用

client.Connect ("127.0.0.1", 10001);

它会跳过

后面的break;
  _socket.BeginConnect (ipe, new System.AsyncCallback (ConnectionCallback), _socket);

转到return true;。我在ConnectionCallback设置了一个断点,但它没有进入这个函数。

10001端口没有服务器监听。

所以我认为它至少应该抛出一个异常(连接失败),然后进入catch

还是我在两个函数中犯了错误?

这是一个最小的,完整的,可验证的例子

    using System;
using System.Net.Sockets;
using System.Net;
namespace TestSocket
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            NetTCPClient tcp_client = new NetTCPClient ();
            tcp_client.Connect ("127.0.0.1", 10001);
        }
    }
    class NetTCPClient
    {
        Socket _socket = null;
        public bool Connect (string address, int remote_port)
        {
            if (_socket != null && _socket.Connected)
                return true;
            IPHostEntry host_entry = Dns.GetHostEntry (address);
            foreach (IPAddress ip in host_entry.AddressList) {
                try {
                    IPEndPoint ipe = new IPEndPoint (ip, remote_port);
                    _socket = new Socket (ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    IAsyncResult ia = _socket.BeginConnect (ipe, new System.AsyncCallback (ConnectionCallback), _socket);
                    break;
                } catch (Exception e) {
                    Console.WriteLine ("Connet() catch an exception!");
                    return false;
                }
            }
            return true;
        }
        void ConnectionCallback (System.IAsyncResult ar)
        {
            Console.WriteLine ("ConnectionCallback() ");
        }
    }
}

不能在Socket.BeginConnect()的回调函数中使用断点

调试器不是这样工作的。除了极少数例外,它不会在没有你的明确指令的情况下切换线程。

当您逐步执行您编写的Connect()方法时,您正在调试程序中的特定线程。ConnectionCallback()方法,如果它被调用(注意它是而不是通常将在调用BeginConnect()期间被同步调用),将在不同的线程中被调用。如果你想调试它,你需要在ConnectionCallback()方法本身设置一个断点。

如果在该方法中设置了断点,则可以确保调试器将在那里暂停程序的执行,而不管哪个线程正在执行该方法。

编辑:

感谢您的完整代码示例。假设实际上就是您正在测试并遇到问题的代码示例,那么您的问题是(正如已经猜到的)以下两种情况之一:

  1. 在逐步完成对Connect()方法的调用之后,您不恢复程序的执行。也就是说,您没有点击"继续"按钮或使用"调试"菜单中的"继续"菜单项。Or…
  2. 你继续执行你的程序,然后在连接尝试被解决之前迅速退出。
在上面的情况#1中,您永远不会看到断点,因为您的程序没有执行。只有当程序的执行确实到达断点时,才会触发断点。但是如果你的程序根本没有发生,你的程序就无法执行到那里。 在上面的情况#2中,您永远不会看到断点,因为您的程序没有执行。在本例中,这是因为程序完全退出了。


如果您想看到ConnectionCallback()方法的断点被触发,您需要让程序运行,并且运行足够长的时间。

作为一个快速的概念验证,我在该方法上设置了一个断点,并将这条语句添加到Main()方法的末尾:
Thread.Sleep(TimeSpan.FromMinutes(5));

然后我使用调试器逐步通过Main()方法。它当然会暂停,让程序在我刚刚添加的语句处运行,但随后又很快在所需的断点处再次中断程序。(我不需要等待近5分钟& help;我只是把它作为一个非常大的时间值,我确信这是足够的)。


为了它的价值,我还尝试了一个测试,我跳过了原始的Main(),即没有调用Thread.Sleep(),但在继续之前跳过对Connect()的调用后等待大约5-10秒。在这种情况下,至少在我的计算机上,我确实看到了断点被触发。这个特定的测试在某种程度上依赖于机器配置,因此它不如将调用添加到Thread.Sleep()可靠。

我想你可能误解了BeginConnect的作用。这不会建立连接——它只是开始异步地建立连接。所以,是的,我一点也不惊讶"step over"会立即跳到下一个语句——这是预期的效果。

然而,我期望ConnectionCallback中的断点将被击中-这是您应该关注的问题。这也是您应该放置异常处理的地方,因为在那里可以发现建立连接的任何问题。

或者,如果你正在使用c# 5或更高版本,你应该考虑使用async/await,这将允许你摆脱所有的回调。然后,在调试时,您将获得更加熟悉的体验—如果您跳过那行,那么在您到达下一行时,它确实将被连接(或者将出现故障)。只是要注意,当它"等待"响应时,可能会发生其他事情(即使在同一个线程上)。

不幸的是,我在Socket中看不到任何实现相关模式的东西。您可以使用TaskFactory.FromAsync将"旧"样式调整为"新"样式,但这可能相当痛苦。

另一种方法是尝试移动到更高级的结构,如TcpClient,而不是较低级别的Socket类。