我有一个使用 IOCP 的服务器应用程序。我想知道关闭 SOCKET
的正确方法是什么? .
如果我简单地调用closesocket()
(对于 SOCKET
,其句柄例如 12345
),而这个 SOCKET
有待处理的 IO 操作(例如:待处理的 WSARecv()
请求),则可能会发生以下情况:
我调用
closesocket()
这将摧毁SOCKET
.我接受另一个
SOCKET
与12345
具有相同的句柄.我将待处理的
WSARecv()
出队SOCKET
的完成数据包 handle 为12345
。现在我假设这个完成包是针对当前的SOCKET
handle 为12345
,但实际上它是针对SOCKET
之前已关闭(这是此方法的主要问题)。
所以这显然是一个糟糕的方法。
第二种似乎正确的方法如下:
我关联了
struct
每个SOCKET
的实例。struct
具有以下成员: anint
叫number_of_pending_IO_operations
,以及boolean
叫Is_SOCKET_being_closed
.当我对
SOCKET
发出 IO 操作时(例如:WSASend()
请求),我递增number_of_pending_IO_operations
通过1
,当我将 SOCKET 的完成数据包出队时,我会递减number_of_pending_IO_operations
通过1
.现在,当我想关闭
SOCKET
时,我不简单地打电话closesocket()
,而是我打电话CancelIOEx()
取消SOCKET
的所有待处理 IO 操作,我还设置了Is_SOCKET_being_closed
至true
.当我要发出另一个 IO 操作(例如:
WSASend()
请求)时,我会检查Is_SOCKET_being_closed
的值,如果是true
,我不会发出IO操作。现在我只需等待所有完成数据包出队,当
number_of_pending_IO_operations
时达到0
和Is_SOCKET_being_closed
设置为true
,我调用closesocket()
.
当然我会有竞争条件,所以我会使用关键部分。
第二种方法是关闭 SOCKET
的正确方法吗? ,或者有更好的方法吗?
最佳答案
我在客户端-服务器应用程序中遇到了类似的问题。我想我没有比赛,没有破败保护,也没有关键部分。但也有一些权衡。
- 一次只能执行一个 WSARecv()。多个缓冲区——也许,但只有一个 WSARecv()。 WSARecv 完成(可能是内联),数据包从完成端口弹出,我快速检查我得到的内容并发出另一个 WSARecv()
- 我有一个 Is_SOCKET_being_close 标志(实际上是一个只能上升的计数器)。计数器,因为我可能同时决定在两个不同的地方杀死套接字。
- 设置 Is_SOCKET_being_close 后,我调用 shutdown(),然后调用 CancelIoEx(),这将破坏之前发出的 WSARecv()。
- CancelIoEx 和 WSARecv() 的完成之间存在竞争——我在发出 WSARecv() 之前通过仔细检查 Is_SOCKET_being_close 来消除它。即使发生竞争,WSARecv() 也注定会失败,因为 shutdown()。
- 该结构是重新计数的,其中一个 ref 由接收状态机保存,并且为 ptr 的每个合法所有者(如潜在的发送者)保存一个 ref。如果您有合法的引用,您可以制作一份副本,然后将其交给其他人(它与概要引用不同,因为它们的 AddRef() 可能会失败)。我只会在该引用耗尽到零时才会 closesocket() ,因为我无法保证没有线程已通过所有检查并即将发出 WSARecv/WSASend。
关于c - 使用IOCP时如何关闭SOCKET?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42488740/