我被要求为 friend 查看一些代码。 (由于 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()
,然后调用 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 问题,除非我应该先取消订阅。
最佳答案
真的是你的选择。如果您认为使用另一个套接字实现会更幸运,那就去做吧。
但是,Microsoft 有很多开发人员(我相信其中一些甚至可能是优秀的)。您可能,只是可能,想考虑这样一种可能性,即错误并不全都出在他们的头上。
在我看来,您可以获得的有关他们的 API 和产品的大量帮助也不错。
也许如果您花时间了解 MFC 模型,您会得到那个“顿悟”时刻并更好地理解它。我不是 Winsock 的粉丝 - 我更习惯于 UNIX 世界,其中同步是要走的路,如果你想要异步类型的行为,你只需运行单独的进程/线程。
我怀疑 CAsyncSocket 仍然受到 MFC 是单线程模型(就 GUI 而言)这一事实的阻碍,即使 Windows 已经拥有真正的抢占式线程已经有一段时间了。 [我对那个无能为力的评论可能是错误的,我已经有一段时间没有直接使用 Win32 了]。
更新:
根据您声明自己正在做什么的更新,我相当确定您在创建之前不允许连接。引用 http://msdn.microsoft.com/en-us/library/3d46645f(VS.80).aspx ,
To use a CAsyncSocket object, call its constructor, then call the Create function to create the underlying socket handle ... and for a client socket call the Connect member function.
至于为什么,我认为这增加了额外的复杂性,因为 Windows 需要在事件泵环境中执行异步套接字,因为它们不能阻塞主 GUI 线程。
在 UNIXy 环境中,要么没有事件线程(正常进程),要么网络操作只是手动转移到另一个线程(在 GUI 应用程序中)。
这很可能是很久以前在 WinSock 中做出的设计决定(可能是在 Windows 3.11 中,这是一个对执行异步操作有更多限制的环境)并且一直贯彻 [虽然这是我的推测,UNIX 套接字 API 有从来没有这种消息被泵送的异步行为,它总是倾向于使用 select()
或线程/进程。
进一步更新:
如果您关闭和/或删除了套接字对象,而该套接字对象上有待处理的操作,通常会发生该断言(不是异常)。对于您的情况,我建议它在您关闭它时仍在尝试进行连接。
然后当连接成功或失败时,回调被调用并且它无法在表中找到您的套接字。
这不是 MFC 的问题,是你 friend 的代码违反了约定。如果您执行连接(或任何异步操作),则必须在关闭套接字(或进一步处理它)之前等待成功或失败 - 在这种情况下,这意味着等待调用你的 OnConnect() 函数。
根据内存,您在创建异步套接字时调用了 create()
,然后所有其他事情都会响应到达消息队列的消息(即调用您的 OnXXX( )
函数)。像所有 Win32 GUI 东西一样,消息应该驱动程序(代码运行以响应消息)。这段代码看起来越来越像经典编码,其中程序驱动一切 - 这种方式很疯狂,因为您将让您的程序和异步套接字“线程”争夺控制权。
我已经有一段时间没看过它了,但您应该可以得到一个 CHATSRVR 示例程序,它会告诉您如何操作。
关于c++ - 我的 MFC 套接字代码中的 CAsyncSocket 断言问题和 "improper argument"错误背后的原因是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/800734/