6

我正在使用完成端口在 Windows NT 中编写一个 tcp 服务器来利用异步 I/O。我有一个 TcpSocket 类、一个 TcpServer 类和一些(虚拟函数)回调来在 I/O 操作完成时调用,例如在读取完成时调用 onRead()。我还有 onOpen() 用于建立连接的时间和 onEof() 用于关闭连接的时间,依此类推。我总是有一个等待读取的套接字,所以如果套接字有效地获取数据(读取将在大小 > 0 时完成)它调用 onRead(),而不是如果客户端从客户端关闭套接字(读取将是以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() 回调的调用?会把事情搞砸吗?

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

4

3 回答 3

3

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

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

于 2013-01-24T11:14:25.903 回答
2

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

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

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

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

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

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

于 2013-01-24T11:31:52.597 回答
0

你调用了错误的方法。您应该调用WSAGetLastError()。在 Winsock API 调用之后 GetLastError() 的结果是没有意义的。

于 2013-01-24T22:29:28.250 回答