5

我被要求为朋友查看一些代码。(由于 MFC 和很多糟糕的代码,我犹豫了一下,但他赢了……)

这是一个基于对话框的应用程序,它使用CAsyncSocket.

问题体现在一些不间断的调试中断和其他类似的事情上——MFC 宏也存在问题ENSURE()——检查套接字是否为空。所有的问题都发生在 MFC 的深处。

如果在 Vista/XP 中使用主题,一些谷歌搜索显示可能存在资源泄漏,但我认为这不是问题所在。

根据我几个小时的调试,代码非常糟糕,但基本上它正在执行以下操作:

(建立连接时没有问题 - 只有没有连接时才会出现这种情况)

  • 调用 Connect(server, socket) (在派生CAsyncSocket对象上)
  • OnConnect()我们被通知连接不起作用/未连接。
  • 在主对话框/应用程序的窗口计时器内有一个计时器。当定时器事件/处理程序被调用时,我们检查是否连接。
  • 如果我们检测到我们没有连接(OnConnect()不好)然后我们调用CAsyncSocket::Close(),然后调用CAsyncSocket::Create()(没有参数)然后调用CAsyncSocket::Connect(server, port)

请注意,初始调用Connect()之前没有调用Create().

我的第一个真正的问题:

  • 两者有什么区别,为什么Create()需要?(如果我删除它,那么它不再崩溃,但是当我重新建立连接时我也没有连接)

一般问题:

  • 上面的代码设计到底有什么问题?
  • 一般来说,这应该如何工作?

编辑:

我修复了代码,以便所有路径都通过调用Create()then Connect()

我的断言仍然有问题CAsyncSocket::DoCallBack()- 下面代码的最后一行是断言:

void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
    if (wParam == 0 && lParam == 0)
        return;

    // Has the socket be closed - lookup in dead handle list
    CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);

    // If yes ignore message
    if (pSocket != NULL)
        return;

    pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, FALSE);
    if (pSocket == NULL)
    {
        // Must be in the middle of an Accept call
        pSocket = CAsyncSocket::LookupHandle(INVALID_SOCKET, FALSE);
        ENSURE(pSocket != NULL);

如果我逐步完成,那么我会收到消息框:“遇到不正确的论点”

我认为(但不确定)MFC 在我关闭它之后试图回调套接字。它在一个回调方法(DoCallback())中,但我已经调用Close()了套接字。

所以它看起来确实像一个 MFC 问题,除非我应该先取消订阅。

4

2 回答 2

6

你的选择真的。如果您认为使用另一个套接字实现会更幸运,那就去做吧。

但是,微软有很多开发人员(我相信其中一些甚至可能是优秀的)。您可能,只是可能,想考虑故障并非全部归咎于他们的可能性。

在我看来,你可以从他们的 API 和产品中获得的帮助也很好。

也许如果您花时间了解 MFC 模型,您会得到那个“AHA”时刻并更好地理解它。我不是 Winsock 的粉丝——我更习惯于 UNIX 世界,其中同步是要走的路,如果你想要异步类型的行为,你只需运行单独的进程/线程。

我怀疑,CAsyncSocket 仍然受到 MFC 是单线程模型(就 GUI 而言)这一事实的阻碍,尽管 Windows 已经拥有真正的抢先线程很长一段时间了。[我可能对那个断断续续的评论有误,我已经有一段时间没有直接使用Win32了]。


更新:

根据您说明您在做什么的更新,我相当肯定您在创建之前不允许连接。引用http://msdn.microsoft.com/en-us/library/3d46645f(VS.80).aspx

要使用 CAsyncSocket 对象,请调用其构造函数,然后调用 Create 函数以创建底层套接字句柄 ... 并为客户端套接字调用 Connect 成员函数。

至于为什么,我认为这是一个额外的复杂性,因为 Windows 需要在事件泵环境中执行异步套接字,因为它们不能阻塞主 GUI 线程。

在 UNIXy 环境中,要么没有事件线程(正常进程),要么网络操作只是手动转移到另一个线程(在 GUI 应用程序中)。

这很可能是很久以前在 WinSock 中做出的设计决定(可能是在 Windows 3.11 中,这是一个执行异步操作的限制性更强的环境)并通过 [虽然这是我的猜测,UNIX 套接字 API 从来没有这个一种异步行为,其中消息被泵送,它总是倾向于使用select()或线程/进程]。


进一步更新:

如果您关闭和/或删除了套接字对象,而其上有一个挂起的操作,则通常会发生该断言(不是例外)。在您的情况下,我建议您关闭它时它仍在尝试进行连接。

然后当连接成功或失败时,回调被调用,它在表中找不到你的套接字。

这不是 MFC 问题,而是您朋友的代码违反了合同。如果您执行连接(或任何异步操作),则必须等待成功或失败才能关闭套接字(或进一步处理它) - 在这种情况下,这意味着等待调用 OnConnect() 函数。

从内存中,您create()在创建异步套接字时调用 ,然后响应到达消息队列的消息(即调用您的OnXXX()函数),其他一切都会发生。像所有 Win32 GUI 的东西一样,消息应该驱动程序(代码运行以响应消息)。这段代码看起来越来越像程序驱动一切的经典编码——这种方式很疯狂,因为你会让你的程序和异步套接字“线程”争夺控制权。

我已经有一段时间没有看它了,但是您应该能够掌握 CHATSRVR 示例程序,它将向您展示如何操作。

于 2009-04-29T03:32:53.753 回答
4

这个问题很可能是代码写得不好,而且很有可能它与 MFC 几乎没有关系或根本没有关系。但是很难从您的描述中确定,归结为“我有一个 MFC 应用程序,它会抛出各种调试断言。我该怎么办?”。

可能需要重写,但如果不了解应用程序的更多信息,没有人可以真正说出来。我认为您(或您的朋友)需要下定决心。

于 2009-04-29T03:25:32.833 回答