【问题标题】:can the infamous `ERROR_NETNAME_DELETED' error be considered an error at all? [duplicate]臭名昭著的“ERROR_NETNAME_DELETED”错误可以被认为是一个错误吗? [复制]
【发布时间】:2013-01-08 02:32:12
【问题描述】:

我正在使用完成端口在 Windows NT 中编写一个 tcp 服务器来利用异步 I/O。 我有一个 TcpSocket 类、一个 TcpServer 类和一些(虚拟函数)回调来在 I/O 操作完成时调用,例如onRead() 用于读取完成的时间。我还有 onOpen() 用于何时建立连接, onEof() 用于何时关闭连接,依此类推。 我总是有一个等待读取的套接字,所以如果套接字有效地获取数据(读取将在大小 > 0 时完成),它会调用 onRead(),而不是客户端从客户端关闭套接字 em>(读取将以size == 0完成)它调用onEof(),并且服务器知道客户端何时使用closesocket(server_socket)关闭套接字;从它的侧面。

一切正常,但我注意到一件事:

当我调用 closesocket(client_socket);在连接的服务器端端点上,而不是客户端,(设置 linger {true, 0} 与否),挂起的读取将作为错误完成, 也就是说,读取大小不仅是 == 0,而且 GetLastError() 也会返回错误:64,或 'ERROR_NETNAME_DELETED'。我在网上搜索了很多关于此的内容,但没有发现任何有趣的内容。

然后我问自己:但这是一个真正的错误吗?我的意思是,这真的可以被认为是错误吗?

问题是在服务器端,当我 closesocket(client_socket); 时会调用 onError() 回调;而不是 onEof()。所以我是这么想的:

如果我在收到此“ERROR_NETNAME_DELETED”“错误”时调用 onEof() 而不是 onError() 会怎样? 这会引入一些错误或未定义的行为吗? 让我问这个问题的另一个重要点是:

当我收到带有“ERROR_NETNAME_DELETED”的读取完成时,我检查了 OVERLAPPED 结构,特别是包含 NTSTATUS 错误代码的重叠->内部参数 的底层驱动程序。如果我们看到 NTSTATUS 错误代码列表 [http://www.tenox.tc/links/ntstatus.html] 我们可以清楚地看到'ERROR_NETNAME_DELETED'是由NTSTATUS 0xC000013B产生的,这是一个错误,但它被称为'STATUS_LOCAL_DISCONNECT'。好吧,它看起来不像是错误的名称。它看起来更像是 `ERROR_IO_PENDING',这是一个错误,但也是正确行为的状态。

那么如何检查 OVERLAPPED 结构的内部参数,当这是 == 到 'STATUS_LOCAL_DISCONNECT' 时,执行对 onEof() 回调的调用?会搞砸吗?

另外,我必须从服务器端说,如果我在调用之前调用 DisconnectEx() 关闭套接字(client_socket);我不会收到那个错误。但是我不想调用 DisconnectEx() 怎么办?例如。当服务器关闭并且不想等待所有 DisconnectEx() 完成,而只想关闭所有客户端的连接时。

【问题讨论】:

  • @Hans,我认为他很好地描述了他是如何遇到这个错误的。

标签: c++ winapi tcp network-programming iocp


【解决方案1】:

你调用了错误的方法。你应该打电话给WSAGetLastError()。调用 Winsock API 后 GetLastError() 的结果是没有意义的。

【讨论】:

  • 实际上我正在调用 WSAGetLastError()。同样,错误值是相同的。我提到 GetLastError() 是因为我也将 I/O 完成端口用于非网络 I/O,但我认为该错误仅对网络 I/O 有意义。
  • @MarcoPagliaricci 所以实际上你的问题应该这么说。
【解决方案2】:

如何处理错误情况取决于您,但该问题表明您的代码中存在潜在问题(从逻辑错误到未定义的行为)。

最重要的一点是你不应该在closesocket之后触摸SOCKET句柄。你在 EOF 上做什么?当我们检测到 EOF 时,我们这边的 closesocket 是合乎逻辑的,但这是你不能在 ERROR_NETNAME_DELETED 处理程序中执行的操作,因为 closesocket 已经发生并且句柄无效。

如果等待读取完成(有可用的真实数据)就在 closesocket,并且您的应用程序检测到它之后 closesocket,想象一下会发生什么也是有益的.您处理传入的数据并且...您是否使用相同的套接字句柄向客户端发送答案?您是否在该句柄上安排下一次阅读?都是错的,不会有ERROR_NETNAME_DELETED 告诉你。

如果在那个非常不幸的时刻,就在closesocket 之前,挂起的读取以EOF 完成,会发生什么?如果您的常规 OnEof 回调被触发,而该回调执行 closesocket,那将再次出错。

如果closesocket 在一个线程中完成,而另一个线程等待 I/O 完成,您描述的问题可能暗示更严重的问题。你确定另一个线程没有调用WSARecv/ReadFile,而第一个线程正在调用closesocket?这是未定义的行为,尽管 winsock 让它看起来好像大部分时间都在工作。

总而言之,如果处理完成(或失败)读取的代码不知道套接字句柄因为已关闭而无用,则它不可能是正确的。在closesocket 之后,等待挂起的I/O 完成很有用,因为如果不这样做,您将无法重用OVERLAPPED 结构;但是处理 这种 类型的完成是没有意义的,就好像它发生在正常操作期间一样,套接字仍处于打开状态(错误/状态代码无关紧要)。

【讨论】:

  • 您的观点非常好。好吧,基本上我有 onEof() 来运行一些东西来清理(例如,dealloc 内存等),但我应该有效地为此有 2 个回调:仅在对方关闭连接时调用的 onEof() 和 onClose () - 以这种方式收到 onEof() 时,另一部分可以按照您的建议调用 closesocket() 。所以,如果我明白了,如果挂起的读取在 closesocket() 之前完成并且应用程序在之后检测到它们,那么就会有 ERROR_NETNAME_DELETED 来表示这种情况,这是它的目的吗?
  • 不,“之前完成”/“之后检测到”场景没有 ERROR_NETNAME_DELETED,这是一个例子,在 未检测到的情况下,事情可能会逻辑上出错 i> 方式。这也说明了为什么在 ERROR_NETNAME_DELETED 回调中进行清理可能是错误的:由于成功读取的这种“不幸”时机,没有什么可处理的(并且 没有什么可处理的,因为您可以't 在关闭的套接字上重新安排 WSARecv)。
【解决方案3】:

如何处理错误情况完全取决于您。在您的情况下,这种错误情况完全是意料之中的,您将其视为预期情况是完全安全的。

这种性质的另一个例子是当您调用 API 函数但不知道要提供多大的缓冲区时。因此,您提供了一个希望足够大的缓冲区。但如果 API 调用失败,则检查最后一个错误是否为 ERROR_INSUFFICIENT_BUFFER。这是预期的错误情况。然后,您可以使用更大的缓冲区重试。

【讨论】:

  • 我同意你的看法。我能想象的唯一缺点是,如果在 Windows 中为其他事物生成错误 ERROR_NETNAME_DELETED,然后当出现真正的错误条件时,将调用 onEof() 回调而不是 onError()。所以也许我可以检查 OVERLAPPED 结构中的 NTSTATUS。
  • 您不应该从OVERLAPPED 结构中读取NTSTATUS 值。它是内部的,可能会发生变化。文档对此很清楚。
  • 是的,没错,Internal成员不是用来使用的,所以也许我会依赖GetLastError()
猜你喜欢
  • 2011-04-14
  • 1970-01-01
  • 2015-03-21
  • 1970-01-01
  • 2012-09-07
  • 1970-01-01
  • 2017-10-03
相关资源
最近更新 更多